Compare commits

...

817 Commits
1.01b ... 2.1

Author SHA1 Message Date
Matt Nadareski
7f4921a47c Bump version to 2.1 2021-07-22 21:02:32 -07:00
Matt Nadareski
07359d2a63 Tweak Linq statement 2021-07-22 20:59:08 -07:00
Matt Nadareski
e5ba670c04 Update to BurnOutSharp 1.7.0 2021-07-22 11:19:58 -07:00
Matt Nadareski
3096c369d3 Update changelog 2021-07-19 22:47:25 -07:00
Matt Nadareski
333368675a Add DMI/PFI/SS to zipfile (fixes #296) 2021-07-19 13:38:16 -07:00
Wilson
d78d744ae2 Fixed log window background turning white after some "time". (#295)
* Fixed launching MPF in Windows 7.

* Potential fix for log window background 'random' color change.

* Updated the color to use hex digits.
2021-07-17 20:35:38 -07:00
Wilson
a8efe056c1 Fixed launching MPF in Windows 7. (#292) 2021-07-12 16:46:30 -07:00
Matt Nadareski
9b2475a854 Add BD to list of supported types for PC (fixes #291) 2021-07-05 22:31:58 -07:00
Matt Nadareski
33e2b27e6d Add new header for mainInfo 2021-07-05 13:26:23 -07:00
Matt Nadareski
91fe9d9bec Support /mr value parameter 2021-07-02 22:57:07 -07:00
Matt Nadareski
b1b9b3134f Update to DIC 20210701 2021-07-01 14:19:25 -07:00
Matt Nadareski
ca5386b529 Update changelog 2021-06-20 11:23:30 -07:00
Matt Nadareski
347beca874 Handle Redump 503s / Timeouts (fixes #289) 2021-06-20 11:21:43 -07:00
Matt Nadareski
27ca4fd1af Check for updates logs as well 2021-06-19 20:47:41 -07:00
Matt Nadareski
6d57a05f8c Encode params for login 2021-06-19 16:12:42 -07:00
Matt Nadareski
4d81449cfa Minor updates 2021-06-19 15:51:18 -07:00
Matt Nadareski
76fcb3ae10 Separate "Include Artifacts" into a separate setting (fixes #287) 2021-06-19 15:41:46 -07:00
Matt Nadareski
b913f5f9ae Update Xbox disc detection (fixes #288) 2021-06-19 15:25:43 -07:00
Matt Nadareski
68fa1eecfb Replace dumping program output processing 2021-06-15 10:24:19 -07:00
Matt Nadareski
dc84f17ac5 Set the matcher for carriage returns 2021-06-15 10:23:14 -07:00
Matt Nadareski
c91137130b Fix newline processing, add changelog 2021-06-15 10:20:13 -07:00
Matt Nadareski
972b07551f Add Logging utility class 2021-06-15 09:55:14 -07:00
Matt Nadareski
61562fe995 Handle carriage returns even without fancy formatting 2021-06-15 09:35:11 -07:00
Matt Nadareski
01b6c3cc98 Minor formatting change in ProcessingQueue 2021-06-14 22:37:47 -07:00
Matt Nadareski
b461dc76b9 Fix incorrect normalization for Aaru 2021-06-14 22:15:36 -07:00
Matt Nadareski
03eeed4113 Update nuget packages 2021-06-03 22:39:50 -07:00
Matt Nadareski
07615cb336 Update nuget packages 2021-06-03 22:19:03 -07:00
Matt Nadareski
9a9a977cc4 Simplify flag validation
Instead of trying to determine if something is a flag, just check if it's one of the supported flags. This should reduce the amount of potential issues that come along with making assumptions about what a parameter could look like.
2021-06-03 22:13:50 -07:00
Matt Nadareski
ab5869331b Add throwability to cuesheet code 2021-06-03 21:58:52 -07:00
Matt Nadareski
1c9ebfd703 TODO additions 2021-06-03 21:20:45 -07:00
Matt Nadareski
a9bc3587a6 Update to DIC 20210601 2021-06-03 21:19:33 -07:00
BadAd84
b1af2b6061 Fix for high cpu usage, issue with loop. (#286) 2021-05-30 12:58:08 -07:00
Matt Nadareski
2fccd53098 Ensure CSSKey file included in log zipping 2021-05-27 12:07:21 -07:00
Matt Nadareski
a7ad3e41d9 Change offset value for HVN discs (fixes #285) 2021-05-27 11:49:24 -07:00
Matt Nadareski
1fc6476f71 Be smarter about checking for zipped logs 2021-05-27 11:46:47 -07:00
Matt Nadareski
53e5a1b1b1 Check for the zipped logs for dealing with overwrites (fixes #277) 2021-05-27 11:20:48 -07:00
Matt Nadareski
119e9dc4cc Always check for all DIC log files, just in case (fixes #281) 2021-05-27 11:00:56 -07:00
Matt Nadareski
9263769906 Fix negative offsets (fixes #282) 2021-05-27 10:41:25 -07:00
Matt Nadareski
ebaf9539b2 Allow users to customize protection scanning 2021-05-06 21:47:57 -07:00
Matt Nadareski
818fb5ab34 Add right click menu with theming 2021-04-28 11:07:54 -07:00
Matt Nadareski
2f514082a9 Replace WindowChrome with dragable titles instead 2021-04-28 10:08:18 -07:00
Matt Nadareski
5c5379e972 Update ScrollViewer style 2021-04-27 11:07:07 -07:00
Matt Nadareski
c39894d9fc Update ProgressBar style 2021-04-27 10:51:32 -07:00
Matt Nadareski
2c6cf0e736 Custom UI elements v2 2021-04-26 23:08:21 -07:00
Matt Nadareski
7e96bbee27 Custom UI elements v1
This enables full-window dark mode to be a reality. The problem is that my design skills are very low
2021-04-26 22:32:05 -07:00
Matt Nadareski
f7019546d1 Add experimental dark mode (fixes #272) 2021-04-26 12:07:21 -07:00
Matt Nadareski
086f54abf0 Topsy-turvy (fixes #274) 2021-04-25 14:08:00 -07:00
Matt Nadareski
143a4149bd Parametrics (#276)
* Begin reducing complexity of parameters classes

* Make the method virtual, not the field

* Consolidate helper methods

* Fix "empty" commands; Fix DD generation

* Handle all sorts of numerical values

* Rip out remaining unneeded enum
2021-04-25 14:03:55 -07:00
Whovian9369
20aed68d58 Fix README formatting in "Notable Testers" Section (#275) 2021-04-25 10:19:15 -07:00
Matt Nadareski
7a2061a36e Bump version for first MPF rebranded stable 2021-04-23 10:30:44 -07:00
Matt Nadareski
191c7b080e Add offset for all HVN discs 2021-04-18 10:42:09 -07:00
Matt Nadareski
aab0813688 Remove HVN-specific flags for now
These flags may cause dumping issues for some, if not a majority, of VideoNow discs
2021-04-16 21:18:48 -07:00
Matt Nadareski
9ad564772c Add content alignment to user input control 2021-04-15 12:59:44 -07:00
Matt Nadareski
20bb34c3c7 Handle Enter/Esc for relevant buttons 2021-04-14 22:26:23 -07:00
Matt Nadareski
b15ddf390b Update to BurnOutSharp 1.6.1 2021-04-14 09:27:42 -07:00
Matt Nadareski
18d1562ad3 Update changelog 2021-04-13 09:31:23 -07:00
Matt Nadareski
d6f2ecbd25 Add version from Git commit (fixes #271)
This also ensures that the new version check still works as well, so the commit hash addition doesn't screw anything up.
2021-04-13 09:30:26 -07:00
Matt Nadareski
3620686afa Change window title 2021-04-13 09:28:26 -07:00
Matt Nadareski
1524224bc3 Fix cursor positioning issue 2021-04-12 22:04:54 -07:00
Matt Nadareski
35200d53d6 Update changelog 2021-04-12 14:59:22 -07:00
Matt Nadareski
4b7d4e05e7 Streamline output path textbox handling
Addresses issue of cursor moving during typing
Addresses issue of extraneous spaces appearing
2021-04-12 13:25:12 -07:00
Matt Nadareski
7e73f69433 Fix Optuons XAML 2021-04-12 08:42:22 -07:00
Matt Nadareski
9e1a972df5 Remove needless UIOptions intermediate class 2021-04-11 22:30:23 -07:00
Matt Nadareski
1edac34c1a Handle dialog return properly
Also fixes an issue where dumpable media with no ring would show a ringcode tab
2021-04-11 09:31:22 -07:00
Matt Nadareski
9adc588c9c Reintroduce options cancel button 2021-04-11 00:09:13 -07:00
Matt Nadareski
71bfdcba8f Proofreading 2021-04-09 13:52:03 -07:00
Matt Nadareski
9854c9970a Add descriptions to new options, change defaults
The default changes are:
- Setting `CompressLogFiles` to `true`
- Setting `ToolsInSeparateWindow` to `true`
- Setting `EnableLogFormatting` to `false`
- Setting `EnableProgressProcessing` to `false`

The last two don't really matter if `ToolsInSeparateWindow` is `true` since no UI-derived logs will ever trigger them, so they're being disabled for now.
2021-04-09 13:31:23 -07:00
Matt Nadareski
0c68164d6d Add option for log formatting 2021-04-09 12:03:30 -07:00
Matt Nadareski
a413357b57 Help users be more patient when starting a dump 2021-04-09 11:47:12 -07:00
Matt Nadareski
5e283c9e80 Add option to enable progress processing
This is one of the larger pain-points for redirecting the output as it involves a regex. Make it optional so people with weaker systems have a better chance.
2021-04-09 11:16:50 -07:00
Matt Nadareski
500eb66e30 Fix the text... got it inverted 2021-04-09 10:59:00 -07:00
Matt Nadareski
411e5245a5 Update tooltip to help a bit more 2021-04-09 10:57:48 -07:00
Matt Nadareski
d270501c2d Update to BurnOutSharp 1.6.0 2021-04-09 10:31:27 -07:00
Matt Nadareski
f7f924593f Add Audio CD detection
On Windows 10, at least, when a purely Audio CD is put in, it gets the volume label of "Audio CD", conveniently. We can use this to at least attempt to match, even if this could be a false positive for some CD-i or things like Jag CD
2021-04-07 10:04:59 -07:00
Matt Nadareski
5126a66eb9 Color code the text 2021-04-06 13:35:47 -07:00
Matt Nadareski
7fddef238d Update changelog 2021-04-06 11:31:27 -07:00
Matt Nadareski
ace673f90d Integrate ring code guide into submission window
Adds a new button to show the ring code guide for easier reference
2021-04-06 11:05:03 -07:00
Matt Nadareski
e063df63df Merge pull request #269 from SabreTools/logjam
Add option to compress log files after dumping
2021-04-02 22:47:26 -07:00
Matt Nadareski
22b15710d0 Take output of compression into account for log 2021-04-02 22:34:46 -07:00
Matt Nadareski
87199c1b0d Update changelog 2021-04-02 22:28:01 -07:00
Matt Nadareski
7e5952bbb8 Add log file lsits for CleanRip and UIC 2021-04-02 22:23:15 -07:00
Matt Nadareski
1a52a3a205 Add Check parameter for compression 2021-04-02 22:20:15 -07:00
Matt Nadareski
742db4c854 Change default archive name 2021-04-02 22:12:32 -07:00
Matt Nadareski
4fd51dbe45 Default to false, for now 2021-04-02 21:57:00 -07:00
Matt Nadareski
096a8a6a06 Add Aaru log file paths, remove TODO in DIC 2021-04-02 21:56:14 -07:00
Matt Nadareski
ad5cd5b8f9 Only encode artifacts if we're outputting JSON 2021-04-02 21:53:41 -07:00
Matt Nadareski
3230d59f6a Fix options window 2021-04-02 21:49:57 -07:00
Matt Nadareski
96fa8d8cef Add DiscImageCreator log paths 2021-04-02 21:36:14 -07:00
Matt Nadareski
01bf3c9efb Add log file compression 2021-04-02 21:21:50 -07:00
Matt Nadareski
da0bf64e94 Add log compression option 2021-04-02 20:55:20 -07:00
Matt Nadareski
d39cf5d13f Make JSON optional (fixes #268) 2021-04-02 18:57:25 -07:00
Matt Nadareski
7c7e78fd4e Errors during dumping should be more verbose
This should *hopefully* be a temporary change in order to determine what is failing after many dumps in a row.
2021-04-02 14:23:02 -07:00
Matt Nadareski
d2c504898a Empty the queue at least 2021-04-01 19:08:39 -07:00
Matt Nadareski
58028b4f18 Reset queue entirely to prevent issues 2021-04-01 15:11:30 -07:00
Matt Nadareski
54e835687b Only rely on cancellation not disposal 2021-04-01 15:10:43 -07:00
Matt Nadareski
10f2318541 Update to DIC 20210401 2021-03-31 22:11:02 -07:00
Matt Nadareski
eb4e95e5db Fix ProcessingQueue disposal (fixes #267) 2021-03-31 21:26:55 -07:00
Matt Nadareski
203418eebc CD-i is a fake audio format 2021-03-31 20:36:06 -07:00
Matt Nadareski
136c6c8fbf Remove two useless TODOs 2021-03-29 13:11:45 -07:00
Matt Nadareski
2c216d6a58 Use BurnOutSharp for PSX scanning 2021-03-29 13:10:49 -07:00
Matt Nadareski
f7ec19cc5d Clean up a couple more TODOs 2021-03-29 13:05:46 -07:00
Matt Nadareski
e8c4a97158 Fix flag value 2021-03-29 12:14:06 -07:00
Matt Nadareski
5e5fc4c812 Factor in size to media type output 2021-03-29 12:12:20 -07:00
Matt Nadareski
5e0d99ff5d Remove unnecessary TODOs 2021-03-29 12:12:05 -07:00
Matt Nadareski
e88e67c927 Fix broken test 2021-03-29 11:56:28 -07:00
Matt Nadareski
d963dbbd5f Move some classes around and cleanup 2021-03-29 11:55:29 -07:00
Matt Nadareski
11ce78fca2 Fix and use ProcessingQueue 2021-03-29 10:50:43 -07:00
Matt Nadareski
fd4022ba84 Add ProcessingQueue (nw) 2021-03-29 10:23:34 -07:00
Matt Nadareski
f91aecad8e Add queueing mechanism to DumpEnvironment 2021-03-29 09:35:39 -07:00
Matt Nadareski
709b7d357f It's a secret... 2021-03-28 22:29:22 -07:00
Matt Nadareski
59980fa160 Clear button should clear progress bar too 2021-03-28 22:25:03 -07:00
Matt Nadareski
180caf83cc Forgot to update param comment and changelog 2021-03-28 22:22:27 -07:00
Matt Nadareski
7047ee832a Simplify null checks 2021-03-28 22:15:33 -07:00
Matt Nadareski
4b5532e1b0 Log levels, struct methods, simpler constructor 2021-03-28 22:10:17 -07:00
Matt Nadareski
137155d3dc Forgot summary comment 2021-03-28 21:34:27 -07:00
Matt Nadareski
793dd8289a Safer queue, use LogLine struct again
Once ConcurrentQueue was used, there were issues with using Run objects in the queue. Even with Dispatcher, there were access issues. So back to the minimal struct.
2021-03-28 21:30:42 -07:00
Matt Nadareski
bcf298e40b Change name of log queue 2021-03-28 20:38:59 -07:00
Matt Nadareski
589aef1e21 Use Run instead of custom wrapper
This simplifies a whole bunch of logic that had to convert from one type to another.
2021-03-28 20:38:15 -07:00
Matt Nadareski
9ab0b7ab3b Clean up var and methods in logger 2021-03-28 20:28:59 -07:00
Matt Nadareski
4c5ea13aac Add log queuing 2021-03-28 15:38:51 -07:00
Matt Nadareski
0cf48bbdaa Cache last line object instead 2021-03-28 13:58:11 -07:00
Matt Nadareski
8fc9cc18a3 Simplify dispatcher calls 2021-03-28 00:21:48 -07:00
Matt Nadareski
b1e156eed6 Smarter about matchers 2021-03-27 22:23:29 -07:00
Matt Nadareski
a8b3a837ef Simplify matchers, fix percentages 2021-03-27 13:21:42 -07:00
Matt Nadareski
3f3a1c9a44 Support new Redump languages and regions 2021-03-27 10:28:45 -07:00
Matt Nadareski
cf4d2f5055 Fix typo in progress 2021-03-25 22:56:17 -07:00
Matt Nadareski
2d19d51bc7 Add matcher for anti-mod string search 2021-03-25 18:48:19 -07:00
Matt Nadareski
4842bdd38b Have the log catch exceptions during matching 2021-03-24 20:20:24 -07:00
Matt Nadareski
7cc4f6d6a7 Only list protections found, not the files
This may be gated behind a flag in the future as having the full list of files and their associated protections can be useful.
2021-03-21 15:38:52 -07:00
Matt Nadareski
b284ffd99d Fix build 2021-03-20 12:47:51 -07:00
Matt Nadareski
81d0cc2cc2 Replace . with _ for DIC output paths (fixes #264) 2021-03-19 21:14:33 -07:00
Matt Nadareski
70f368d6e9 Make copy scan errors separate (fixes #263) 2021-03-19 17:44:42 -07:00
Matt Nadareski
bfbae5cf5c Fix default output directory in UI 2021-03-15 22:06:31 -07:00
Matt Nadareski
7cc231f5fd Fix incorrectly disposing stream 2021-03-15 22:06:14 -07:00
Matt Nadareski
eda34b3477 Fix text alignment 2021-03-14 19:37:06 -07:00
Matt Nadareski
bfb67ec0c9 Make UI more consistent for dump button 2021-03-12 15:29:26 -08:00
Matt Nadareski
275fd0da48 Rearrange DumpEnvironment code 2021-03-12 15:14:40 -08:00
Matt Nadareski
1ad5c25ed0 Remove .NET Framework 4.7.2, update copyright dates 2021-03-12 15:05:33 -08:00
Matt Nadareski
512f7ae016 Clean up parameters and related
This makes some former virtual methods into properties, cleans up the location of some logic, and makes the BaseParameters class a little neater
2021-03-12 14:59:04 -08:00
Matt Nadareski
3a00efc7fd Make dumping path a little easier to follow
Resets the progress bar a bit earlier
Disables/enables UI elements during dumping to avoid weird interactions
Handle more things that shouldn't be verbose logs
2021-03-11 22:14:19 -08:00
Matt Nadareski
3068c74ad7 DVD/BD have more label info possible 2021-03-11 18:50:06 -08:00
Matt Nadareski
9bdead5616 Various bits of maintenence
Added and fixed some of the CD DIC Matchers
Added caching for last line of text and last matcher used
Made replacing the last line more efficient
Fixed default system handling
Fixed potential issue with sector reading
2021-03-11 13:02:48 -08:00
Matt Nadareski
0ed5feb96b Support a new Matcher for DIC DVD 2021-03-10 19:11:31 -08:00
Matt Nadareski
ec80ef2ede Sort matchers and add a couple more 2021-03-10 16:08:08 -08:00
Matt Nadareski
617591cc26 Fix redump username binding 2021-03-10 15:25:42 -08:00
Matt Nadareski
c855aa5cef Make outputting to separate window a setting 2021-03-10 15:13:11 -08:00
Matt Nadareski
e2a27479f5 Display dump progress in log window (fixes #131)
This builds heavily upon the work that Jack put into capturing and processing DIC outputs. For now, there are only matchers for DIC outputs. All other programs will still direct to the log window, but may not be processed as neatly
2021-03-10 14:51:13 -08:00
Matt Nadareski
c8cfe76aa3 Better text bindings 2021-03-09 16:38:15 -08:00
Matt Nadareski
ecb0234258 Make inner and outer layers more apparent
Also makes label and data side data make more sense
2021-03-09 14:06:18 -08:00
Matt Nadareski
24ce4bcc51 Make layers more clearly named 2021-03-09 13:32:10 -08:00
Matt Nadareski
162af3de31 Hook up default system in options (fixes #246) 2021-03-09 12:01:45 -08:00
Matt Nadareski
7c0af96dc9 Make XGD extension 2021-03-09 09:55:08 -08:00
Matt Nadareski
026f999b46 VideoNow discs are audio only (fixes #261) 2021-03-09 09:41:39 -08:00
Matt Nadareski
bbbc3e98a0 One more pass on options 2021-03-08 22:19:22 -08:00
Matt Nadareski
f78ad4e7b6 Fix incorrect eject value for Check 2021-03-08 20:48:51 -08:00
Matt Nadareski
d756ae2163 Fix poorly designed tests 2021-03-08 16:19:53 -08:00
Matt Nadareski
701738ce25 Forgot about nulls 2021-03-08 15:47:29 -08:00
Matt Nadareski
620bdc9132 Reduce logic weirdness, possibly fix PIC layerbreak 2021-03-08 15:40:30 -08:00
Matt Nadareski
71f474c26c CMI is its own option 2021-03-08 15:20:13 -08:00
Matt Nadareski
c48137a5c1 Update changelog (fixes #259) 2021-03-08 15:11:11 -08:00
Matt Nadareski
e248368484 Add force dumping option for Aaru 2021-03-08 15:10:09 -08:00
Matt Nadareski
7225b8c32d Make eject after dump an option 2021-03-08 15:06:40 -08:00
Matt Nadareski
20e475e130 Separate out Aaru and DIC options 2021-03-08 14:56:26 -08:00
Matt Nadareski
88dc005592 Use aaruf instead of aif 2021-03-08 14:51:44 -08:00
Matt Nadareski
40792ff0eb Update changelist 2021-03-07 14:20:01 -08:00
Matt Nadareski
6ae59e91de Shuffle things for Redump obviousness 2021-03-07 14:17:25 -08:00
Matt Nadareski
5c7aa5f7f5 Hook up language selection for PS2 2021-03-07 13:45:24 -08:00
Matt Nadareski
aaa1ed674f Fix path population on drive scan 2021-03-07 10:19:59 -08:00
Matt Nadareski
42def3bbe5 Fix dropdown population 2021-03-07 10:05:54 -08:00
Matt Nadareski
9cdab52556 Fix system selection regression 2021-03-06 21:49:33 -08:00
Matt Nadareski
5c104ebf3d Fix tests 2021-03-06 21:47:02 -08:00
Matt Nadareski
7ab72e542c Fix auto extension filling 2021-03-05 17:18:08 -08:00
Matt Nadareski
3a8420cd2f Fix broken dumping 2021-03-05 17:14:15 -08:00
Matt Nadareski
916aa9fd70 Move things around like Shadow 2021-03-02 11:34:48 -08:00
Matt Nadareski
8809df10b6 Use Shadow's disable code for categories 2021-03-02 09:55:50 -08:00
Matt Nadareski
34d65a9d70 KnownSystem cleanup based on Shadow's stuff 2021-03-02 09:48:36 -08:00
Matt Nadareski
2830d7c8a2 Use Shadow's combined element model 2021-03-02 09:08:56 -08:00
Matt Nadareski
c702c8b0ac Update to DIC 20210301 2021-03-02 09:05:44 -08:00
Matt Nadareski
e1df9a9754 Use Shadow's description converter implementation 2021-03-01 23:02:45 -08:00
Matt Nadareski
03fe5ce8a5 Take Shadow's advice about lists 2021-03-01 22:58:55 -08:00
Matt Nadareski
432a9dda16 Clean up options binding 2021-03-01 21:51:40 -08:00
Matt Nadareski
1dfc0f731e UniformGrid 2021-03-01 21:09:26 -08:00
Matt Nadareski
af515e0adf Update attributions 2021-02-28 13:19:55 -08:00
Matt Nadareski
2222b57143 Fix spelling 2021-02-28 11:37:53 -08:00
Matt Nadareski
d8cfa2e6a4 Quick touch-up on disc info window 2021-02-28 11:36:29 -08:00
Matt Nadareski
a622cdd4e0 Clean up tabified Options window 2021-02-28 11:20:38 -08:00
Matt Nadareski
2a4ec83d0d 1 to 1 tabification of Options window 2021-02-28 11:02:21 -08:00
Matt Nadareski
b1d144d091 A little more log view cleanup 2021-02-28 10:55:49 -08:00
Matt Nadareski
23a8b66c16 Get rid of warning 2021-02-28 10:29:51 -08:00
Matt Nadareski
381ebd5961 Clean up logging view 2021-02-28 10:28:31 -08:00
Matt Nadareski
b4d1f7d6c3 LogWindow -> LogOutput 2021-02-28 02:28:53 -08:00
Matt Nadareski
b3eff64275 Get fancy with expander 2021-02-28 00:46:38 -08:00
Matt Nadareski
f15c00acca Multiline comments and contents 2021-02-28 00:36:36 -08:00
Matt Nadareski
a66d0d4722 Perform some cleanup on Options window 2021-02-27 23:23:12 -08:00
Matt Nadareski
66cdade01b Fix layerbreak reading (wrong endianness) 2021-02-27 23:04:25 -08:00
Matt Nadareski
9e9f001768 More StackPanel stuff 2021-02-27 22:39:07 -08:00
Matt Nadareski
3514bdcbc4 Update changelist 2021-02-27 22:32:37 -08:00
Matt Nadareski
312df37365 Use tabs, enable layers 2 and 3 2021-02-27 22:32:03 -08:00
Matt Nadareski
6214d91940 Rename ring fields in SubmissionInfo 2021-02-27 21:47:10 -08:00
Matt Nadareski
23d500434b Add and use UserInput
Please note that this only affects the disc information window for now. This may end up in the others later.
2021-02-27 21:33:37 -08:00
Matt Nadareski
7ee5e3ab43 Add PIC parsing 2021-02-27 16:58:09 -08:00
Matt Nadareski
6be4bb7c85 Boilerplate for 4 layer support 2021-02-26 22:52:28 -08:00
Matt Nadareski
d620c13e22 Update changelist 2021-02-26 22:38:21 -08:00
Matt Nadareski
73b585cdd2 Add PS5 version extraction (fixes #258) 2021-02-26 22:36:43 -08:00
Matt Nadareski
2f33581e4a Add PS5 console to list 2021-02-26 22:27:36 -08:00
Matt Nadareski
f3dba9a026 Add Xbox Series consoles to list 2021-02-26 22:13:18 -08:00
Matt Nadareski
d7d0af369c Boilerplate for 3 layer support 2021-02-26 21:58:10 -08:00
Matt Nadareski
2ab9a45a71 Update changelist 2021-02-04 22:06:13 -08:00
Matt Nadareski
dc0bf642a9 Better handling of drive changes
Similar to how things like changed options, UI initialization, and media scan all share basically the same path, most of what goes into changing drive letter is the same. So with a small tweak to that code, it is viable to use that method for all 4 paths.
2021-02-04 21:52:26 -08:00
Matt Nadareski
9e32435972 Reorganize Drive class a little 2021-02-04 21:38:57 -08:00
Matt Nadareski
c2b724e949 Auto-detect VCD (fixes #257) 2021-02-04 21:16:42 -08:00
Matt Nadareski
195d3c6ff7 Update to DIC 20210202 2021-02-01 21:16:59 -08:00
Matt Nadareski
04a8079cee Add better notes and outputs for process 2021-01-26 22:46:06 -08:00
Matt Nadareski
0b2daf7d30 Add notes, remove a couple files from serialization 2021-01-26 22:45:38 -08:00
Matt Nadareski
1353ee603b Fix serialization issue 2021-01-26 22:45:04 -08:00
Matt Nadareski
e9d272d139 Potentially massive file gets compressed 2021-01-26 20:55:45 -08:00
Matt Nadareski
0308c7a81e Direct to Base64 for binary files 2021-01-26 17:12:44 -08:00
Matt Nadareski
4caad10c9b Comment out binary files for now 2021-01-26 14:36:35 -08:00
Matt Nadareski
85c1623d33 Forgot the DVD/BD binary files 2021-01-26 13:55:05 -08:00
Matt Nadareski
5be1825bb5 Add all output files as encoded artifacts to JSON 2021-01-26 13:45:12 -08:00
Matt Nadareski
ec5e533bd8 Rename currently unused variable 2021-01-26 13:44:38 -08:00
Matt Nadareski
c3e4c7fa78 Add artifacts section to submission info 2021-01-26 11:39:45 -08:00
Matt Nadareski
bf66a3fcbd Add version to submission info 2021-01-25 17:05:32 -08:00
Matt Nadareski
101193cb78 Streamline statuses from trying to execute 2021-01-25 13:11:20 -08:00
Matt Nadareski
b610c29be6 Log exceptions on dumping as well 2021-01-25 12:05:46 -08:00
Matt Nadareski
999c4dceb5 Ensure the executable exists before trying to run it 2021-01-25 11:55:18 -08:00
Matt Nadareski
2957370fac Missed a couple of places 2021-01-25 10:21:50 -08:00
Matt Nadareski
5dd36d7b06 Inline all of those params during logging 2021-01-25 10:02:34 -08:00
Matt Nadareski
47b5bbe7e7 Update to BurnOutSharp 1.5.1 2021-01-22 12:03:12 -08:00
Matt Nadareski
5a2e4ca77e Add link to BOS in issue templates 2021-01-21 11:15:10 -08:00
Matt Nadareski
c479ddb80b Fix duplicate security sector data 2021-01-15 22:06:19 -08:00
Matt Nadareski
da1f59e0c1 Fix UIC output parsing and generation 2021-01-15 14:31:31 -08:00
Matt Nadareski
9574832e86 Add ability to get hashes for UIC outputs 2021-01-15 12:52:55 -08:00
Matt Nadareski
3df7842d23 Add ability to hash a file to base parameters 2021-01-15 12:13:27 -08:00
Matt Nadareski
62a0f6e49a Port most hashing code from SabreTools
Note that this explcitly omits both the formerly-supported RIPEMD160 as well as the currently-Aaru-specific SpamSum.
2021-01-15 11:57:24 -08:00
Matt Nadareski
60c180e4dd UIC outputs don't contain a datfile 2021-01-15 11:49:27 -08:00
Matt Nadareski
343d937362 Fix BCA formatting for CleanRip outputs (fixes #251) 2021-01-12 15:36:02 -08:00
Matt Nadareski
f20b321433 Add *tmp file support for DIC 2021-01-07 09:52:00 -08:00
Matt Nadareski
ba19b0825f Update changelist 2021-01-06 09:58:50 -08:00
Matt Nadareski
5abb44957c Add common path for init of full UI
This unites the code that runs when the application is loaded, when options are updated, and when a drive scan is initiated. All 3 situations require that nearly every bit of the UI and possibly even the disc to be re-scanned.
2021-01-05 21:39:46 -08:00
Matt Nadareski
b9d9cf812f Slightly different _mainInfo wording now... 2021-01-03 14:22:25 -08:00
Matt Nadareski
d1529f428a Updated to DIC version 20210102 2021-01-02 22:03:18 -08:00
Matt Nadareski
5061a79c4c Make initial window load more accurate 2020-12-31 14:57:06 -08:00
Matt Nadareski
eab3e25cf2 Fix crash on drive scan 2020-12-31 13:39:19 -08:00
Matt Nadareski
33e73a8992 More messages isnt a bad thing 2020-12-31 11:02:14 -08:00
Matt Nadareski
a63972b549 Get reasonable defaults based on selected system 2020-12-30 22:57:36 -08:00
Matt Nadareski
bc57df7fc1 Add a note for later 2020-12-30 22:29:42 -08:00
Matt Nadareski
e16f616286 Safer queries on getting disc type 2020-12-30 22:20:47 -08:00
Matt Nadareski
c5497d787b Ensure parameters have system and type set 2020-12-30 21:14:06 -08:00
Matt Nadareski
64f60004c4 Error messages on Redump errors 2020-12-30 15:00:52 -08:00
Matt Nadareski
020c063177 Parameters should know their state better 2020-12-30 14:49:28 -08:00
Matt Nadareski
a3e6f8157f Extension cleanup 2020-12-30 14:28:46 -08:00
Matt Nadareski
f27040cd1c Add informational issue template 2020-12-30 14:07:11 -08:00
Matt Nadareski
3a490cfc8d Add note about DIC-only dumps 2020-12-30 14:02:51 -08:00
Matt Nadareski
1f8c5998c1 Fix copy-paste error 2020-12-29 11:15:24 -08:00
Matt Nadareski
f96d4ee37e More safety around listing operations 2020-12-29 11:10:15 -08:00
Matt Nadareski
d0d15f03af Remove Avalonia UI (fixes #245) 2020-12-29 10:56:25 -08:00
Matt Nadareski
eff6407206 Remove CD-i DV from supported profiles 2020-12-27 13:39:26 -08:00
Matt Nadareski
c1e559568a Fix build 2020-12-26 14:31:59 -08:00
Matt Nadareski
ef030b290c Add support for new Redump regions 2020-12-26 14:03:24 -08:00
Matt Nadareski
71630591a1 Add short-circuit for unmatched tracks 2020-12-12 13:37:33 -08:00
Matt Nadareski
31505745be Update to Aaru v5.2 2020-12-02 20:51:56 -08:00
Matt Nadareski
e40444b60c Update changelist 2020-11-30 21:24:15 -08:00
Matt Nadareski
44edf387a6 Relabel labels depending on media type 2020-11-30 16:12:54 -08:00
Matt Nadareski
d3d225644d Add preliminary "default system" support 2020-11-23 14:00:15 -08:00
Matt Nadareski
3afd9200a9 Multiline fields require matching newlines 2020-11-23 13:45:36 -08:00
Matt Nadareski
c5c340ff12 Make label SID change for Avalonia too 2020-11-23 13:22:30 -08:00
Matt Nadareski
b28b85f7c6 Don't gray out label SID for DVD, HD-DVD, BD (fixes #247) 2020-11-23 13:20:37 -08:00
Matt Nadareski
c60684b83c Pass through progress in missing place 2020-11-23 13:17:27 -08:00
Matt Nadareski
a183a1ec91 Fix issue with 0xbe drives, again 2020-11-12 14:43:24 -08:00
Matt Nadareski
abd8488185 Fix remaining links 2020-11-10 20:42:04 -08:00
Matt Nadareski
e6dea158e5 Rename to MPF 2020-11-10 17:43:21 -08:00
Matt Nadareski
24150c7b3c Bump version to 1.18 2020-11-10 10:55:29 -08:00
Matt Nadareski
040720a1c2 Detect HD-DVD-Video 2020-11-08 13:40:30 -08:00
Matt Nadareski
70d30d370f Update to BurnOutSharp 1.5.0 2020-11-03 21:49:59 -08:00
Matt Nadareski
a50b99da07 Use older version of DIC for now 2020-11-01 16:08:57 -08:00
Matt Nadareski
2cecbe69ad Support the /ps command for DIC 2020-10-31 23:05:11 -07:00
Matt Nadareski
557410660f Update to DIC 20201101 2020-10-31 22:55:07 -07:00
Matt Nadareski
0d75dbaaa2 Add dumping program selection finally 2020-10-11 22:06:40 -07:00
Matt Nadareski
d38f95c73a Check sector 0 for Saturn, if possible (fixes #241) 2020-10-11 21:15:35 -07:00
Matt Nadareski
c9d59a90e4 Default to CD-ROM when detection fails as well 2020-10-11 21:07:28 -07:00
Matt Nadareski
fc6a34d987 Use CD-ROM as default when no type detection (fixes #237) 2020-10-11 20:46:24 -07:00
Matt Nadareski
8acabb692f Remove default config file (fixes #238) 2020-10-11 20:36:12 -07:00
Matt Nadareski
61f8871839 Change default for fixed drive detection (fixes #233) 2020-10-11 20:35:39 -07:00
Matt Nadareski
69d61ad185 Use selected drive in UI for copy protect scan (fixes #235) 2020-10-11 20:31:27 -07:00
Matt Nadareski
27391ed31b Remove subdump (fixes #234)
Note that it still downloaads the subdump executable and puts it in the same place. This won't be changing anytime soon since it's useful. It also is flat out removing the option in the menu without replacing or reshaping the menu. This will be addressed at a later date.
2020-10-11 20:24:41 -07:00
Matt Nadareski
ed56c92101 Clean up sector read method a bit more (nw) 2020-10-08 21:21:11 -07:00
Matt Nadareski
ab8ae1524f Actually use the drive letter (nw) 2020-10-08 14:18:23 -07:00
Matt Nadareski
de7c247583 Add initial version of sector reading to library 2020-10-08 12:26:28 -07:00
Matt Nadareski
997fd8f7ac Why do I keep forgetting changelist? 2020-10-01 11:46:55 -07:00
Matt Nadareski
e39e07246a Fix archive naming 2020-10-01 11:34:27 -07:00
Matt Nadareski
e0b0406d76 Let's see how badly this goes... 2020-10-01 11:28:39 -07:00
Matt Nadareski
1efbe9a784 Update BurnOutSharp version; fix autobuild 2020-10-01 09:44:49 -07:00
Matt Nadareski
fab921c2dd Added feature request template 2020-09-29 13:01:28 -07:00
Matt Nadareski
56896b4bea Update issue templates 2020-09-29 12:57:19 -07:00
Matt Nadareski
54eb7d8ecd Forgot the changelog 2020-09-28 11:26:47 -07:00
Matt Nadareski
114587b9f6 Reset paragraph state on clear (fixes #231) 2020-09-28 11:22:40 -07:00
Matt Nadareski
cad33c6c07 Don't lose automatic comments 2020-09-24 15:25:56 -07:00
Matt Nadareski
8154999f1c Fix alternate mainInfo parsing 2020-09-24 15:08:17 -07:00
Matt Nadareski
3a30c14085 Make this apparent on the main page 2020-09-24 11:42:28 -07:00
Matt Nadareski
db8b1df480 Add rolling changelog section 2020-09-24 11:35:16 -07:00
Matt Nadareski
e20cac8a80 Sync 2020-09-24 10:57:09 -07:00
Matt Nadareski
ce9b4e39f5 Add more internal info for GD-ROM LD 2020-09-24 10:49:27 -07:00
Matt Nadareski
ddff4b2e58 SCD has internal serial 2020-09-24 10:26:51 -07:00
Matt Nadareski
16a470475c Remember, no Disc 2020-09-24 10:21:35 -07:00
Matt Nadareski
6e01473e87 Saturn serial -> internal serial, strip 'V' from version 2020-09-24 10:19:34 -07:00
Matt Nadareski
8682089151 Update to DIC 20200921 2020-09-23 10:11:38 -07:00
Matt Nadareski
f45cb0075f Remove superflous check for PVD retrieval (fixes #229) 2020-09-19 14:31:17 -07:00
Matt Nadareski
afccb48798 Specify solution file 2020-09-19 13:40:36 -07:00
Matt Nadareski
ce6995fba0 It was seriously a directory marker issue 2020-09-17 22:04:31 -07:00
Matt Nadareski
3328d1adea Try without the git extension 2020-09-17 22:00:51 -07:00
Matt Nadareski
33d79df9a8 Update AppVeyor 2020-09-17 21:59:07 -07:00
Matt Nadareski
4fb64b19d6 Remove weird csproj stuff 2020-09-17 21:57:00 -07:00
Matt Nadareski
bf0f495c8b Ignore all files except the compiled cicm.cs 2020-09-17 21:54:50 -07:00
Matt Nadareski
5d5f8e8d8c Use official repo as submodule for CICM 2020-09-17 21:52:40 -07:00
Matt Nadareski
91aa248355 A little more cleanup and safety 2020-09-17 21:38:45 -07:00
Matt Nadareski
633c6c1efb Slight tweaks, mostly naming 2020-09-17 21:25:22 -07:00
Matt Nadareski
facb1f673f Wrap login call with try/catch 2020-09-17 15:52:29 -07:00
Matt Nadareski
9c8938c7f2 Get some more layerbreak stuff 2020-09-17 13:30:35 -07:00
Matt Nadareski
17f75f3ce6 First parts of layerbreak info 2020-09-17 10:37:04 -07:00
Matt Nadareski
b99c48afa2 First part of DVD protection extraction for Aaru 2020-09-16 20:45:07 -07:00
Matt Nadareski
9481fada23 Fix writing of valid indexes for Aaru cuesheet 2020-09-16 17:13:15 -07:00
Matt Nadareski
7efbc5043c Add error count for Aaru outputs 2020-09-16 17:00:11 -07:00
Matt Nadareski
92880d3148 Fix discs with ISO extension 2020-09-16 16:25:33 -07:00
Matt Nadareski
9800f2b8ae Use spans instead of Linq (and accidental commit) 2020-09-16 16:09:34 -07:00
Matt Nadareski
a5d01604cb Strip out hard-to-find CD Check instances 2020-09-16 16:01:16 -07:00
Matt Nadareski
aaab84f90a Fix multiple things in Aaru
Addresses overlooked issue with DatFile generation, consolidates common code into methods, fixes PVD generation and write
2020-09-16 15:59:11 -07:00
Matt Nadareski
318a1a303c Fix multiline outputs 2020-09-16 15:58:02 -07:00
Matt Nadareski
0061de6b2e Fix cuesheet write-to-stream 2020-09-16 15:57:22 -07:00
Matt Nadareski
b3db7c547f Generate cuesheet using new classes for Aaru 2020-09-16 14:31:29 -07:00
Matt Nadareski
1da29464a8 Add writing of cuesheets 2020-09-16 14:09:02 -07:00
Matt Nadareski
d1d4ff41c6 Fix reading (assumes reasonably sized cuesheets) 2020-09-16 13:41:00 -07:00
Matt Nadareski
673c2745a9 Fix index check 2020-09-16 13:00:42 -07:00
Matt Nadareski
4ad88441cc Add parsing in of cuesheets 2020-09-16 12:53:28 -07:00
Matt Nadareski
71bb822856 Add cuesheet structures 2020-09-16 11:22:33 -07:00
Matt Nadareski
2a7789bd12 Bump version to 1.17.1 2020-09-15 10:03:39 -07:00
Matt Nadareski
1d0417cc1c Fix DiscInformationWindow issues 2020-09-14 13:51:26 -07:00
Matt Nadareski
905c578d90 Fix DIC flags based on the code 2020-09-14 13:30:14 -07:00
Matt Nadareski
a457c85e53 Better internal handling of Options window 2020-09-13 21:01:53 -07:00
Matt Nadareski
36630fc0ed Fix the tests I broke 2020-09-13 20:52:08 -07:00
Matt Nadareski
f509ac60da Make DiscInformationWindow more robust 2020-09-13 20:41:23 -07:00
Matt Nadareski
bf8aac6f81 Move some constants around 2020-09-13 20:41:09 -07:00
Matt Nadareski
ceddcc0722 Remove useless passthrough vars 2020-09-12 14:11:55 -07:00
Matt Nadareski
3a2bda6fb6 Bump version to 1.17 2020-09-11 21:31:46 -07:00
Matt Nadareski
aea2403153 Remove fields that are too variable 2020-09-11 11:48:45 -07:00
Matt Nadareski
6e334df42f Cleanup now that .NET 4.6.2 is gone 2020-09-10 23:30:07 -07:00
Matt Nadareski
04cb68c9bd Rearrange based on Avalonia 2020-09-10 23:21:18 -07:00
Matt Nadareski
06060c071b Update all libraries, including BurnOutSharp 2020-09-10 22:45:36 -07:00
Matt Nadareski
02903686ad Remove accidental add 2020-09-10 22:02:12 -07:00
Matt Nadareski
00cbed725d Only fill error count if it's missing 2020-09-10 21:09:35 -07:00
Matt Nadareski
dabccd3a8a Fix for LibCrypt detect until BOS updated 2020-09-10 21:00:16 -07:00
Matt Nadareski
0e1e499a66 Guard against false positive LibCrypt (fixes #226) 2020-09-10 20:46:43 -07:00
Matt Nadareski
b4f485a0cc Add PS1/PS2 internal serial to comments (fixes #227) 2020-09-10 20:32:51 -07:00
Matt Nadareski
5bad68b9ff Include warning count with error count 2020-09-10 20:14:08 -07:00
Matt Nadareski
2db686fc47 Backport accept/cancel from Avalonia 2020-09-10 11:44:44 -07:00
Matt Nadareski
d705a4b316 UmdImageCreator stands alone 2020-09-10 11:27:25 -07:00
Matt Nadareski
0154ee5bb6 Start wiring through DCDumper a bit 2020-09-10 11:11:26 -07:00
Matt Nadareski
29f14f5690 Remove .NET 4.6.2 from readme 2020-09-10 10:54:47 -07:00
Matt Nadareski
dd6f7b0794 Consolidate version checking 2020-09-10 10:53:47 -07:00
Matt Nadareski
e5c9591d64 Merge pull request #225 from SabreTools/crossplat
Add Avalonia UI
2020-09-10 10:37:13 -07:00
Matt Nadareski
df8da8bd46 Fix PS1 copy protect output (fixes #222) 2020-09-10 10:32:21 -07:00
Matt Nadareski
c9b21006d1 Fix update check (fixes #220) 2020-09-09 14:34:50 -07:00
Matt Nadareski
2b37882322 Reformat CSS data (fixes #224) 2020-09-09 14:07:12 -07:00
Matt Nadareski
8a2b3f2c89 Separation of Creators 2020-09-09 11:08:20 -07:00
Matt Nadareski
c16a9aeb70 Couple more 2020-09-09 10:38:00 -07:00
Matt Nadareski
d5a0797c92 Couple more mapped values 2020-09-09 10:36:43 -07:00
Matt Nadareski
a55a769886 Fix conversion issue 2020-09-09 10:05:09 -07:00
Matt Nadareski
fd18c60f28 Let's reference the Wiki 2020-09-08 23:30:02 -07:00
Matt Nadareski
2704d1f88d Add 'private' flag to Paranoia mode for Aaru (fixes #212) 2020-09-08 22:53:43 -07:00
Matt Nadareski
c16c938fa4 Change end of section marker for XGD (fixes #219) 2020-09-08 22:51:17 -07:00
Matt Nadareski
497e2a09fd Add as many default regions as possible 2020-09-08 22:41:36 -07:00
Matt Nadareski
8bc8887ce6 Set Region name default again 2020-09-08 22:22:52 -07:00
Matt Nadareski
7e5a5586f9 Merge pull request #221 from sadikyo/patch-1
Update README.md
2020-09-08 22:14:56 -07:00
sadikyo
eee068db7e Update README.md
A few minor spelling and grammar corrections
2020-08-27 13:31:47 -04:00
Matt Nadareski
79e70173fa Add more standard programs to doc 2020-08-14 16:12:57 -07:00
Matt Nadareski
4f732557b9 Drive label should not contain bad chars 2020-08-14 12:59:23 -07:00
Matt Nadareski
6cbcebd661 Do the README shuffle 2020-08-14 11:06:57 -07:00
Matt Nadareski
f74dc01657 Fix percentage in Avalonia 2020-08-07 22:05:04 -07:00
Matt Nadareski
4735bef7b4 Merge branch 'master' into crossplat 2020-08-07 22:01:56 -07:00
Matt Nadareski
52a925df12 Fix percentage output 2020-08-07 22:00:15 -07:00
Matt Nadareski
3e10199cb0 Add path and scan to Check 2020-08-07 21:15:36 -07:00
Matt Nadareski
470e641a8c Protection scan output to Avalonia 2020-08-06 22:53:11 -07:00
Matt Nadareski
f61b84d058 Okay, fine 2020-08-06 22:53:11 -07:00
Matt Nadareski
5e77b43be1 Add icon, fix version, copyright, appveyor 2020-08-06 22:53:09 -07:00
Matt Nadareski
46530a9fca Add Avalonia, remove NET462 2020-08-06 22:53:07 -07:00
Matt Nadareski
12610c0d69 Add missing file feedback 2020-08-06 22:52:25 -07:00
Matt Nadareski
9bb9d3f407 Add progress to protection scan 2020-08-06 22:05:16 -07:00
Matt Nadareski
a0ed20042c Explain components 2020-08-04 13:53:33 -07:00
Matt Nadareski
37c253aac2 Preliminary use of cicm.cs 2020-08-03 12:52:53 -07:00
Matt Nadareski
68135c58ae Vut default hol output 2020-08-02 22:22:33 -07:00
Matt Nadareski
f9efa71fcc Better categorization 2020-08-02 22:00:07 -07:00
Matt Nadareski
184913ad28 Fix a couple of small things 2020-08-02 21:51:19 -07:00
Matt Nadareski
ff52767a02 Add DVD-Audio 2020-08-02 21:41:38 -07:00
Matt Nadareski
34ac5f9ba0 Fix audio disc error count (fixes #215) 2020-08-02 21:22:24 -07:00
Matt Nadareski
7296d109cc Set default categories (fixes #214) 2020-08-02 20:46:30 -07:00
Matt Nadareski
9be705ae30 Remove erroneous return (fixes #213) 2020-07-30 14:32:39 -07:00
Matt Nadareski
af8b376f5a Clarify wording 2020-07-28 14:28:54 -07:00
Matt Nadareski
03f9668048 Update README with more information 2020-07-28 14:10:29 -07:00
Matt Nadareski
3bb23fa7cc Update to Aaru v5.1 2020-07-24 21:04:43 -07:00
Matt Nadareski
f85c9d4e7e Move submission info to more proper place 2020-07-22 21:49:20 -07:00
Matt Nadareski
5de3d209be Migrate more to the proper place 2020-07-22 21:45:58 -07:00
Matt Nadareski
f1b136c817 Remote stands alone 2020-07-22 21:11:40 -07:00
Matt Nadareski
c18829e0b3 Sync 2020-07-22 13:54:45 -07:00
Matt Nadareski
d114b7f868 Add missing media dump params 2020-07-22 11:17:38 -07:00
Matt Nadareski
445cc173ce Fix incorrect PIC formatting 2020-07-16 14:45:51 -07:00
Matt Nadareski
1e28fcad2e Fix usage of avdp flag 2020-07-16 11:46:50 -07:00
Matt Nadareski
8d4e5155d9 Update to DIC 20200716 2020-07-16 11:06:58 -07:00
Matt Nadareski
027f562573 Change types order for XBOX/360 (fixes #209) 2020-07-07 13:13:18 -07:00
Matt Nadareski
74caf084ca Read entire PIC file (fixes #189) 2020-07-07 13:09:54 -07:00
Matt Nadareski
7396b02543 Update to DIC 20200620 2020-07-06 22:15:10 -07:00
Matt Nadareski
8035826b1b Fix datetime parsing 2020-06-25 12:39:27 -07:00
Matt Nadareski
c6e73582c5 More Aaru improvements and fixes 2020-06-25 11:19:16 -07:00
Matt Nadareski
ca09d8c703 Add Aaru to official list in Check 2020-06-25 11:17:26 -07:00
Matt Nadareski
40b6551c7a Fix README formatting 2020-06-16 17:05:17 -07:00
Matt Nadareski
95b664705d Command and DICer 2020-06-16 17:02:28 -07:00
Matt Nadareski
6bda7b35a2 Make async things async 2020-06-16 15:25:55 -07:00
Matt Nadareski
6fedebf2a9 Lower-case SHA-1 for search 2020-06-16 13:57:11 -07:00
Matt Nadareski
02f98c674b Trim on input files for Check 2020-06-16 13:25:45 -07:00
Matt Nadareski
fb1e4130df More CleanRip fixes 2020-06-16 13:25:24 -07:00
Matt Nadareski
c507f52b80 Add a hack for DICUI check in CleanRip 2020-06-16 12:50:04 -07:00
Matt Nadareski
7b2784f1a2 Wii needs CMP for Redump, separate for submission 2020-06-16 12:00:23 -07:00
Matt Nadareski
7a7c83e8cf Fix Redump check for DIC 2020-06-16 11:41:55 -07:00
Matt Nadareski
b86ef09763 Inform Check users if credentials invalid 2020-06-16 11:16:39 -07:00
Matt Nadareski
71c050edf6 Make subdump optional 2020-06-12 11:17:57 -07:00
Matt Nadareski
988f25b514 Fix INI parsing 2020-06-11 21:46:25 -07:00
Matt Nadareski
e878e8b904 Anti-modchip for the rest of us 2020-06-05 15:38:48 -07:00
Matt Nadareski
bd3484cb3d Better common methods 2020-06-05 14:15:06 -07:00
Matt Nadareski
3d769ed707 Private again 2020-06-05 11:15:51 -07:00
Matt Nadareski
613be66f91 Add INI parser, use for PS1/2 discs 2020-06-05 11:13:40 -07:00
Matt Nadareski
81e0ffbc3c Update to DIC 20200604, static 2020-06-04 16:36:09 -07:00
Matt Nadareski
8f3d325e7d Update to version 1.16.1 2020-05-07 15:40:29 -07:00
Matt Nadareski
a63472c6e6 Options (#201)
* Start reorganizing options and internals

* Make entire options flow more robust

* Few more TODOs, slightly cleaner code

* Simplify Options constructor

* Fix eject and reset

* Some other abstractions

* Enforce readonly

* Fix tests, like the TODO said

* Move specific output file parsing to specific parameters

* Add some future enums, add notes around .NET Core build path

* Wrap last incompatible .NET Core stuff

* Extract out CleanRip, fix a bunch of other spaghetti
2020-05-07 14:36:06 -07:00
Matt Nadareski
8c98005605 Fix CleanRip support 2020-05-07 14:23:49 -07:00
Matt Nadareski
8062d6cf17 Extract out CleanRip, fix a bunch of other spaghetti 2020-05-07 13:56:21 -07:00
Matt Nadareski
78bf6e63ed Wrap last incompatible .NET Core stuff 2020-05-07 13:03:24 -07:00
Matt Nadareski
68e0d759f7 Add some future enums, add notes around .NET Core build path 2020-05-07 12:58:37 -07:00
Matt Nadareski
26e72284af Move specific output file parsing to specific parameters 2020-05-06 20:24:33 -07:00
Matt Nadareski
0efecd6601 Fix tests, like the TODO said 2020-05-06 17:00:34 -07:00
Matt Nadareski
9a3c2eb626 Enforce readonly 2020-05-06 16:46:26 -07:00
Matt Nadareski
ca753b4526 Some other abstractions 2020-05-06 16:37:33 -07:00
Matt Nadareski
230b6ca721 Fix eject and reset 2020-05-06 16:06:30 -07:00
Matt Nadareski
dc90f9af3f Simplify Options constructor 2020-05-06 15:56:08 -07:00
Matt Nadareski
b5898c7ea3 Few more TODOs, slightly cleaner code 2020-05-06 15:44:46 -07:00
Matt Nadareski
36f1aea509 Make entire options flow more robust 2020-05-06 15:33:28 -07:00
Matt Nadareski
6742444182 Start reorganizing options and internals 2020-05-06 14:24:37 -07:00
Matt Nadareski
e01fd37e6b Typo 2020-05-05 14:37:34 -07:00
Matt Nadareski
ca7071f82a Update Aaru version for WIP builds 2020-05-05 14:23:23 -07:00
Matt Nadareski
3cb67e3e65 Add Python2 CNF parsing, forgot DumpEnv fixes 2020-05-01 15:02:58 -07:00
Matt Nadareski
e67dd589b5 Fix options, add fallbacks, consolidate code 2020-05-01 14:59:29 -07:00
Matt Nadareski
74f491eaaa Forgot the flag 2020-04-21 17:03:45 -07:00
Matt Nadareski
9ebd28ef5a More dd stuff, including AppVeyor. Why? Because. 2020-04-21 16:09:37 -07:00
Matt Nadareski
2a58052bfd So, I added DD support 2020-04-21 15:52:57 -07:00
Matt Nadareski
5ed73aff2b Mock out the basics needed for DD 2020-04-21 14:39:39 -07:00
Matt Nadareski
5a6ad09004 No DIC firmware check temporarily (fixes https://github.com/SabreTools/DICUI/issues/199) 2020-04-21 11:24:22 -07:00
Matt Nadareski
6a43b74043 Fix file location in AppVeyor builds 2020-04-13 12:17:17 -07:00
Matt Nadareski
69dc91aa1a Update to 1.16 2020-04-13 12:03:58 -07:00
Matt Nadareski
f840db5143 Cleanup and Upgrades (#197)
* First part of cleanup

* Second part of cleanup

* Second part of cleanup, part two

* Second part of cleanup, part three

* Second part of cleanup, part four

* Third part of cleanup

* Fourth part of cleanup (nw)

* Rebranding

* Aaru-fication

* Try to fix .NET Core builds

* Strip out CD Check for false positives

* Update DIC to 20200403

* Add .NET 4.8 to automated builds

* Address a couple of TODOs

* Typo

* Aaru is up to date

* Fix AppVeyor

* Add new systems (fixes #196)

* Fix build

Co-authored-by: Matt Nadareski <mnadareski@mparticle.com>
2020-04-13 11:55:21 -07:00
Matt Nadareski
f33cd41ebb Update BurnOutSharp (fixes #191) 2020-02-18 14:27:44 -08:00
Matt Nadareski
da7896e62a Make it super apparent they're disabled 2020-02-18 11:43:49 -08:00
Matt Nadareski
08b5b4a1aa Condiitionally disable L1 for single-layer discs 2020-02-18 11:34:57 -08:00
Matt Nadareski
c397701818 Better check for submission info write (fixes #190 2020-02-18 11:25:34 -08:00
Matt Nadareski
617eb00f21 Fix PS regex matching (fixes #192) 2020-02-18 11:15:26 -08:00
Matt Nadareski
751fd0cf1e Check PSX.EXE date second (fixes #193) 2020-02-18 11:05:15 -08:00
Matt Nadareski
84c64e6073 Add Chef as Check flag 2020-02-06 23:54:49 -08:00
Matt Nadareski
ff5bc464ab Non-dumping commands shouldn't cause issues 2020-02-04 13:25:50 -08:00
Matt Nadareski
b09a87c7e4 Partial datfile and cuesheet support for Chef 2020-02-04 00:44:12 -08:00
Matt Nadareski
4032b48e63 Calculate start better 2020-02-04 00:41:52 -08:00
Matt Nadareski
12fad35065 Fix Chef flag handling for debug, verbose, and version 2020-02-04 00:20:13 -08:00
Matt Nadareski
c28be2da72 Update Creator version... again 2020-02-04 00:16:19 -08:00
Matt Nadareski
122cc0d20b Support similarly named parameters for DiscImageChef 2020-02-03 14:17:42 -08:00
Matt Nadareski
89db32dd53 Preliminary Support for DiscImageChef (#188)
* Professional cook

* Accuracy improvements

* Better yet

* Spicy

* Simplify and reduce

* You eye note

* More info for validation

* Read you

* Verbose

* Typo

* Custom fix

* Of note

* Clearly the same

* Creator update

* Creator MCN flag gone

* Missed a spot

* Fix issues after code walkthrough
2020-02-03 14:02:24 -08:00
Matt Nadareski
59040ae0f0 Update naming schemes 2020-01-30 14:47:23 -08:00
Matt Nadareski
d8035745a9 Add button to save console output 2020-01-29 13:28:27 -08:00
Matt Nadareski
c73e13c1f0 Update packages, better LibCrypt handling (fixes #134) 2020-01-29 12:21:41 -08:00
Matt Nadareski
af75c7c949 Add .NET Core 3.0 note to README 2020-01-29 10:18:43 -08:00
Matt Nadareski
896eb28308 Upgrade EDC check for PS1 (fixes #148) 2020-01-29 10:17:56 -08:00
Matt Nadareski
6ca3d39e5f Manual protect scan can handle SmartE weirdness 2020-01-28 21:25:26 -08:00
Matt Nadareski
a7af0d0d7b Add P* serials for PlayStation (fixes #187) 2020-01-27 22:38:07 -08:00
Matt Nadareski
04f5a7ea8d Add remote version check (fixes #45) 2020-01-27 22:27:40 -08:00
Matt Nadareski
5d82cc5622 Add option to reset drive after dump (fixes #143) 2020-01-27 13:33:30 -08:00
Matt Nadareski
2f37f51c0c Simplify serial matching for PS1/PS2 (fixes #186) 2020-01-27 11:58:08 -08:00
Matt Nadareski
4690db61e1 Add CleanRip formatting support (fixes #185) 2020-01-27 03:06:11 -08:00
Matt Nadareski
672ec42903 Don't overwrite region if it already exists 2020-01-27 01:38:52 -08:00
Matt Nadareski
cb39599a46 Get region from PS1/2 executable name (fixes #175) 2020-01-27 01:34:39 -08:00
Matt Nadareski
80b5ff3920 Fix options UI, add ignore fixed drives setting (fixes #182) 2020-01-27 00:42:58 -08:00
Matt Nadareski
f27d3a91d3 Fix Check build 2020-01-27 00:04:17 -08:00
Matt Nadareski
b18357bff5 Better eject location 2020-01-27 00:03:03 -08:00
Matt Nadareski
e3cd5b7082 Move eject to before user input (fixes #173) 2020-01-26 23:59:05 -08:00
Matt Nadareski
16d7a6224b Fix drive speed selection (fixes #177) 2020-01-26 23:18:20 -08:00
Matt Nadareski
f8e32ec06c Multi-select language dropdown in submission info (fixes #170) 2020-01-26 23:06:52 -08:00
Matt Nadareski
7073d4e298 Allow testing of credentials from UI (fixes #171) 2020-01-26 22:59:03 -08:00
Matt Nadareski
f7f464920b Fix ampersand tests 2020-01-26 22:45:59 -08:00
Matt Nadareski
e677dc20e9 Add missing default setting 2020-01-26 22:45:46 -08:00
Matt Nadareski
9871eb9928 Save path fixing until the very end (fixes #176) 2020-01-26 22:24:10 -08:00
Matt Nadareski
12b43cf688 Remove ampersand filtering (fixes #180) 2020-01-26 22:06:38 -08:00
Matt Nadareski
7dd6db66ee GD-ROM LD area header extraction 2019-12-23 13:07:23 -08:00
Matt Nadareski
d7726a070e Update to DIC 20191223 2019-12-23 10:28:42 -08:00
Matt Nadareski
fbc9d04782 Advanced Support (#172)
* Who doesn't like drives?

* Add another TODO

* Use built in stuff, it's quicker

* More special handling for floppies, easier this time

* Fix broken test

* Set active drive priority, String -> string

* Track reason for no scanning

* Update DIC version and release notes
2019-11-17 01:06:41 -05:00
Matt Nadareski
24b08dd245 Update BurnOutSharp 2019-10-25 20:37:08 -07:00
Matt Nadareski
c7ebe69b05 Update BurnOutSharp 2019-10-24 13:25:28 -07:00
Matt Nadareski
22ffda4b5a Fix copy protection scan by preferring 32-bit 2019-10-19 02:42:01 -07:00
Matt Nadareski
b89c051d7d DICUI 1.14 Release 2019-10-01 11:57:15 -07:00
Matt Nadareski
8b41f03472 Update to new DIC version (#167)
* Remove `.` handling code since it should be in DIC now

* Add support for 3 new flags

* Fix dot tests

* Fix PS4 autodetect (Fixes #164)

* Fix PS4 version finding

* Update DIC archive path

* Add support for disk command (unused by default)
2019-10-01 11:53:44 -07:00
Matt Nadareski
700ff50eef Upgrades, people! (#166)
* First step: 4.6.2

* Make Check and Library .NET Core 3.0 compatible

* Enable 4.6.2 and 4.7.2 on DICUI

* DICUI .NET Core 3.0

* Solution items

* New Appeyor paths, environment

* Upgrade DICUI.Test for all frameworks too

* Fix .NET Core 3.0 difference in path handling
2019-10-01 11:36:01 -07:00
Matt Nadareski
95ac7236ff Fix PS4 version finding 2019-09-29 22:23:45 -07:00
Matt Nadareski
755f1b8e95 Root directories are weird (Fixes #163) 2019-09-29 21:28:20 -07:00
Matt Nadareski
d431a4c87f Fix PS4 autodetect (Fixes #164) 2019-09-29 21:18:02 -07:00
Matt Nadareski
04348d2d58 PSX.EXE is a valid filename to find 2019-09-17 20:59:10 -07:00
Matt Nadareski
6f5214c1a4 Even More Info (#161)
* Add (currently) hidden setting for disc info

* Show setting in Options menu

* Fix new window

* New place for combo box items

* Fixes to outputs

* Add category, fix a few things

* Actually use the custom converter

* Allow tabs in ringcode
2019-09-17 20:50:44 -07:00
Hennadiy Brych
1fe12be0b5 PSX date fixes (#157)
* Sony PlayStation date extraction fixed, 3 separate issues:
1. 190x year instead of 200x
2. Failure to get date for some titles where SYSTEM.CNF BOOT record doesn't have backslash after "cdrom:"
3. Date shift by one day due to Utc offset applied
Relaxed directory path requirements to allow dot and ampersand. DiscImageCreator had to be changed in order to support this, will be submitted separately.

* Reverted extra path characters modification.

* Reverted to UTC time
2019-08-28 00:02:13 -07:00
Matt Nadareski
9c9ca16366 Persist paths unless changing drive letters (fixes #154) 2019-08-12 14:08:55 -07:00
Matt Nadareski
1702db71d1 Add new Redump regions (GC; Uk,Au; U,Ca) 2019-08-05 11:07:51 -07:00
Matt Nadareski
0ba7ccdb9a HTML decode (fixes #150) 2019-07-30 00:19:02 -07:00
Matt Nadareski
3ccf65190e Migrate to archive.org 2019-07-18 11:26:17 -07:00
Matt Nadareski
6b9ee3d5ed Update AppVeyor config 2019-07-18 11:20:09 -07:00
Matt Nadareski
3ee0355034 Add speculative values for VideoNow dumping 2019-07-18 10:42:23 -07:00
Matt Nadareski
7074fa1e2c Update to newest DIC, create new release 2019-07-01 20:50:38 -07:00
Matt Nadareski
44b91a6611 Add initial type dectection, hopefully fixes #145 2019-06-17 14:36:54 -07:00
Matt Nadareski
b2185dbed9 Better ways of getting settings (defaults, existence, etc) 2019-05-26 22:30:06 -07:00
Matt Nadareski
ec4b69b6e6 Multiline outputs should lack prefix (force DAT multiline) 2019-05-26 00:57:06 -07:00
Matt Nadareski
b6db873a00 Settings and more (fixes #141) 2019-05-26 00:48:26 -07:00
Matt Nadareski
db9222b737 Accidential layerbreak infos 2019-05-24 14:26:11 -07:00
Matt Nadareski
b7bfa3ba28 Readonly Regex, Rather 2019-05-24 11:18:49 -07:00
Matt Nadareski
ab25687119 Better UMD support 2019-05-24 11:17:59 -07:00
Matt Nadareski
9bfa0a01ad I clearly remember how Remove works 2019-05-23 22:00:36 -07:00
Matt Nadareski
19c99fb7fe I'm having fun 2019-05-22 22:06:43 -07:00
Matt Nadareski
913244459e Oops, forgot a couple 2019-05-22 21:49:55 -07:00
Matt Nadareski
c66df99e22 Add a few more fun things that might get UI features later 2019-05-22 21:33:31 -07:00
Matt Nadareski
03f4a6e2e4 csproj cleanup 2019-05-22 16:13:47 -07:00
Matt Nadareski
31a243eeb4 Update changelist (been too long) 2019-05-22 16:07:15 -07:00
Matt Nadareski
043c0eff1c BluRay has a default layerbreak and known sizes 2019-05-22 15:49:15 -07:00
Matt Nadareski
ff44313e13 Buffs for potential future work 2019-05-21 00:22:16 -07:00
Matt Nadareski
e06ed31b21 Round 2, fight 2019-05-21 00:07:24 -07:00
Matt Nadareski
aeaec9e805 Add currently unused dump status 2019-05-20 23:39:37 -07:00
Matt Nadareski
6df0c76939 Stupid default namespace 2019-05-20 23:35:04 -07:00
Matt Nadareski
72efffcec4 Cleanup brigade 2019-05-20 22:14:53 -07:00
Matt Nadareski
930e4f7514 Only allow trailing spaces if there's no trailing directory marker 2019-05-20 15:08:41 -07:00
Matt Nadareski
85f71032b5 Fix trailing space handling for output directory 2019-05-20 15:00:41 -07:00
Matt Nadareski
ead530e2ec Code prettification 2019-05-20 00:12:06 -07:00
Matt Nadareski
dc14b1ea52 Both successes, not overwrite 2019-05-19 23:59:16 -07:00
Matt Nadareski
4628be2855 Simplify per-type and per-system code 2019-05-19 23:50:37 -07:00
Matt Nadareski
1f24c08770 Move some logic to SubmissionInfo, fix PS1/PS2/PS4 2019-05-19 22:13:57 -07:00
Matt Nadareski
4889b9d0bf Fix EXE parsing for PS1/PS2 discs 2019-05-14 21:05:05 -07:00
Matt Nadareski
b3d7422a66 Minor cleanup of fields and comments 2019-05-14 20:54:08 -07:00
Matt Nadareski
a0000528de Build date as string again 2019-05-14 20:50:17 -07:00
Matt Nadareski
2be46ec7a1 Trim fields that may have trailing whitespace 2019-05-14 01:56:39 -07:00
Matt Nadareski
f2510a08a4 Barcode, another low-hanging fruit 2019-05-14 01:51:48 -07:00
Matt Nadareski
2787377250 Add ability to get matching IDs from Redump 2019-05-14 01:46:10 -07:00
Matt Nadareski
e61ca31a3f Remove unused Forms version 2019-05-13 23:51:00 -07:00
Matt Nadareski
1b735b5d06 Migrate to SubmissionInfo object, add JSON output 2019-05-13 21:55:08 -07:00
Matt Nadareski
ad3d26cdea Better naming of blank-labeled discs 2019-05-12 21:18:07 -07:00
Matt Nadareski
f372ec4ccd Add new system, better submissioninfo, future 2019-05-11 22:32:22 -07:00
Matt Nadareski
44e75a50d7 Mass add of unused systems 2019-05-06 12:29:19 -07:00
Matt Nadareski
6f0ea2de3e Formats cleanup before adding more 2019-05-06 10:52:07 -07:00
Matt Nadareski
b4f0f7f4b3 Ensure all GD-ROM systems have LD optinos as well 2019-05-05 23:57:11 -07:00
Matt Nadareski
5027d67731 Add new VideoNow systems, cleanup 2019-04-27 17:11:39 -07:00
Matt Nadareski
6c8016345a Use full file path in Check (to correct relative paths) 2019-04-20 22:03:23 -07:00
Matt Nadareski
7ba04e63c7 Support JagCD (may need /ms still) 2019-04-11 21:12:36 -07:00
Matt Nadareski
77dc1d0029 Fix X360 DMI offset 2019-04-04 14:33:26 -07:00
Matt Nadareski
2b6538707f Add DMI extraction for XBOX/369 (fixes #139) 2019-04-04 01:09:51 -07:00
Matt Nadareski
902e4e5715 Make DICUI.Check better 2019-04-04 00:37:17 -07:00
Matt Nadareski
28f6d50e5a Better error checking; more DICUI.Check updates 2019-03-30 21:44:57 -07:00
Matt Nadareski
d8ed7d6ad7 Fix CD dumping with newest release; DICUI.Check minor update 2019-03-30 21:32:10 -07:00
Matt Nadareski
078d7d0ea3 Fix failing test 2019-03-27 17:37:03 -07:00
Matt Nadareski
5895a66c7a Update to DIC 20190326 2019-03-27 17:32:31 -07:00
Matt Nadareski
5ccff836e2 Add DVD for Panasonic M2 (fixes #138) 2019-02-26 11:57:12 -08:00
Matt Nadareski
b2c2e8c4d9 Reverse mould SID fields order (fixes #137) 2019-02-26 11:55:14 -08:00
Matt Nadareski
890959cfe0 Add DICUI.Check, standalone output parsing tool 2019-02-10 14:47:53 -08:00
Matt Nadareski
8729c7f20c Add new template to CD/GD as well 2019-01-28 21:15:07 -08:00
Matt Nadareski
74826909f4 Bump version to 1.12 2019-01-27 22:25:53 -08:00
Matt Nadareski
eb4904afb4 Add support for SCD/MCD headers (fixes #133) 2019-01-27 21:42:03 -08:00
Matt Nadareski
06975316b9 Add Panasonic M2 and Hasbro VideoNow to supported discs 2019-01-27 21:31:05 -08:00
Matt Nadareski
e0be24e54f Update links after organizational shift 2019-01-27 20:45:10 -08:00
Matt Nadareski
eb3cbc44a7 Add missing PVD text (fixes #128) 2019-01-27 16:30:15 -08:00
Matt Nadareski
d6f39bfd4c Ped-ant-ic (fixes #132) 2019-01-27 16:27:59 -08:00
Matt Nadareski
576fa07a2c Fix dumping status in Winforms 2018-11-07 20:27:35 -08:00
Matt Nadareski
ae2b26f84e Capitalization that's been bothering me 2018-11-05 20:30:39 -08:00
Matt Nadareski
1d9a2c9be7 Make sure that detected disc type is shown; cleanup 2018-11-05 20:27:01 -08:00
Matt Nadareski
538c0bba09 Fix output of the media type box in Winforms 2018-11-05 15:30:45 -08:00
Matt Nadareski
57c31cad48 Update AppVeyor build number 2018-11-05 14:51:14 -08:00
Matt Nadareski
093db36f44 Make Floppy Disk a supported format again 2018-11-05 14:51:00 -08:00
Matt Nadareski
fd4db3f8f0 Make SCM optional for Audio-CD (fixes #127) 2018-11-05 14:48:25 -08:00
Matt Nadareski
856a3161be Fix missing file extension 2018-10-22 11:09:28 -07:00
Matt Nadareski
dfafe9789a Update AppVeyor for new DIC version 2018-10-22 11:06:25 -07:00
Matt Nadareski
9f750d01c4 Correct submissionInfo output order (fixes #125) 2018-10-22 10:44:29 -07:00
Matt Nadareski
705ee82cbb Audio CDs don't have the _EdcEcc file right now 2018-10-22 10:41:10 -07:00
Matt Nadareski
d1f80efa41 Add DIC merge command and skip sector flag 2018-10-22 10:32:48 -07:00
Matt Nadareski
f88e6617e5 Fix some of the descriptions in submissionInfo (partial #115) 2018-10-15 17:22:42 -07:00
Matt Nadareski
1401422b08 Add DVD for PS3 (fixes #124) 2018-10-15 17:19:48 -07:00
Matt Nadareski
4539066af0 Update version numbers for 1.11 2018-10-08 12:53:59 -07:00
Matt Nadareski
ec23502275 Abstract out non-UI code to separate DLL (#122)
* Abstract out non-UI code to separate DLL

This change seems rather large, but it's mostly just moving anything that is not directly driving the UI to its own, separate library. This will make it easier at a later date for any UI improvements or changes as well as making the code much more discrete. One TODO has been added as a result of this change.

* Remove MessageBox from library

This change removes the last UI-driven elements from the library. This now makes the library (in theory) UI-agnostic and removes the TODO.

* Options is more UI-related

* Fix Nuget references in csproj

* Add Winforms UI

This is a clone of the current WPF UI but implemented in Windows Forms. WPF is not currently supported by Mono for Linux and macOS, so this can provide an alternative to those users who want to run on those systems instead. This also adds a second artifact for the Winforms build.
2018-10-08 11:06:22 -07:00
Matt Nadareski
331f875e7f Add better progress logging for dumps (fixes #121) 2018-09-24 12:17:57 -07:00
Matt Nadareski
e68f88062f Fix mundane test 2018-09-20 22:39:07 -07:00
Matt Nadareski
d1bb8184d1 Path correction 2018-09-20 22:18:07 -07:00
Matt Nadareski
b7b3f36402 Fix build 2018-09-20 21:30:13 -07:00
Matt Nadareski
859af8efea Format wars 2018-09-20 21:25:22 -07:00
Matt Nadareski
8594d7884c Update terminology (fixes #119) 2018-09-19 20:50:57 -07:00
Matt Nadareski
946dc929f1 Add profile for low-density part of GDROM
Currently, it's easier to just say "it's a CD" and call it a day
2018-09-19 20:49:33 -07:00
Matt Nadareski
1e5b1a205e Update DIC version for AppVeyor 2018-09-19 20:42:49 -07:00
Matt Nadareski
3271cd0ea3 Fix Saturn output info (fixes #118) 2018-09-10 18:27:13 -07:00
Matt Nadareski
647bab8cd3 Merge branch 'master' of https://github.com/reignstumble/DICUI 2018-09-10 16:44:49 -07:00
Matt Nadareski
89b2438dac Threefold fixes
1) Fix the issue where custom parameters that include the `/s 2` flag crash the program
2) Make editing custom parameters a little more intutive (still need to figure out either a way in-documentation or in-UI to explain the checkbox a little better). This part of the change needs to be more tested but there aren't many people using the current feature anyway.
3) Fix the random "Error count: -1" issue that some users were seeing (ended up being a result of more things being in the `_mainError.txt` file than originally anticipated)
2018-09-10 16:43:25 -07:00
Whovian9369
1dcd36d640 Change printable string misspellings relating to DIC (#117)
* Update DumpEnvironment.cs

Change Disk to Disc in printable strings of DumpEnvironment.cs

* Update OptionsWindow.xaml

Change Disk to Disc in printable strings of OptionsWindow.xaml
2018-09-03 13:16:03 -07:00
Matt Nadareski
2f63ebcd29 Fix unprotected DVD info; hopefully fix error count parsing 2018-09-03 00:38:52 -07:00
Matt Nadareski
316953f38c Add UmdImageCreator output parsing 2018-09-02 22:15:40 -07:00
Matt Nadareski
62813ac701 Fix tests 2018-09-01 18:03:48 -07:00
Matt Nadareski
22a936e60b XBOX CDs are not XISO formatted 2018-09-01 17:57:26 -07:00
Matt Nadareski
6412205fa3 Unknown sector ranges aren't recorded 2018-09-01 17:43:38 -07:00
Matt Nadareski
75c18ea3c4 Update for even more DIC changes 2018-09-01 17:02:09 -07:00
Matt Nadareski
6bdb93208e Add DVD-Video protection info 2018-08-29 14:40:35 -07:00
Matt Nadareski
588ed19692 Update README.md
The link for AppVeyor now goes directly to the artifacts page for easier downloading. Hopefully this reduces the issues that are seen with new users.
2018-08-28 21:44:07 -07:00
Matt Nadareski
d56ab5ab37 Update to new version of DIC in AppVeyor 2018-08-28 21:42:01 -07:00
Matt Nadareski
819d18112e My fault, this try/catch doesn't need to be here 2018-08-28 21:29:24 -07:00
Matt Nadareski
2e79cbb0db Fix XBOX/X360 outputs 2018-08-28 21:01:52 -07:00
Matt Nadareski
f024611de8 Take advantage of new XGD3 support in DIC (fixes #71) 2018-08-28 17:31:45 -07:00
Matt Nadareski
48e6283c37 Add PS4 PIC extraction (fixes #106) 2018-08-19 01:25:52 -07:00
Matt Nadareski
fec70f17bc ...wrong path 2018-08-19 00:50:58 -07:00
Matt Nadareski
257f7ab7df Last fix: subfoldering 2018-08-19 00:48:18 -07:00
Matt Nadareski
c1ed3aca02 Path is important 2018-08-19 00:43:32 -07:00
Matt Nadareski
ace6a78236 Attempt to fix AppVeyor 2018-08-19 00:41:34 -07:00
Matt Nadareski
f1e9f1d433 Add PS4 version finding, minor fixes (fixes #105) 2018-08-19 00:19:57 -07:00
Matt Nadareski
e83190c091 Writeback for updated path values (#111) 2018-08-17 20:22:08 -07:00
Matt Nadareski
82eabc12ca Try to fix output path checks (fixes #111) 2018-08-17 20:16:13 -07:00
Matt Nadareski
76b549d773 Add try/catch around dump (fixes #110) 2018-08-17 20:08:50 -07:00
Matt Nadareski
227688d7dc Fix PS2 VER spacing (fixes #112) 2018-08-17 19:24:55 -07:00
Matt Nadareski
4c9e2359af Update system info 2018-08-14 21:59:41 -07:00
Matt Nadareski
d179d7f464 Recognize new swap commands 2018-08-14 21:54:41 -07:00
Matt Nadareski
fff19e75fd Fix XBOX security sector format 2018-08-02 12:38:29 -07:00
Matt Nadareski
2175ccfb26 Update CHANGELIST 2018-07-29 12:44:46 -07:00
Matt Nadareski
621923bdba Add new Redump-recongized system 2018-07-24 19:27:45 -07:00
Matt Nadareski
3c216aa309 More accurate detection 2018-07-21 17:02:51 -07:00
Matt Nadareski
d85186e5cd Fix copy protection output again
This doesn't warrant a full PR
2018-07-19 23:40:17 -07:00
Matt Nadareski
1a94ad3d23 Don't try to get fancy 2018-07-19 15:34:16 -07:00
Matt Nadareski
91776d939c AppVeyor and Test (#103)
* Fixes to AppVeyor

* Fix tests
2018-07-19 15:31:25 -07:00
Matt Nadareski
3d058e633f Unblock and Update (#102)
* RAINBOW

* Add to FormatOutputData

* Update BurnOutSharp

* Fix parameter creation

* Minor improvements

* Update package versions
2018-07-19 14:11:58 -07:00
Matt Nadareski
94c0564fe8 Goodbye drive speed finders (#101)
* Goodbye drive speed finders

* More accurate scaling

* Ensure disposal
2018-07-17 16:42:38 -07:00
Matt Nadareski
87ecc7588e Update BurnOutSharp 2018-07-17 10:41:29 -07:00
Matt Nadareski
8a5c114a7e External and Fix (#100)
* Progress report updates log

* Logging additions

* Minor update

* Set halfway point only if nothing can be found

* Better population metrics

* QOL improvements

* More tiny logging updates

* Autoscroll

* Be consistent

* Update packages, use submodules

* Use NuGet

* Not 100% sure why I did it that way...

* Update README.md

* Make copy protect scan prompt an option

* Enhanced-CD might be Win/Mac

* Add unconfirmed formats

* Enhance!
2018-07-17 10:21:37 -07:00
ReignStumble
e307cb217f Fix DPI issue 2018-07-16 22:41:05 -04:00
Matt Nadareski
b8c56bea58 Whole Cleanup (#97)
* Cleanup TODOs

* Remove more TODOs

* Add case-insensitive dictionary for protection scans

* Namespace cleanups

* Space

* Minor fixes

* Oops

* Change layerbreak detect value
2018-07-15 22:26:22 -07:00
Matt Nadareski
564a6af9b8 Protection, Progress, and (Possibly) Empty Drives (#93)
* Include but mark inactive drives (fixes #87)

* Add more checks for the active flag

* Force users to choose on dump

* Fix quotes

* Update SolidShield 1

* Select first active by default

* Add TODO

* Add dumping progress indicator

* Clamp runtime

* Add first textfile scans; EA CdKey

* Slightly better executable detection

* Better file type detection

* Get as much into magic number checks as possible

* Nicer formatting

* Trailing slash

* Add back for benefit of the doubt

* Remove dead code

* Safer cabinet scanning

* Remove iXcomp; Add Unshield

* Fix build, add more comments

* Post-merge cleanup

* Add a new log line

* Make protection scan mechanics easier to see

* Add a space
2018-07-13 16:40:40 -07:00
Jacopo Santoni
5bcde889b3 Log Window enhancements (#95)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* created LogWindow and first tests of output gathering

* integrated all the proof of concept related to stream fetching of DIC output into LogWindow

* working on LogWindow\ncreated more efficient output matching management, improved handling of process termination

* created Tasks file\nmoved some methods there, created DumpEnvironment to manage are arguments

* moved additional methods to Tasks to make it more modular

* changed StartDumping flow to avoid returning if extra tools are not found

* moved main dump workflow into Tasks class

* created specific DumpResult class for better error reporting/management

* fixes

* fixes Dispatcher thread issue with progress bar

* continued refactor
- split EnsureDiscInformation into an additional EnsureCorrectInformationForSystemAndMediaType
- proof of concept of using custom extensions to enum types to give better functionality (and encapsulation)
- changed cmb_MediaType to keep a List<MediaType?> and got rid of Tuple<string, MediaType?>

* restored GetDiscType functionality

* fixed btn_StartStop enabled on EnsureCorrect... error

* fixed whitespace

* fixed indentation

* merged conflicts

* moved log window

* working on LogWindow resize/location behavior

* working on message logging in LogWindow

* added working integration between MenuItem for LogWindow and its visibility status, fixes issues with positioning and visibility, correctly bound Verbose flag to property

* finished integrating LogWindow, added some verbose logs

* added logs

* fixed include order

* added option to show log window at startup, removed useless QuietMode property from MainWindow

* added precise calculation of MainWindow position when LogWindow is shown and fixed topmost cheat
2018-07-13 16:38:50 -07:00
Jacopo Santoni
b39c96cdd2 Log Window (#94)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* created LogWindow and first tests of output gathering

* integrated all the proof of concept related to stream fetching of DIC output into LogWindow

* working on LogWindow\ncreated more efficient output matching management, improved handling of process termination

* created Tasks file\nmoved some methods there, created DumpEnvironment to manage are arguments

* moved additional methods to Tasks to make it more modular

* changed StartDumping flow to avoid returning if extra tools are not found

* moved main dump workflow into Tasks class

* created specific DumpResult class for better error reporting/management

* fixes

* fixes Dispatcher thread issue with progress bar

* continued refactor
- split EnsureDiscInformation into an additional EnsureCorrectInformationForSystemAndMediaType
- proof of concept of using custom extensions to enum types to give better functionality (and encapsulation)
- changed cmb_MediaType to keep a List<MediaType?> and got rid of Tuple<string, MediaType?>

* restored GetDiscType functionality

* fixed btn_StartStop enabled on EnsureCorrect... error

* fixed whitespace

* fixed indentation

* merged conflicts

* moved log window

* working on LogWindow resize/location behavior

* working on message logging in LogWindow

* added working integration between MenuItem for LogWindow and its visibility status, fixes issues with positioning and visibility, correctly bound Verbose flag to property

* finished integrating LogWindow, added some verbose logs

* added logs

* fixed include order

* added option to show log window at startup, removed useless QuietMode property from MainWindow
2018-07-13 15:48:35 -07:00
Matt Nadareski
2cc73a30f5 Protection Is Key (#92)
* Fix some protections, start adding IS scanning

* Fix IS CAB

* Update TODO
2018-07-10 10:23:52 -07:00
Matt Nadareski
be7d745d10 Create Parameters Class (#91)
* Update commented code

* Add Parameters class

This class is currently unused, but represents a set of parameters that can be passed to and from any given method. It has some copied/modified code from Validators.cs, for the time being due to the current overlap.

* Add better documentation

* Add and use DICFlag enumeration

* Port more things to Parameter-specific version

* Add commented code for later

* Added new options (#90)

* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* added 4 new options: quiet mode, paranoid mode, disable media type detect and c2 reread amount

* added default C2 reread tries to config

* fixed issues for PR

* removed commented leftover

* Update commented code

* Add Parameters class

This class is currently unused, but represents a set of parameters that can be passed to and from any given method. It has some copied/modified code from Validators.cs, for the time being due to the current overlap.

* Add better documentation

* Add and use DICFlag enumeration

* Port more things to Parameter-specific version

* Add commented code for later

* Use Parameters object

* Fix dumping process

* Cleanup Validators
2018-07-09 21:42:02 -07:00
Jacopo Santoni
d51adccab4 Added new options (#90)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* added 4 new options: quiet mode, paranoid mode, disable media type detect and c2 reread amount

* added default C2 reread tries to config

* fixed issues for PR

* removed commented leftover
2018-07-09 16:35:46 -07:00
Matt Nadareski
6141ce8539 Remove Disable Beep; Handle Error Case (#89)
* Disable beep not supported for these

* Remove testing code

This more or less ensures that the current run finishes properly

* Extra fixing for process issues

This, with the last 2 commits, make it so that geting disc speed works again. This was only found because in testing, many MANY instances of DiscImageCreator were made but never killed, polluting the process list. These issues should now be fixed.
2018-07-07 01:00:27 -07:00
Matt Nadareski
96d6e8bd2f Bits and Bobs (#88)
* Finally overhaul UI element naming

* Re-enable disk scan button

* Add cabfile scanning

* Fix csproj

* Fix TODO

* Add a couple more protections, disable one for now

* Fix SecuROM 7 detection

Thanks antimatter for helping test

* No beep for eject and speed check

* Fix Options after renames

* Ensure disc information in  couple places again
2018-07-07 00:08:22 -07:00
Matt Nadareski
c034df4266 More and more cleanup (#86)
* Namespace cleanups

* Make dump validation instanced

* Add note to Tasks

* Move stuff to DumpEnvironment

Most of the stuff in Tasks.cs acted on a single input parameter, namely a DumpEnvironment. Since that's the case, it was more logical to wrap those into DumpEnvironment and make them instance methods rather than keep them static. Due to this change, quite a few methods changed access, and some had to be marked internal due to wanting them to be tested separately.

* Gut Tasks

* One less thing in MainWindow

* Remove explicit cast

* Wrong check

* Create helper method

* Disable scan button on dump

* Remove unnecessary getters/setters

* Method name/description cleanup

* Address TODO

* Namespace cleanups

* Make dump validation instanced

* Add note to Tasks

* Move stuff to DumpEnvironment

Most of the stuff in Tasks.cs acted on a single input parameter, namely a DumpEnvironment. Since that's the case, it was more logical to wrap those into DumpEnvironment and make them instance methods rather than keep them static. Due to this change, quite a few methods changed access, and some had to be marked internal due to wanting them to be tested separately.

* Gut Tasks

* One less thing in MainWindow

* Remove explicit cast

* Wrong check

* Create helper method

* Disable scan button on dump

* Remove unnecessary getters/setters

* Method name/description cleanup

* Address TODO

* Clean up OnContentRendered

* Namespace cleanups

* Make dump validation instanced

* Add note to Tasks

* Move stuff to DumpEnvironment

Most of the stuff in Tasks.cs acted on a single input parameter, namely a DumpEnvironment. Since that's the case, it was more logical to wrap those into DumpEnvironment and make them instance methods rather than keep them static. Due to this change, quite a few methods changed access, and some had to be marked internal due to wanting them to be tested separately.

* Gut Tasks

* One less thing in MainWindow

* Remove explicit cast

* Wrong check

* Create helper method

* Disable scan button on dump

* Remove unnecessary getters/setters

* Method name/description cleanup

* Address TODO

* Clean up OnContentRendered

* Update event handlers
2018-07-05 21:34:04 -07:00
ReignStumble
fe04c6df73 Centered Window + No more resizable 2018-07-06 00:11:11 -04:00
ReignStumble
aff195eb71 Added Exit and About 2018-07-06 00:08:20 -04:00
ReignStumble
3aca999b63 New Menubar 2018-07-06 00:00:16 -04:00
ReignStumble
0e7ce76f01 Wait for UI to render before Populating 2018-07-05 23:43:01 -04:00
Matt Nadareski
6d72417512 Cleanups and mixups (#85)
Cleanups and Mixups
2018-07-05 16:52:34 -07:00
Matt Nadareski
ac89f06843 Separate out from Constants.cs 2018-07-05 13:33:08 -07:00
Matt Nadareski
39ce56d579 Post-merge cleanup 2018-07-05 13:18:01 -07:00
Matt Nadareski
9dbd30adba Merge DumpEnvironment/DumpInformation; Add Copy Protection Scan (#82)
* Merge DumpEnvironment and DumpInformation

* None of these need to be static

* Start adding protection checks

* Convert all BurnOut-related files from VB

* Placeholders for SafeDisc 3 and 4

* Fix some small things

* SubIntention not always output

* Make copy protect scan optional

* Try/catch on disc information to avoid crashes

* Add placeholders for Origin, Steam, UPlay

* Don't use copied method, for safety

* Protection cleanup

* More cleanup on SafeDisc v1/2

* Add area for cabfiles (for later); clean up more protections

* Comment out cabfile path, for now

* Underscores? Really?

* Namespace cleanup

* More Steam variations

* Add an actual UPlay installer name

* Skeleton for GFWL

* Add one for Origin

* More Steam, update note

* Add note kinda like GFWL

* Fix Origin installer name (IDK what I put there before...)

* Remove TODOs for ones with valid checks

* Add first GFWL installer name

* Move online services to last

Physical disc protections should ALWAYS come before an online service is mentioned. This makes sure that the physical media's information is not lost

* Add another TODO

* ...slightly longer comment

* Fix CDCops, start adding file lists

* More paths

* Even more paths

* Last paths for now

* Dictionary is smarter... so more work ahead

* Remind me why I'm doing this?

* Okay, recreated in dictionary now

* Reorganiation

* Alphabetization is fun

* Add unused call to new scan path

* Add TODO

* Remove TODO

* Remove TODO, comment out dummy files check

* Better state for no detected protection

* Make copy protect scan a Task so it can be used elsehwere later

* ScanEx -> Scan; cleanup

* Remove unused protection scan methods

These methods used to be a simple check for if a particular file existed. These are now captured in the "CreateProtectionMapping" dictionary

* Remove SuffixInStr

* Characters to literals, decimal to hex, start of consolidation

* Add remaining string-only mappings, cleanup

* Update summaries, remove unused flag

* Trim out unnecessary variables

These variables used to be used when a DLL or an EXE was found to have something in it but other files were needed to check against for better output. This also assumed a monolithic output in what protections were found. Now that we just list everything and all files are scanned, this should catch everything that comes through

* Remove unused path-based protections

* Accidentally wiped out Sysiphus

* CD Check is... special

* No more internal vars; static shock

* Add region tags

* Remove unused Dummy Files check

* Add scan for copy protection without dump

* Better checking of files

* Better message box

* Add TODO

* Clearer language for SafeDisc

* Fix SafeDisc scanning

Thanks to eientei95 for helping find this one

* Slight EVORE cleanup

* Fix SolidShield

Thanks to eientei95 for helping to fix this one

* Add sector scan note

* Add user-requested TODO

* Refactor of ComboBox underlying types (#84)

* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* Refactored KnownSystem combobox management

- created KnownSystemComboBoxItem to manage both header and system items
- totally rewrote KnownSystem category (through KnownSystemCategory enum and markers)
- fixed null access in EnsureDiscInformation caused by null _drives
- rewrote cmb_SystemType management to use new classes

* - created Drive class to keep drive letters, volume label and is floppy flag altogether
- changed all the code to use the new Drive class in combobox and in DumpEnvironment

* fixed retrieval of value from cmb_KnownSystem combobox

* removed OrderedDictionary, not needed anymore

* Merge DumpEnvironment and DumpInformation

* None of these need to be static

* Start adding protection checks

* Convert all BurnOut-related files from VB

* Placeholders for SafeDisc 3 and 4

* Fix some small things

* SubIntention not always output

* Make copy protect scan optional

* Try/catch on disc information to avoid crashes

* Add placeholders for Origin, Steam, UPlay

* Don't use copied method, for safety

* Protection cleanup

* More cleanup on SafeDisc v1/2

* Add area for cabfiles (for later); clean up more protections

* Comment out cabfile path, for now

* Underscores? Really?

* Namespace cleanup

* More Steam variations

* Add an actual UPlay installer name

* Skeleton for GFWL

* Add one for Origin

* More Steam, update note

* Add note kinda like GFWL

* Fix Origin installer name (IDK what I put there before...)

* Remove TODOs for ones with valid checks

* Add first GFWL installer name

* Move online services to last

Physical disc protections should ALWAYS come before an online service is mentioned. This makes sure that the physical media's information is not lost

* Add another TODO

* ...slightly longer comment

* Fix CDCops, start adding file lists

* More paths

* Even more paths

* Last paths for now

* Dictionary is smarter... so more work ahead

* Remind me why I'm doing this?

* Okay, recreated in dictionary now

* Reorganiation

* Alphabetization is fun

* Add unused call to new scan path

* Add TODO

* Remove TODO

* Remove TODO, comment out dummy files check

* Better state for no detected protection

* Make copy protect scan a Task so it can be used elsehwere later

* ScanEx -> Scan; cleanup

* Remove unused protection scan methods

These methods used to be a simple check for if a particular file existed. These are now captured in the "CreateProtectionMapping" dictionary

* Remove SuffixInStr

* Characters to literals, decimal to hex, start of consolidation

* Add remaining string-only mappings, cleanup

* Update summaries, remove unused flag

* Trim out unnecessary variables

These variables used to be used when a DLL or an EXE was found to have something in it but other files were needed to check against for better output. This also assumed a monolithic output in what protections were found. Now that we just list everything and all files are scanned, this should catch everything that comes through

* Remove unused path-based protections

* Accidentally wiped out Sysiphus

* CD Check is... special

* No more internal vars; static shock

* Add region tags

* Remove unused Dummy Files check

* Add scan for copy protection without dump

* Better checking of files

* Better message box

* Add TODO

* Clearer language for SafeDisc

* Fix SafeDisc scanning

Thanks to eientei95 for helping find this one

* Slight EVORE cleanup

* Fix SolidShield

Thanks to eientei95 for helping to fix this one

* Add sector scan note

* Add user-requested TODO
2018-07-05 12:58:20 -07:00
Jacopo Santoni
48de63513e Refactor of ComboBox underlying types (#84)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* Refactored KnownSystem combobox management

- created KnownSystemComboBoxItem to manage both header and system items
- totally rewrote KnownSystem category (through KnownSystemCategory enum and markers)
- fixed null access in EnsureDiscInformation caused by null _drives
- rewrote cmb_SystemType management to use new classes

* - created Drive class to keep drive letters, volume label and is floppy flag altogether
- changed all the code to use the new Drive class in combobox and in DumpEnvironment

* fixed retrieval of value from cmb_KnownSystem combobox

* removed OrderedDictionary, not needed anymore
2018-07-05 11:30:52 -07:00
Matt Nadareski
56eaf7c2c5 Add Test Project (#79)
* Add Test project; Migrate so there's not an external folder

* Add Test project; Migrate so that there's not an external folder

* Fix build; Add remaining test classes (skeletons)

* Fix more paths

* Make unit tests runnable, fix issue found by unit tests

* Add more tests, fix more things found by tests

* Add skeleton for ValidatorsTest, fix OrderedDictionary

* Add UIElementsTest

* Add new test classes, slightly update Result

* Implement DumpEnvirionment tests; fix minor things from testing

* Add Tasks testing skeletons; Reorder methods and change access modifiers for some

* Update notes for DumpInformation tests

* Implement a couple Validators tests; fix ValidateParameters

* Implement DetermineFlagsTest

* Make one test more readable

* More cleanup around ValidateParameters

* Split check

* WiiU is not supported by /raw command

* Add TODO question

* Update TODO with a plan
2018-06-28 19:41:18 -07:00
Matt Nadareski
c1bd30e008 Streamline Callbacks and Ensure MediaType (#78)
* Separate drive speeds; better ensuring of media type

This is the first attempt at separating out the drive speeds into separate categories. At the moment, it seems to be working just fine for CD and DVD.
This also includes some updated code to better ensure that the detected (or selected if the user has changed it) media type is kept between all of the regular changes

* Multiple medium

* Add more drive speed prototyping

* Strip out redundant calls

* Separate drive speeds; better ensuring of media type

This is the first attempt at separating out the drive speeds into separate categories. At the moment, it seems to be working just fine for CD and DVD.
This also includes some updated code to better ensure that the detected (or selected if the user has changed it) media type is kept between all of the regular changes

* Multiple medium

* Add more drive speed prototyping

* Strip out redundant calls

* Streamline merge

* Comment patrol; keep BluRay a first-class citizen

* HD-DVD is supported?!
2018-06-27 23:21:52 -07:00
Jacopo Santoni
4176f22d79 Added preferred dump speed option (#77)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* added max dump speed option
- added slider element into OptionsWindow
- moved drive speeds to UIElements from MainWindow
- added setting to Options which is saved

still need to understand how to manage DVD/CD-ROM different speeds

* kept working on max dump speed settings

- rewrote max speed list management in Options
- added second slider in OptionsWindow
- added callback from OptionsWindow to MainWindow on updated options

* final tweaks before PR

* renamed maxDumpSpeed to preferredDumpSpeed

* restored SetCurrentDiscType functionality

* fixes for PR

* fixes for PR
2018-06-27 20:06:19 -07:00
Matt Nadareski
1e16e952cf Update for 1.07 2018-06-27 10:35:40 -07:00
Matt Nadareski
9f493ae27a One last fix for drive speed finding
DIC can sometimes report that a drive has a 0x speed, causing this automatic finder to fail in those cases
2018-06-27 10:35:29 -07:00
Matt Nadareski
2021230836 Quick fix to account for possibly odd speeds 2018-06-27 00:21:53 -07:00
Matt Nadareski
12ba5702fd Minor updates
This mostly takes care of adding a ton of comments and descriptions. Other minor changes include:
- Removing last remnants of psxt001z
- Capitalizing public variables
- Fixing the build that I stupidly broke
- Ensuring whitespace-named discs are handled
- Simplified DumpEnvironment init
2018-06-26 21:08:26 -07:00
Jacopo Santoni
2cf083eb89 Refactor of dump procedure into smaller components (#76)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* created Tasks file\nmoved some methods there, created DumpEnvironment to manage are arguments

* moved additional methods to Tasks to make it more modular

* changed StartDumping flow to avoid returning if extra tools are not found

* moved main dump workflow into Tasks class

* created specific DumpResult class for better error reporting/management

* fixes

* continued refactor
- split EnsureDiscInformation into an additional EnsureCorrectInformationForSystemAndMediaType
- proof of concept of using custom extensions to enum types to give better functionality (and encapsulation)
- changed cmb_MediaType to keep a List<MediaType?> and got rid of Tuple<string, MediaType?>

* restored GetDiscType functionality

* fixed btn_StartStop enabled on EnsureCorrect... error

* fixed whitespace

* fixed indentation

* fixes for PR
2018-06-26 20:18:37 -07:00
Matt Nadareski
c1f22d47dc Remove Redundant Calls and PSX Automation (#75)
* Work with callbacks to increase perf

* Make EDC field for PSX automatic

* Make AntiMod field for PSX automatic

* Make LibCrypt field for PSX automatic

* Remove psxt001z

Now that we have confirmed that DIC outputs the required information for libcrypt, we no longer need the external call to psxt001z to confirm (output was unused anyway)

* We need the subIntention data for LibCrypt

* Let's avoid null for now

* Set default speed in case of error

* Check the layerbreak better

* Remove extraneous header checking

* Add SubIntention field if it exists, always

* Add LibCrypt flag by default for PSX

* SubIntention needs a newline
2018-06-26 10:35:58 -07:00
Matt Nadareski
11287d081d Add system requirements (fixes #72) 2018-06-25 10:18:06 -07:00
Matt Nadareski
8652af5697 Usability Updates (#73)
* Checkpoint

* Add notes

* Rename Validation -> Validators

* Move data to Data folder

* Get current disk type

* Automatically detect disc type

* Comment out WIP code

* Add more prototype

The hope is that having this unhooked prototype code will get either myself or another contributor the right inspiration to get it going properly
2018-06-25 10:00:06 -07:00
Matt Nadareski
69561cb1a0 Bits and Pieces (#70)
* DiscType -> MediaType

* Fix bulk find/replace

* Add OrderedDictionary

* Stage 1 of moving off of Tuples

* Add CHANGELIST.md

* Stage 2 of Tuple removal

* String replacement for output paths

* Stage 3 of Tuple removal

* Slight reordering
2018-06-21 11:46:14 -07:00
Matt Nadareski
d587d2b4b3 Add system and media type to output submission info (#65)
* Add new constants to the template

* Slight reordering

* Add new fields to the output

* Whitespace!

* Minor formatting cleanup

* Add TODO

* Whitepace, 2.0
2018-06-20 22:00:26 -07:00
Matt Nadareski
a19418e46f Remove sg-raw (#64)
* Remove sg-raw from OptionsWindow.xaml.cs

* Remove sg-raw from OptionsWindow.xaml

* Remove sg-raw from Options.cs

* Remove sg-raw from MainWindow.xaml.cs

* Missed the other rows

* Remove sg-raw from App.config
2018-06-20 12:44:39 -07:00
Jacopo Santoni
de22ead07c Enhancement of options management (#63)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* added OptionsFrame that will manage all applicaton settings and implemented path browse buttons

* removed old properties code, added Options to manage all the program options, implemented interactions with OptionsWindow

* fixed margins automatically inserted by MSVS

* fixed margins automatically inserted by MSVS

* removed empty method placed by XAML designer

* added closed callback for OptionsWindows, tweaks

* fixed button in toolbar
2018-06-20 12:30:31 -07:00
Matt Nadareski
f777869103 Formats and Types (#58)
* Combine single/dual-layer disc types

* Add silly formats

* Minor tweaks to disc type population

* Make codepath slightly less complex

* Fix null error on dump

* Update strings that looked wrong in output
2018-06-20 11:38:12 -07:00
reignstumble
aec1131271 Update README.md 2018-06-20 12:23:01 -04:00
Jacopo Santoni
308fad3ed2 Separated of System and Disc Type (#56)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox
2018-06-18 12:43:07 -07:00
Matt Nadareski
61ce45667b Update for 1.06 2018-06-15 20:27:34 -07:00
Matt Nadareski
ababfdd2ed XBOX/360 Fixes (#50)
* Better creation of parameters (fixes Xbox/360)

* Split X360 into XDG2/3

* Don't enable drive speed if we're not supposed to

* Add first attempt at Xbox info extraction

* Trim or it never matches
2018-06-15 20:24:43 -07:00
Matt Nadareski
9683074197 Only eject when told 2018-06-14 20:18:01 -07:00
Matt Nadareski
ab2bc8f50c Worst typo ever 2018-06-14 20:11:49 -07:00
reignstumble
aa86ddaf47 Fix incorrectly enabled button 2018-06-14 20:40:41 -04:00
Matt Nadareski
68afebace4 Update for 1.05a 2018-06-14 16:37:43 -07:00
Matt Nadareski
bebe3ab8a0 Add specialty checking for PS1, PS2, and Saturn (#47)
* Setup upcoming work

* Add EXE date checking for PS1/PS2

* Add PS2 version checking

* Add Saturn header reading, fix validation again

* Get all Saturn build info
2018-06-14 16:33:40 -07:00
Matt Nadareski
1f314df8c1 Update for 1.05 2018-06-14 14:14:25 -07:00
Matt Nadareski
38c3638f21 Update Commands for new DIC Version (#46)
* Split constants further

* Fix TODOs in the dump information

* Add TODO for sg-raw

* Use DIC flags, add XBOX command

* Add .dat output for floppy disks, add note

* Better custom parameter checking

* Remove postfix from commands, add /74 flag

* Update DIC flag constant names

* Add descripion for Template constants

* Add description for UIElements

* New utilities namespace

* Add XBOX-specific information

* Add more disc-based arcade systems

* Add special check for PSX discs
2018-06-14 14:06:10 -07:00
Matt Nadareski
cde4b671fc Parameter Checking updates (#44)
* Fix custom parameter parsing, rename to just "parameters"

* Consolodate and fix param checking code
2018-06-13 22:15:53 -07:00
Matt Nadareski
578854cd3b Fixes and Floppies (#43)
* Fix button appearance on Settings window

* Add notes for floppy dumping later

* Support floppy disk reading (fixes #12)
2018-06-13 20:14:25 -07:00
Matt Nadareski
1cd7885194 More fixes (#42)
* Make custom parameters react to filename/directory changes

* Add subdump for Saturn (fixes #20)

* Add disc eject, fix minor issue (fixes #17)

* Fix spacing issue
2018-06-13 18:02:45 -07:00
Matt Nadareski
c0de39c229 Miscellaneous Fixes (#41)
* Make error count more accurate

* Extract dump information to separate file

* Remove TODO

* Add subIntention field check to other formats

* Correct Wii and WiiU disc types

* Nuon correction (hard to base on the limited releases)

* Update Sega Lindbergh

* Add two Sega arcade platforms

* Clearer TODO

* Validate "swap" command

* Remove hanging comma from TODO

* Remove TODO
This one I'm removing because technically, the "gd" command is the correct way of dumping via DIC. The "swap" command is a stopgap for drives that only support GD-ROM dumping via hacked TOC discs
2018-06-13 17:07:55 -07:00
Matt Nadareski
a1148f80c8 Updated for 1.04b 2018-06-13 16:11:22 -07:00
Matt Nadareski
632654d00b Fix "Custom Input" not working 2018-06-13 16:09:11 -07:00
Matt Nadareski
a6d6b800a5 Fix order of operations and extra extension 2018-06-13 16:06:18 -07:00
Matt Nadareski
8527cc5746 Add SubIntention (SecuROM) field (#40)
* Add SubIntention (SecuROM) field

* Fix issue with internationalization
2018-06-13 15:56:38 -07:00
Matt Nadareski
f94f54f4d7 Update for 1.04a 2018-06-13 14:55:08 -07:00
Matt Nadareski
2091ef1d92 Add properties dialog (#39) 2018-06-13 14:52:13 -07:00
Matt Nadareski
af865bca9e Fix case where empty tray causes a crash (#38) 2018-06-13 14:49:26 -07:00
Matt Nadareski
23588fa5ae Merge branch 'master' of https://github.com/reignstumble/DICUI 2018-06-13 12:38:25 -07:00
Matt Nadareski
e763fec01e Update README for 1.04 (#37)
Update README for 1.04
2018-06-13 12:27:42 -07:00
Matt Nadareski
732ff2ccca Update README for 1.04 2018-06-13 12:26:57 -07:00
Matt Nadareski
f960aca8a9 Populate drive speed (#35) (#1) (#36)
* Allow child process to be killed (#10)

* Add constants, use them, use child process more

* Try to automatically populate the drive speed
2018-06-13 12:23:35 -07:00
Matt Nadareski
335c1388d4 Merge branch 'master' of https://github.com/mnadareski/DICUI 2018-06-13 12:22:40 -07:00
Matt Nadareski
87c0d5b44b Populate drive speed (#35) (#1)
* Allow child process to be killed (#10)

* Add constants, use them, use child process more

* Try to automatically populate the drive speed
2018-06-13 12:20:24 -07:00
Matt Nadareski
1a44fb5c8a Merge branch 'master' into master 2018-06-13 12:20:14 -07:00
Matt Nadareski
285921cae3 Fix getting speed on changed drive letter 2018-06-13 12:18:06 -07:00
Matt Nadareski
2255dba640 Populate drive speed (#35)
* Allow child process to be killed (#10)

* Add constants, use them, use child process more

* Try to automatically populate the drive speed
2018-06-13 11:57:10 -07:00
Matt Nadareski
99158f31c9 Merge branch 'master' into master 2018-06-13 11:56:30 -07:00
Matt Nadareski
53a2ef227b Try to automatically populate the drive speed 2018-06-13 11:51:21 -07:00
Matt Nadareski
c0efc63741 Allow child process to be killed (#10) (#34)
* Allow child process to be killed (#10)

* Add constants, use them, use child process more
2018-06-13 11:31:19 -07:00
Matt Nadareski
905bd1a7dc Add constants, use them, use child process more 2018-06-13 11:30:33 -07:00
Matt Nadareski
5d49d4833f Allow child process to be killed (#10) 2018-06-13 11:26:40 -07:00
Matt Nadareski
8fd3e2d405 Merge pull request #33 from mnadareski/master
Split constants
2018-06-13 11:11:41 -07:00
Matt Nadareski
1521a918f3 Split constants 2018-06-13 10:39:03 -07:00
Matt Nadareski
5f580ee025 Merge pull request #32 from mnadareski/master
Enable validation of DIC parameters
2018-06-13 00:03:34 -07:00
Matt Nadareski
9c7cee0aa3 Fix validation, hook up to main dumping, fix dumping 2018-06-12 23:41:11 -07:00
Matt Nadareski
d549609a0f Add as many validations as humanly possible 2018-06-12 22:30:14 -07:00
Matt Nadareski
207fdbb49c Start adding validation to DIC command line 2018-06-12 22:06:16 -07:00
Matt Nadareski
63e63a0033 Add DIC command constants 2018-06-12 22:05:50 -07:00
Matt Nadareski
73be55a645 Merge pull request #31 from mnadareski/master
Add verification for overwrite
2018-06-12 20:30:54 -07:00
Matt Nadareski
d913aa247e Re-enable button on error 2018-06-12 20:29:40 -07:00
Matt Nadareski
d9a9f3bfd4 Add verification for overwrite 2018-06-12 20:22:49 -07:00
reignstumble
ea3e040b3f Merge pull request #29 from mnadareski/master
Add Custom Parameter Support
2018-06-12 20:47:54 -04:00
Matt Nadareski
ed67522675 Add custom parameter support 2018-06-12 16:49:33 -07:00
reignstumble
6e3902bbb1 Update README.md 2018-06-12 19:02:59 -04:00
Matt Nadareski
3f802e8548 Consistent UI states 2018-06-12 15:15:41 -07:00
reignstumble
c3c4cf6e5b Merge pull request #28 from mnadareski/master
Minor fixes
2018-06-12 17:43:33 -04:00
Matt Nadareski
39258c4e85 Use subfolder for indivdual dumps, change filename 2018-06-12 14:16:16 -07:00
Matt Nadareski
df81259fba Address feedback 2018-06-12 14:10:10 -07:00
reignstumble
a835c81951 Merge pull request #27 from mnadareski/master
Write Centralized Disc Information
2018-06-12 17:03:43 -04:00
Matt Nadareski
af4f3e8444 Add better default values, fix output 2018-06-12 13:48:30 -07:00
Matt Nadareski
085916e1e6 Fix minor issues with output, ordering 2018-06-12 13:34:25 -07:00
Matt Nadareski
557354c844 Fix build 2018-06-12 12:30:29 -07:00
Matt Nadareski
a23a74ffa4 Attempt to write out all information 2018-06-12 12:23:04 -07:00
Matt Nadareski
958aeeae32 Add constants, create formatted output 2018-06-12 11:39:10 -07:00
reignstumble
c73ff42500 Merge pull request #26 from mnadareski/master
Disc Information Framework
2018-06-12 09:38:11 -04:00
Matt Nadareski
24a93e298b Remove last batchfile 2018-06-12 01:03:10 -07:00
Matt Nadareski
ae81bb0743 Move unused code due to future dependencies 2018-06-12 00:40:41 -07:00
Matt Nadareski
cf7863fbb2 Add some system specific values, notes 2018-06-12 00:34:00 -07:00
Matt Nadareski
5e0e71120d Add layerbreak disc info for relevant discs 2018-06-12 00:06:30 -07:00
Matt Nadareski
5adbdd29e6 Add all CD-ROM info getters 2018-06-12 00:00:27 -07:00
Matt Nadareski
fd855b9024 More clariication of template, fix .dat reading 2018-06-11 23:43:38 -07:00
Matt Nadareski
17c283ff67 Add a couple more info grabbers 2018-06-11 22:07:42 -07:00
Matt Nadareski
a37e6aaf18 Add first two info extraction helpers 2018-06-11 21:51:35 -07:00
Matt Nadareski
1dbf23ca95 Add first iteration of getting output information (incomplete) 2018-06-11 21:23:32 -07:00
Matt Nadareski
2e52d00107 Add TODO 2018-06-11 21:02:39 -07:00
Matt Nadareski
eb69b74b4a Add check to ensure all output files are found 2018-06-11 21:01:50 -07:00
Matt Nadareski
db4a785558 Use path combination 2018-06-11 20:40:20 -07:00
Matt Nadareski
9bdd2d6951 First use of finding the first track 2018-06-11 20:37:44 -07:00
Matt Nadareski
de48c8f0f9 Add method to find the first (only) track of a dump 2018-06-11 20:32:04 -07:00
reignstumble
c59f7c2dae Merge pull request #25 from mnadareski/master
Cleanup/Automation
2018-06-11 23:12:49 -04:00
Matt Nadareski
2740a6a1c1 Don't blindly start processes 2018-06-11 00:14:37 -07:00
Matt Nadareski
56e8470069 Add note regarding issues/12 2018-06-10 23:57:26 -07:00
Matt Nadareski
a5f1a12aee Error checking and don't include speed in bd command 2018-06-10 23:35:25 -07:00
Matt Nadareski
8749b486a8 Separate paths to be configurable in future 2018-06-10 16:43:46 -07:00
Matt Nadareski
3446957f68 Better output names, misc cleanup 2018-06-10 16:37:05 -07:00
Matt Nadareski
cb9813db3d Spaces 2018-06-10 16:17:06 -07:00
Matt Nadareski
6e6b716c18 Remove all debug console writes 2018-06-10 16:16:47 -07:00
Matt Nadareski
400ef38c60 Make sure events don't have anything but calling a helper in them 2018-06-10 16:14:17 -07:00
Matt Nadareski
87d5fc0e66 More reasonable default drive speed 2018-06-10 16:10:09 -07:00
Matt Nadareski
d5d8ae0501 Fix build 2018-06-10 16:08:19 -07:00
Matt Nadareski
fae7a5254d Remove unnecessary event 2018-06-10 16:07:14 -07:00
Matt Nadareski
97be13f020 Make code more well-ordered, add default list of all drive speeds supported, privatize EVERYTHING 2018-06-10 16:06:20 -07:00
Matt Nadareski
dd0dfc5cf5 Move drive scanning to Utilities 2018-06-10 16:05:25 -07:00
Matt Nadareski
a7a295434d Simplification of rows, some renames 2018-06-10 16:04:58 -07:00
Matt Nadareski
319f5563cb More descriptive process names 2018-06-10 15:13:16 -07:00
Matt Nadareski
939174d8c4 Merge branch 'master' of https://github.com/mnadareski/DICUI 2018-06-10 15:11:30 -07:00
Matt Nadareski
1d5d7b265e Remove one unnecessary batch file creation step 2018-06-10 15:11:25 -07:00
Matt Nadareski
a2ac7222f9 Remove extra ECC check from PS1 2018-06-10 15:07:28 -07:00
reignstumble
4f0b5904c3 Merge pull request #24 from mnadareski/master
Add Enumerations; Helper Methods
2018-06-09 14:54:59 -04:00
Matt Nadareski
7ff848347c Merge branch 'master' into master 2018-06-08 16:24:11 -07:00
Matt Nadareski
a1c89d17b8 How did I miss this? 2018-06-08 16:16:50 -07:00
Matt Nadareski
3575c43703 Missed this one 2018-06-08 16:14:19 -07:00
Matt Nadareski
928c9966fa Cleanup, make some things work more intended 2018-06-08 16:12:46 -07:00
Matt Nadareski
a404a270f1 Make combobox entirely auto-generated 2018-06-08 15:54:23 -07:00
Matt Nadareski
dfba97685a Fix minor formatting issues 2018-06-08 15:15:47 -07:00
Matt Nadareski
8de2f1c9bb Add helper function to make list of systems 2018-06-08 14:55:17 -07:00
Matt Nadareski
f57b29615b Add other string conversion, update comments 2018-06-08 14:37:17 -07:00
reignstumble
565a9bb09a Update README.md 2018-06-08 17:20:33 -04:00
Matt Nadareski
d9d61cf665 Fix build 2018-06-08 14:15:05 -07:00
reignstumble
106f16d703 Features Update 2018-06-08 17:11:13 -04:00
Matt Nadareski
8b600a6a35 Add enumerations and unused methods 2018-06-08 14:05:06 -07:00
reignstumble
b251ae7927 Update README.md 2018-05-22 12:29:32 -04:00
ReignStumble
3a749b67e3 Merge branch 'master' of https://github.com/reignstumble/DICUI 2018-05-18 23:13:49 -04:00
ReignStumble
3abc277aab Changed /c2 to /c2 20 2018-05-18 23:13:47 -04:00
reignstumble
894f2c6fde Update README.md 2018-05-18 22:39:05 -04:00
ReignStumble
8c43615235 Fixed PS4 and XBOX one drive speed and more 2018-05-18 22:32:30 -04:00
ReignStumble
bbf51a7f94 Fixed a missing space in sg_raw.exe command 2018-05-14 14:55:04 -04:00
reignstumble
515e58fae8 Merge pull request #6 from mnadareski/master
Enable future settings use
2018-05-14 14:48:17 -04:00
Matt Nadareski
f3319f4ee2 Untabify 2018-05-14 11:21:17 -07:00
Matt Nadareski
e2789ddd11 Enable future settings use
This change converts the static DIC path to one that is controlled by settings. Currently, nothing saves or changes these settings so nothing has changed in terms of what the workflow is. This will, however, enable these settings to be controlled through the UI in the future, and allow for settings persistence between sessions without having to rely on AppData or the registry.
2018-05-14 11:20:31 -07:00
reignstumble
1c04f6e30c Update README.md 2018-05-14 13:49:58 -04:00
reignstumble
c7d4d319ce Update README.md 2018-05-14 13:49:26 -04:00
reignstumble
13a5f02082 Merge pull request #5 from mnadareski/master
Combine IBM PC-CD options, misc fixes
2018-05-14 13:42:34 -04:00
reignstumble
a188cad6ee Update README.md 2018-05-14 13:35:30 -04:00
Matt Nadareski
039cbf6de4 Merge branch 'master' into master 2018-05-14 10:28:35 -07:00
Matt Nadareski
c0cf207776 Ensure tabbing stays the same with source 2018-05-14 10:27:28 -07:00
Matt Nadareski
16ae5e1c66 Fix automatic merge issues 2018-05-14 10:26:25 -07:00
Matt Nadareski
bfca66951d Merge branch 'master' of https://github.com/mnadareski/DICUI 2018-05-14 10:24:52 -07:00
Matt Nadareski
5b777c3bb5 Combine IBM PC-CD options, fix "no copy protection"
Previously, the "No Copy Protection" variant was using the wrong "type" of dumping. This would have caused issues in the long run. This change also consolidates all of the variants previously used for IBM PC into a single option that can accurately capture all of the previous ones. This will reduce new user confusion about what choice to make given a disc.
2018-05-14 10:23:48 -07:00
reignstumble
4b840ebe4f Update README.md 2018-05-14 12:08:51 -04:00
reignstumble
d38c5cd051 Update README.md 2018-05-14 12:08:35 -04:00
reignstumble
7fa9c5c296 Update README.md 2018-05-14 12:07:30 -04:00
ReignStumble
3a28bc798a Cleanup 2018-05-14 12:06:27 -04:00
ReignStumble
e88df626b7 Missing break; for Audio CD 2018-05-14 10:29:31 -04:00
reignstumble
ff7355de6e Merge pull request #2 from mnadareski/master
Bring list to parity with Redump known types
2018-05-14 09:45:58 -04:00
reignstumble
3ed267d751 Merge branch 'master' into master 2018-05-14 09:45:46 -04:00
reignstumble
25a60ef9dd Merge pull request #4 from NHellFire/patch-1
Fix a few issues
2018-05-14 09:44:48 -04:00
Nathan Rennie-Waldock
eb664cebba Make sure output directory has a trailing backslash for PSX commands 2018-05-14 12:08:37 +01:00
Nathan Rennie-Waldock
da4d9fbf67 Quote output path as it may contain spaces 2018-05-14 11:56:30 +01:00
Nathan Rennie-Waldock
d21a5c8495 Fix PC CD (no copy protection) arguments 2018-05-14 11:54:10 +01:00
Matt Nadareski
e305ea1fdb Enable building on all machines 2018-05-13 22:09:37 -07:00
Matt Nadareski
ea82e42601 Bring list to parity with Redump known types
This change adds all remaining consoles and disc-based devices that are currently listed in Redump as valid devices. This also does some minor renames and splits that bring this to parity as well. One minor TODO note was added possibly for future ease-of-use for dumping IBM PC software.
2018-05-13 22:02:44 -07:00
reignstumble
c2c42a1acd Merge pull request #1 from mnadareski/master
Add a solution file for easier access
2018-05-14 00:17:44 -04:00
Matt Nadareski
a0fceee153 Add a solution file for easier access 2018-05-13 21:15:15 -07:00
reignstumble
aa9e6d3025 Delete DICUI_1.0.zip 2018-05-13 23:54:05 -04:00
reignstumble
677eb710fe Delete DICUI-1.01b.zip 2018-05-13 23:53:58 -04:00
reignstumble
3ef246384f Delete DICUI_1.01b.zip 2018-05-13 23:49:40 -04:00
reignstumble
76c15e585c Add files via upload 2018-05-13 23:49:26 -04:00
105 changed files with 29920 additions and 874 deletions

View File

@@ -0,0 +1,28 @@
---
name: Feature Request
about: For when you know better than me what you want
title: "[Request]"
labels: enhancement
assignees: mnadareski
---
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
- .NET Core 3.1 and .NET 5.0 have known limitations, so make sure that what you're asking for isn't already in another build.
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
If none of those apply, then continue...
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

22
.github/ISSUE_TEMPLATE/informational.md vendored Normal file
View File

@@ -0,0 +1,22 @@
---
name: Info
about: Something you need to tell me
title: "[Info]"
labels: question
assignees: mnadareski
---
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
- .NET Core 3.1 and .NET 5.0 have known limitations, so make sure that what you're giving information on isn't already in another build.
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
If none of those apply, then continue...
**Is your information related to one of the dumping programs supported or something that isn't a bug in the code? Please describe.**
A clear and concise description of what the information is. Ex. With the latest build of DumpingProgram, it [...]
**Additional context**
Add any other context or screenshots about the information here.

49
.github/ISSUE_TEMPLATE/issue-report.md vendored Normal file
View File

@@ -0,0 +1,49 @@
---
name: Issue Report
about: Tell me what's wrong, seriously
title: "[Problem]"
labels: bug
assignees: mnadareski
---
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the issue has already been addressed.
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
- .NET Core 3.1 and .NET 5.0 have known issues, please try using another build to reproduce the error
- Check multiple discs to help narrow down the issue
- Check the Options to see if changing any of those affects your issue.
If all of those fail, then continue...
**Version**
What version are you using?
- [ ] Stable release (version here)
- [ ] WIP release (version here)
**Build**
What runtime version are you using?
- [ ] .NET Framework 4.7.2 running on (Operating System)
- [ ] .NET Framework 4.8 running on (Operating System)
- [ ] .NET Core 3.1 running on (Operating System)
**Describe the issue**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "MPF.Library/Aaru/CICMMetadata"]
path = MPF.Library/Aaru/CICMMetadata
url = https://github.com/claunia/CICMMetadata

27
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,27 @@
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/MPF/bin/Debug/netcoreapp3.1/MPF.dll",
"args": [],
"cwd": "${workspaceFolder}/MPF",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}

42
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,42 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/MPF/MPF.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/MPF/MPF.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/MPF/MPF.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -1,9 +0,0 @@
<Application x:Class="DICUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DICUI"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace DICUI
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

288
CHANGELIST.md Normal file
View File

@@ -0,0 +1,288 @@
### 2.1 (2021-07-22)
- Enum, no more
- Sony works backward
- Add experimental dark mode
- Allow users to customize protection scanning
- Fix negative offsets for `/a` flag
- Always check for all DIC log files, just in case
- Check for the zipped logs for dealing with overwrites
- Be smarter about checking for zipped logs
- Change offset value for HVN discs
- Update to DIC 20210601
- Fix Aaru command normalization
- Handle carriage returns better
- Add logging helper class
- Set matcher on carriage return for log formatting
- Replace dumping program output processing
- Fix volume name detection for XBOX discs
- Add new setting for including artifacts in serialzied JSON
- Fix logging in to Redump for verifications
- Gracefully handle timeouts during login
- Update to DIC 20210701
- Support `/mr` value parameter
- Support new mainInfo header
- Add BD to IBM-PC supported discs
- Fix launching MPF on Windows 7
- Fix log window background turning white
- Add DMI/PFI/SS to log zipfile
- Update to BurnOutSharp 1.7.0
### 2.0 (2021-04-23)
- Rename DICUI to Media Preservation Frontend (MPF)
- Add handling for BEh drive _mainInfo.txt changes
- Fix multiline regex fields during info pulling
- Add preliminary support for user-defined default system
- Change labels in media info window depending on media type
- Update to Aaru v5.2
- Only pull disc information if every track returns at least one ID
- Add new supported Redump regions
- Remove Philips CD-i Digital Video from supported profiles
- Remove experimental Avalonia UI, will wait for MAUI next year
- ~~Updated to DIC version 20210102~~
- Add support for `/mr` DIC flag
- UI initialization code refactored to have more consistent results
- Support DIC `.imgtmp`, `.scmtmp`, and `.subtmp` possible output files
- Fix BCA formatting for CleanRip outputs
- Add hashing for UMDs from UIC outputs
- Fix information gathering for UIC outputs
- Fix issue with duplicate security sector data in XGD DiscImageCreator outputs
- ~~Update to BurnOutSharp 1.5.1~~
- ~~Update to DIC version 20210202~~
- Add VCD detection
- Fix UI not updating properly on drive change
- Add Xbox Series and PS5 to supported systems
- Add PS5 type detection and version extraction
- Add internal support for 3- and 4-layer discs
- Revamp disc information window
- Add PIC layerbreak extraction
- Overhaul main window and logging panel
- Overhaul options window
- Update attributions and about text
- ~~Updated to DIC version 20210301~~
- Add user-selectable Language Selection via dropdown in disc submission window for PS2
- Separate out Aaru- and DIC-specific settings
- Add new options based on original "Paranoid Mode" mega-option
- Internal overhaul of options and dump environment
- VideoNow discs are audio only
- Hook up default system in options
- Make inner and outer layers in UI and outputs more clear
- Program output to log by default, setting otherwise
- DVDs and BDs can have label-side data
- Remove .NET Framework 4.7.2 support
- Make logging window a bit safer
- Support new Redump languages and regions
- Implement internal log queue
- It's a secret...
- Updated to DIC version 20210401
- Support log file compression
- Add ring code guide button to disc submission window
- ~~Update to BurnOutSharp 1.6.0~~
- Fix "rewinding" issue when inputting output paths with spaces
- Add version to About box
- Update to BurnOutSharp 1.6.1
### 1.18 (2020-11-10)
- Add more information extraction and generation for Aaru
- Remove instances of CD Check from copy protection (again, sorry)
- Fix multiline submission info outputs
- Fix PVD retrieval for multi-session discs
- Updated to DIC version 20200921
- Add and fix multiple Sega disc header pieces or submission info
- Fixed issues in parsing the alternate mainInfo format
- Fixed issue with logging clear not working properly
- ~~Updated to BurnOutSharp 1.4.1~~
- Added split archives for AppVeyor builds
- Remove subdump from both UI and run steps
- Removed default config file
- Fixed copy protect scan using wrong drive when using UI option
- Changed default to skip fixed drives
- Fixed default media type when skipping type detection
- Attempt sector reading for Saturn system detection
- Fixed default media type when detection fails
- Add option to allow users to select dumping program
- ~~Updated to DIC version 20201101~~
- Add support for `/ps` DIC flag
- Updated to BurnOutSharp 1.5.0
- Added HD-DVD-Video detection
### 1.17.1 (2020-09-14)
- Shuffled some shared, internal UI variables
- Synced WPF and Avalonia UI internals
- Made the disc information window less prone to bugs
- Fixed DIC flags based on code (not documentation)
- Added support for old(?) DIC flags: `/fix` and `/re`
### 1.17 (2020-09-12)
- Updated to Aaru version 5.1
- Updated to BurnOutSharp version 1.4.0
- Updated to DIC version 20200716
- Added experimental Avalonia UI
- Created wiki
- Removed .NET 4.6.2 and .NET Core 3.0 builds
- Added .NET 4.8 and .NET Core 3.1 builds
- Fix numerous things related to PS1/PS2
- Make subdump running optional
- Overhaul DICUI.Check with more options
- Numerous small bug- and regression-fixes
### 1.16.1 (2020-05-07)
- Add preliminary support for DD for Windows (end to end still NW)
- Add CNF parsing for Konami Python 2 discs (PS2-based)
- Updated included Aaru version
- Massive cleanup effort to detangle large chunks of code
- Miscellaneous bugfixes that came from the above
### 1.16 (2020-04-13)
- Updated to DIC version 20200403
- UI updates based on user feedback
- Added support for Aaru (formerly DiscImageChef)
- Added more support for different output file formats (such as CleanRip)
- Add PS1/PS2 serial extraction and matching
- Fix PS1 date support when both PSX.EXE and normal executable are both present
- Update BurnOutSharp
- Many MANY bits of internal cleanup
### 1.15 (2019-11-16)
- Updated to DIC version 20191116
- Support non-optical, non-floppy stuff better
- Update protection scanning and fix some things
### 1.14 (2019-10-01)
- Updated to DIC version 20191001
- Added builds for .NET 4.6.2, .NET 4.7.2, and .NET Core 3.0
- Updated and fixed a bunch of things related to Redump
- Fixed path persistence when changing system and media type
- Added more system autodetects
- Added new, optional, disc information filling window (WIP)
### 1.13 (2019-07-02)
- Added new DIC commands and flags
- Made DICUI Check more robust
- Added and updated systems along with format cleanup
- Created a new SubmissionInfo template (and internals)
- Added automatic grabbing of Redump information, if possible
- Better media format support (BD, UMD)
- Initial disc type detection
### 1.12.2 (2019-04-19)
- Added DICUI Check, a new standalone tool for parsing DIC output from platforms unsupported by DICUI
- Added a few machines/formats
- Updated to DIC version 20190326
- Added DMI data extraction for Xbox and X360
### 1.12.1 (2019-01-28)
- Fixed !submissionInfo.txt output for CD-ROM and GD-ROM
### 1.12 (2019-01-27)
- Added a few new systems and formats
- Added new DIC commands and flags
- Updated the `!submissionInfo.txt` file order
- Fixed Audio CD handling
- Added Sega CD / Mega CD header extraction
- Readded Floppy Disk as a supported format
- And more! See the full Git commit list for more details
### 1.11 (2018-09-20)
- Fix formatting of XBOX and XBOX 360 security sector output
- Add new XBOX swap commands and outputs
- Fixes for some PlayStation 2 and 4 outputs
- Added external programs to AppVeyor builds
- Fixed `.` in path issues with DIC; attempted to fix issues with `&`
- Combined XBOX 360 XGD 2/3 due to new DIC support with Kreon drives
- Fixed (semi-)longstanding bug with XBOX disc layer detection
- Added DVD-Video protection output
- Made custom parameters work a little more intuitively
- Added *EXPERIMENTAL* Winforms-based UI
- And more! See the full Git commit list for more details
### 1.10 (2018-07-29)
- Added many new options for user customization
- Added unit testing and an AppVeyor build
- Many code refactorings
- **LOG WINDOW**
- Separated out protection scan and Unshield ports to new projects
- Added "empty drive" support; should help with 3DO and HFS dumping
- And much more! See the full Git commit list for more details
### 1.07 (2018-06-27)
- Separated system and media type for easier navigation
- Combined instances of single- and dual-layer discs
- Removed reliance on **sg-raw** and **psxt001z**
- Added system and disc type to the submission info
- First attempt at getting current disc type
- Made the three PSX-specific fields (**EDC**, **Anti-modchip**, and **LibCrypt**) automatically filled in, when possible
- Many, many, many behind the scenes updates for speed, future features, and stability
### 1.06 (2018-06-15)
- Fixed not being able to use the `/c2` flag properly
- Fixed times when the ability to start dumping was improperly allowed
- Added full support for XBOX and XBOX360 (XDG1, XDG2) dumping through DIC (using a Kreon, or presumably a 0800)
### 1.05a (2018-06-14)
- Fixed some ordering and nullability issues
- Added automatic fields for PS1, PS2, Saturn
### 1.05 (2018-06-14)
- Miscellaneous fixes around custom parameter validation, dump information accuracy, settings window, and TODO cleanup
- Add many more supported platforms, mostly arcade (based on publicly available information)
- Add floppy disk dumping support
- Add optional disc eject on completion
- Add subdump for Sega Saturn
- Fully support newest version of DIC including all new flags and commands
- PlayStation and Saturn discs still don't have all internal information automatically generated
### 1.04b (2018-06-13)
- Added subIntention reading
- Fixed extra extensions being appended
- Fixed internationalization error (number formatting)
- Fixed "Custom Input" not working
### 1.04a (2018-06-13)
- Fixed issue with empty trays
- Added settings dialog
### 1.04 (2018-06-13)
- Behind-the-scenes fixes and formatting
- Better checks for external programs
- Automatically changing disc information
- Custom parameters (and parameter validation)
- Automatic drive speed selection
- Automatic submission information creation
- Add ability to stop a dump from the UI
### 1.03 (2018-06-08)
- edccchk now run on all CD-Roms
- Discs unsupported by Windows are now regonized
- Extra \ when accepting default save has been removed.
### 1.02b (2018-05-18)
- Added missing DLL
### 1.02 (2018-05-18)
- Fixed XBOX One and PS4 Drive Speed issue.
- Started implementing DiscImageCreator Path selection.
- Conforming my naming for objects and variable
### 1.01d (2018-05-18)
-Combine IBM PC-CD options, misc fixes.

View File

@@ -1,144 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7B1B75EB-8940-466F-BD51-76471A57F9BE}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>DICUI</RootNamespace>
<AssemblyName>DICUI</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>C:\Users\admin\Desktop\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>1</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Icon.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<ManifestCertificateThumbprint>654CEE5FEAF46C8C1C369D7ED34DA157828A8D2F</ManifestCertificateThumbprint>
</PropertyGroup>
<PropertyGroup>
<ManifestKeyFile>WpfApp1_TemporaryKey.pfx</ManifestKeyFile>
</PropertyGroup>
<PropertyGroup>
<GenerateManifests>true</GenerateManifests>
</PropertyGroup>
<PropertyGroup>
<SignManifests>true</SignManifests>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.6.1">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.6.1 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Resource Include="Icon.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Binary file not shown.

Binary file not shown.

View File

@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
DICUI Copyright (C) 2018 ReignStumble
MPF Copyright (C) 2018 ReignStumble
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2"/>
</startup>
</configuration>
</configuration>

View File

@@ -0,0 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<OutputType>Exe</OutputType>
<Title>MPF Check</Title>
<AssemblyName>MPF.Check</AssemblyName>
<Description>Validator for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
<PropertyGroup>
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.7.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj">
<Project>{51ab0928-13f9-44bf-a407-b6957a43a056}</Project>
<Name>MPF.Library</Name>
</ProjectReference>
</ItemGroup>
</Project>

275
MPF.Check/Program.cs Normal file
View File

@@ -0,0 +1,275 @@
using System;
using System.IO;
using BurnOutSharp;
using MPF.Data;
using MPF.Redump;
using MPF.Utilities;
namespace MPF.Check
{
public class Program
{
public static void Main(string[] args)
{
// Help options
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
{
DisplayHelp();
return;
}
// List options
if (args[0] == "-lm" || args[0] == "--listmedia")
{
ListMediaTypes();
Console.ReadLine();
return;
}
else if (args[0] == "-lp" || args[0] == "--listprograms")
{
ListPrograms();
Console.ReadLine();
return;
}
else if (args[0] == "-ls" || args[0] == "--listsystems")
{
ListKnownSystems();
Console.ReadLine();
return;
}
// Normal operation check
if (args.Length < 3)
{
DisplayHelp("Invalid number of arguments");
return;
}
// Check the MediaType
var mediaType = Converters.ToMediaType(args[0].Trim('"'));
if (mediaType == MediaType.NONE)
{
DisplayHelp($"{args[0]} is not a recognized media type");
return;
}
// Check the KnownSystem
var knownSystem = Converters.ToKnownSystem(args[1].Trim('"'));
if (knownSystem == KnownSystem.NONE)
{
DisplayHelp($"{args[1]} is not a recognized system");
return;
}
// Default values
string username = null, password = null;
string internalProgram = "DiscImageCreator";
string path = string.Empty;
bool scan = false, compress = false;
// Loop through and process options
int startIndex = 2;
for (; startIndex < args.Length; startIndex++)
{
// Redump login
if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
{
string[] credentials = args[startIndex].Split('=')[1].Split(';');
username = credentials[0];
password = credentials[1];
}
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
{
username = args[startIndex + 1];
password = args[startIndex + 2];
startIndex += 2;
}
// Use specific program
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
{
internalProgram = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
internalProgram = args[startIndex + 1];
startIndex++;
}
// Use a device path for physical checks
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
{
path = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
{
path = args[startIndex + 1];
startIndex++;
}
// Scan for protection (requires device path)
else if (args[startIndex].StartsWith("-s") || args[startIndex].StartsWith("--scan"))
{
scan = true;
}
// Compress log and extraneous files
else if (args[startIndex].StartsWith("-z") || args[startIndex].StartsWith("--zip"))
{
compress = true;
}
// Default, we fall out
else
{
break;
}
}
// Make new Progress objects
var resultProgress = new Progress<Result>();
resultProgress.ProgressChanged += ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ProgressUpdated;
// If credentials are invalid, alert the user
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
{
using (RedumpWebClient wc = new RedumpWebClient())
{
bool? loggedIn = wc.Login(username, password);
if (loggedIn == true)
Console.WriteLine("Redump username and password accepted!");
else if (loggedIn == false)
Console.WriteLine("Redump username and password denied!");
else
Console.WriteLine("An error occurred validating your crendentials!");
}
}
// Loop through all the rest of the args
for (int i = startIndex; i < args.Length; i++)
{
// Check for a file
if (!File.Exists(args[i].Trim('"')))
{
DisplayHelp($"{args[i].Trim('"')} does not exist");
return;
}
// Get the full file path
string filepath = Path.GetFullPath(args[i].Trim('"'));
// Now populate an environment
var options = new Options
{
InternalProgram = Converters.ToInternalProgram(internalProgram),
ScanForProtection = scan && !string.IsNullOrWhiteSpace(path),
PromptForDiscInformation = false,
CompressLogFiles = compress,
RedumpUsername = username,
RedumpPassword = password,
};
Drive drive = null;
if (!string.IsNullOrWhiteSpace(path))
drive = new Drive(null, new DriveInfo(path));
var env = new DumpEnvironment(options, "", filepath, drive, knownSystem, mediaType, null);
// Finally, attempt to do the output dance
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
Console.WriteLine(result.Message);
}
}
/// <summary>
/// Display help for MPF.Check
/// </summary>
/// <param name="error">Error string to prefix the help text with</param>
private static void DisplayHelp(string error = null)
{
if (error != null)
Console.WriteLine(error);
Console.WriteLine("Usage:");
Console.WriteLine("MPF.Check.exe <mediatype> <system> [options] </path/to/output.bin> ...");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
Console.WriteLine("-lm, --listmedia List supported media types");
Console.WriteLine("-ls, --listsystems List supported system types");
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");
Console.WriteLine();
Console.WriteLine("Check Options:");
Console.WriteLine("-c, --credentials <user> <pw> Redump username and password");
Console.WriteLine("-u, --use <program> Dumping program output type");
Console.WriteLine("-p, --path <drivepath> Physical drive path for additional checks");
Console.WriteLine("-s, --scan Enable copy protection scan (requires --path)");
Console.WriteLine("-z, --zip Enable log file compression");
Console.WriteLine();
}
/// <summary>
/// List all media types with their short usable names
/// </summary>
private static void ListMediaTypes()
{
Console.WriteLine("Supported Media Types:");
foreach (var val in Enum.GetValues(typeof(MediaType)))
{
if (((MediaType)val) == MediaType.NONE)
continue;
Console.WriteLine($"{((MediaType?)val).ShortName()} - {((MediaType?)val).LongName()}");
}
}
/// <summary>
/// List all programs with their short usable names
/// </summary>
private static void ListPrograms()
{
Console.WriteLine("Supported Programs:");
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
{
if (((InternalProgram)val) == InternalProgram.NONE)
continue;
Console.WriteLine($"{((InternalProgram?)val).LongName()}");
}
}
/// <summary>
/// List all known systems with their short usable names
/// </summary>
private static void ListKnownSystems()
{
Console.WriteLine("Supported Known Systems:");
foreach (var val in Enum.GetValues(typeof(KnownSystem)))
{
if (((KnownSystem)val) == KnownSystem.NONE)
continue;
Console.WriteLine($"{((KnownSystem?)val).ShortName()} - {((KnownSystem?)val).LongName()}");
}
}
/// <summary>
/// Simple process counter to write to console
/// </summary>
private static void ProgressUpdated(object sender, Result value)
{
Console.WriteLine(value.Message);
}
/// <summary>
/// Simple process counter to write to console
/// </summary>
private static void ProgressUpdated(object sender, ProtectionProgress value)
{
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
}
}
}

View File

@@ -0,0 +1,7 @@
{
"profiles": {
"MPF.Check": {
"commandName": "Project"
}
}
}

View File

@@ -0,0 +1,477 @@
namespace MPF.Aaru
{
/// <summary>
/// Top-level commands for Aaru
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
// Database Family
public const string DatabasePrefixShort = "db";
public const string DatabasePrefixLong = "database";
public const string DatabaseStats = "stats";
public const string DatabaseUpdate = "update";
// Device Family
public const string DevicePrefixShort = "dev";
public const string DevicePrefixLong = "device";
public const string DeviceInfo = "info";
public const string DeviceList = "list";
public const string DeviceReport = "report";
// Filesystem Family
public const string FilesystemPrefixShort = "fi";
public const string FilesystemPrefixShortAlt = "fs";
public const string FilesystemPrefixLong = "filesystem";
public const string FilesystemExtract = "extract";
public const string FilesystemListShort = "ls";
public const string FilesystemListLong = "list";
public const string FilesystemOptions = "options";
// Image Family
public const string ImagePrefixShort = "i";
public const string ImagePrefixLong = "image";
public const string ImageAnalyze = "analyze";
public const string ImageChecksumShort = "chk";
public const string ImageChecksumLong = "checksum";
public const string ImageCompareShort = "cmp";
public const string ImageCompareLong = "compare";
public const string ImageConvert = "convert";
public const string ImageCreateSidecar = "create-sidecar";
public const string ImageDecode = "decode";
public const string ImageEntropy = "entropy";
public const string ImageInfo = "info";
public const string ImageOptions = "options";
public const string ImagePrint = "print";
public const string ImageVerify = "verify";
// Media Family
public const string MediaPrefixShort = "m";
public const string MediaPrefixLong = "media";
public const string MediaDump = "dump";
public const string MediaInfo = "info";
public const string MediaScan = "scan";
// Standalone Commands
public const string Configure = "configure";
public const string Formats = "formats";
public const string ListEncodings = "list-encodings";
public const string ListNamespaces = "list-namespaces";
public const string Remote = "remote";
}
/// <summary>
/// Supported encodings for Aaru
/// </summary>
/// TODO: Use to verify encoding settings
public static class EncodingStrings
{
public const string ArabicMac = "x-mac-arabic";
public const string AtariASCII = "atascii";
public const string CentralEuropeanMac = "x-mac-ce";
public const string CommodorePETSCII = "petscii";
public const string CroatianMac = "x-mac-croatian";
public const string CyrillicMac = "x-mac-cryillic";
public const string FarsiMac = "x-mac-farsi";
public const string GreekMac = "x-mac-greek";
public const string HebrewMac = "x-mac-hebrew";
public const string RomanianMac = "x-mac-romanian";
public const string SinclairZXSpectrum = "spectrum";
public const string SinclairZX80 = "zx80";
public const string SinclairZX81 = "zx81";
public const string TurkishMac = "x-mac-turkish";
public const string UkrainianMac = "x-mac-ukrainian";
public const string Unicode = "utf-16";
public const string UnicodeBigEndian = "utf-16BE";
public const string UnicodeUTF32BigEndian = "utf-32BE";
public const string UnicodeUTF32 = "utf-32";
public const string UnicodeUTF7 = "utf-7";
public const string UnicodeUTF8 = "utf-8";
public const string USASCII = "us-ascii";
public const string WesternEuropeanAppleII = "apple2";
public const string WesternEuropeanAppleIIc = "apple2c";
public const string WesternEuropeanAppleIIe = "apple2e";
public const string WesternEuropeanAppleIIgs = "apple2gs";
public const string WesternEuropeanAppleLisa = "lisa";
public const string WesternEuropeanAtariST = "atarist";
public const string WesternEuropeanGEM = "gem";
public const string WesternEuropeanGEOS = "geos";
public const string WesternEuropeanISO = "iso-8859-1";
public const string WesternEuropeanMac = "macintosh";
public const string WesternEuropeanRadix50 = "radix50";
}
/// <summary>
/// Dumping flags for Aaru
/// </summary>
public static class FlagStrings
{
// Boolean flags
public const string Adler32Short = "-a";
public const string Adler32Long = "--adler32";
public const string ClearLong = "--clear";
public const string ClearAllLong = "--clear-all";
public const string CRC16Long = "--crc16";
public const string CRC32Short = "-c";
public const string CRC32Long = "--crc32";
public const string CRC64Long = "--crc64";
public const string DebugShort = "-d";
public const string DebugLong = "--debug";
public const string DiskTagsShort = "-f";
public const string DiskTagsLong = "--disk-tags";
public const string DuplicatedSectorsShort = "-p";
public const string DuplicatedSectorsLong = "--duplicated-sectors";
public const string EjectLong = "--eject";
public const string ExtendedAttributesShort = "-x";
public const string ExtendedAttributesLong = "--xattrs";
public const string FilesystemsShort = "-f";
public const string FilesystemsLong = "--filesystems";
public const string FirstPregapLong = "--first-pregap";
public const string FixOffsetLong = "--fix-offset";
public const string FixSubchannelLong = "--fix-subchannel";
public const string FixSubchannelCrcLong = "--fix-subchannel-crc";
public const string FixSubchannelPositionLong = "--fix-subchannel-position";
public const string Fletcher16Long = "--fletcher16";
public const string Fletcher32Long = "--fletcher32";
public const string ForceShort = "-f";
public const string ForceLong = "--force";
public const string GenerateSubchannelsLong = "--generate-subchannels";
public const string HelpShort = "-h";
public const string HelpShortAlt = "-?";
public const string HelpLong = "--help";
public const string LongFormatShort = "-l";
public const string LongFormatLong = "--long-format";
public const string LongSectorsShort = "-r";
public const string LongSectorsLong = "--long-sectors";
public const string MD5Short = "-m";
public const string MD5Long = "--md5";
public const string MetadataLong = "--metadata";
public const string PartitionsShort = "-p";
public const string PartitionsLong = "--partitions";
public const string PersistentLong = "--persistent";
public const string PrivateLong = "--private";
public const string ResumeShort = "-r";
public const string ResumeLong = "--resume";
public const string RetrySubchannelLong = "--retry-subchannel";
public const string SectorTagsShort = "-p";
public const string SectorTagsLong = "--sector-tags";
public const string SeparatedTracksShort = "-t";
public const string SeparatedTracksLong = "--separated-tracks";
public const string SHA1Short = "-s";
public const string SHA1Long = "--sha1";
public const string SHA256Long = "--sha256";
public const string SHA384Long = "--sha384";
public const string SHA512Long = "--sha512";
public const string SkipCdiReadyHoleLong = "--skip-cdiready-hole";
public const string SpamSumShort = "-f";
public const string SpamSumLong = "--spamsum";
public const string StopOnErrorShort = "-s";
public const string StopOnErrorLong = "--stop-on-error";
public const string TapeShort = "-t";
public const string TapeLong = "--tape";
public const string TrimLong = "--trim";
public const string VerboseShort = "-v";
public const string VerboseLong = "--verbose";
public const string VerifyDiscShort = "-w";
public const string VerifyDiscLong = "--verify-disc";
public const string VerifySectorsShort = "-s";
public const string VerifySectorsLong = "--verify-sectors";
public const string VersionLong = "--version";
public const string WholeDiscShort = "-w";
public const string WholeDiscLong = "--whole-disc";
// Int8 flags
public const string SpeedLong = "--speed";
// Int16 flags
public const string RetryPassesShort = "-p";
public const string RetryPassesLong = "--retry-passes";
public const string WidthShort = "-w";
public const string WidthLong = "--width";
// Int32 flags
public const string BlockSizeShort = "-b";
public const string BlockSizeLong = "--block-size";
public const string CountShort = "-c";
public const string CountLong = "--count";
public const string MediaLastSequenceLong = "--media-lastsequence";
public const string MediaSequenceLong = "--media-sequence";
public const string SkipShort = "-k";
public const string SkipLong = "--skip";
// Int64 flags
public const string LengthShort = "-l"; // or "all"
public const string LengthLong = "--length"; // or "all"
public const string StartShort = "-s";
public const string StartLong = "--start";
// String flags
public const string CommentsLong = "--comments";
public const string CreatorLong = "--creator";
public const string DriveManufacturerLong = "--drive-manufacturer";
public const string DriveModelLong = "--drive-model";
public const string DriveRevisionLong = "--drive-revision";
public const string DriveSerialLong = "--drive-serial";
public const string EncodingShort = "-e";
public const string EncodingLong = "--encoding";
public const string FormatConvertShort = "-p";
public const string FormatConvertLong = "--format";
public const string FormatDumpShort = "-t";
public const string FormatDumpLong = "--format";
public const string ImgBurnLogShort = "-b";
public const string ImgBurnLogLong = "--ibg-log";
public const string MediaBarcodeLong = "--media-barcode";
public const string MediaManufacturerLong = "--media-manufacturer";
public const string MediaModelLong = "--media-model";
public const string MediaPartNumberLong = "--media-partnumber";
public const string MediaSerialLong = "--media-serial";
public const string MediaTitleLong = "--media-title";
public const string MHDDLogShort = "-m";
public const string MHDDLogLong = "--mhdd-log";
public const string NamespaceShort = "-n";
public const string NamespaceLong = "--namespace";
public const string OptionsShort = "-O";
public const string OptionsLong = "--options";
public const string OutputPrefixShort = "-w";
public const string OutputPrefixLong = "--output-prefix";
public const string ResumeFileShort = "-r";
public const string ResumeFileLong = "--resume-file";
public const string SubchannelLong = "--subchannel";
public const string XMLSidecarShort = "-x";
public const string XMLSidecarLong = "--cicm-xml";
}
/// <summary>
/// Supported formats for Aaru
/// </summary>
/// TODO: Use to verify format settings
public static class FormatStrings
{
// Supported filters
public const string AppleDouble = "AppleDouble";
public const string AppleSingle = "AppleSingle";
public const string BZip2 = "BZip2";
public const string GZip = "GZip";
public const string LZip = "LZip";
public const string MacBinary = "MacBinary";
public const string NoFilter = "No filter";
public const string PCExchange = "PCExchange";
public const string XZ = "XZ";
// Read-only media image formats
public const string AppleDiskArchivalRetrievalTool = "Apple Disk Archival/Retrieval Tool";
public const string AppleNewDiskImageFormat = "Apple New Disk Image Format";
public const string AppleNIB = "Apple NIB";
public const string BlindWrite4 = "BlindWrite 4";
public const string BlindWrite5 = "BlindWrite 5";
public const string CPCEMUDiskFileAndExtendedCPCDiskFile = "CPCEMU Disk-File and Extended CPC Disk-File";
public const string D2FDiskImage = "d2f disk image";
public const string D88DiskImage = "D88 Disk Image";
public const string DIMDiskImage = "DIM Disk Image";
public const string DiscFerret = "DiscFerret";
public const string DiscJuggler = "DiscJuggler";
public const string DreamcastGDIImage = "Dreamcast GDI image";
public const string DunfieldsIMD = "Dunfield's IMD";
public const string HDCopyDiskImage = "HD-Copy disk image";
public const string KryoFluxSTREAM = "KryoFlux STREAM";
public const string MAMECompressedHunksOfData = "MAME Compressed Hunks of Data";
public const string MicrosoftVHDX = "Microsoft VHDX";
public const string NeroBurningROMImage = "Nero Burning ROM image";
public const string PartCloneDiskImage = "PartClone disk image";
public const string PartimageDiskImage = "Partimage disk image";
public const string SpectrumFloppyDiskImage = "Spectrum Floppy Disk Image";
public const string SuperCardPro = "SuperCardPro";
public const string SydexCopyQM = "Sydex CopyQM";
public const string SydexTeleDisk = "Sydex TeleDisk";
// Read/write media image formats
public const string AaruFormat = "Aaru Format";
public const string ACTApricotDiskImage = "ACT Apricot Disk Image";
public const string Alcohol120MediaDescriptorStructure = "Alcohol 120% Media Descriptor Structure";
public const string Anex86DiskImage = "Anex86 Disk Image";
public const string Apple2InterleavedDiskImage = "Apple ][Interleaved Disk Image";
public const string Apple2IMG = "Apple 2IMG";
public const string AppleDiskCopy42 = "Apple DiskCopy 4.2";
public const string AppleUniversalDiskImageFormat = "Apple Universal Disk Image Format";
public const string BasicLisaUtility = "Basic Lisa Utility";
public const string CDRDAOTocfile = "CDRDAO tocfile";
public const string CDRWinCuesheet = "CDRWin cuesheet";
public const string CisCopyDiskImageDCFile = "CisCopy Disk Image(DC-File)";
public const string CloneCD = "CloneCD";
public const string CopyTape = "CopyTape";
public const string DigitalResearchDiskCopy = "Digital Research DiskCopy";
public const string IBMSaveDskF = "IBM SaveDskF";
public const string MAXIDiskImage = "MAXI Disk image";
public const string ParallelsDiskImage = "Parallels disk image";
public const string QEMUCopyOnWriteDiskImage = "QEMU Copy-On-Write disk image";
public const string QEMUCopyOnWriteDiskImageV2 = "QEMU Copy-On-Write disk image v2";
public const string QEMUEnhancedDiskImage = "QEMU Enhanced Disk image";
public const string RawDiskImage = "Raw Disk Image";
public const string RayAracheliansDiskIMage = "Ray Arachelian's Disk IMage";
public const string RSIDEHardDiskImage = "RS-IDE Hard Disk Image";
public const string T98HardDiskImage = "T98 Hard Disk Image";
public const string T98NextNHDr0DiskImage = "T98-Next NHD r0 Disk Image";
public const string Virtual98DiskImage = "Virtual98 Disk Image";
public const string VirtualBoxDiskImage = "VirtualBox Disk Image";
public const string VirtualPC = "VirtualPC";
public const string VMwareDiskImage = "VMware disk image";
// Supported filesystems for identification and information only
public const string AcornAdvancedDiscFilingSystem = "Acorn Advanced Disc Filing System";
public const string AlexanderOsipovDOSFileSystem = "Alexander Osipov DOS file system";
public const string AmigaDOSFilesystem = "Amiga DOS filesystem";
public const string AppleFileSystem = "Apple File System";
public const string AppleHFSPlusFilesystem = "Apple HFS+ filesystem";
public const string AppleHierarchicalFileSystem = "Apple Hierarchical File System";
public const string AppleProDOSFilesystem = "Apple ProDOS filesystem";
public const string AtheOSFilesystem = "AtheOS Filesystem";
public const string BeFilesystem = "Be Filesystem";
public const string BSDFastFileSystem = "BSD Fast File System(aka UNIX File System, UFS)";
public const string BTreeFileSystem = "B-tree file system";
public const string CommodoreFileSystem = "Commodore file system";
public const string CramFilesystem = "Cram filesystem";
public const string DumpEightPlugin = "dump(8) Plugin";
public const string ECMA67 = "ECMA-67";
public const string ExtentFileSystemPlugin = "Extent File System Plugin";
public const string F2FSPlugin = "F2FS Plugin";
public const string Files11OnDiskStructure = "Files-11 On-Disk Structure";
public const string FossilFilesystemPlugin = "Fossil Filesystem Plugin";
public const string HAMMERFilesystem = "HAMMER Filesystem";
public const string HighPerformanceOpticalFileSystem = "High Performance Optical File System";
public const string HPLogicalInterchangeFormatPlugin = "HP Logical Interchange Format Plugin";
public const string JFSPlugin = "JFS Plugin";
public const string LinuxExtendedFilesystem = "Linux extended Filesystem";
public const string LinuxExtendedFilesystem234 = "Linux extended Filesystem 2, 3 and 4";
public const string LocusFilesystemPlugin = "Locus Filesystem Plugin";
public const string MicroDOSFileSystem = "MicroDOS file system";
public const string MicrosoftExtendedFileAllocationTable = "Microsoft Extended File Allocation Table";
public const string MinixFilesystem = "Minix Filesystem";
public const string NewTechnologyFileSystem = "New Technology File System(NTFS)";
public const string NILFS2Plugin = "NILFS2 Plugin";
public const string NintendoOpticalFilesystems = "Nintendo optical filesystems";
public const string OS2HighPerformanceFileSystem = "OS/2 High Performance File System";
public const string OS9RandomBlockFilePlugin = "OS-9 Random Block File Plugin";
public const string PCEngineCDPlugin = "PC Engine CD Plugin";
public const string PCFXPlugin = "PC-FX Plugin";
public const string ProfessionalFileSystem = "Professional File System";
public const string QNX4Plugin = "QNX4 Plugin";
public const string QNX6Plugin = "QNX6 Plugin";
public const string ReiserFilesystemPlugin = "Reiser Filesystem Plugin";
public const string Reiser4FilesystemPlugin = "Reiser4 Filesystem Plugin";
public const string ResilientFileSystemPlugin = "Resilient File System plugin";
public const string RT11FileSystem = "RT-11 file system";
public const string SmartFileSystem = "SmartFileSystem";
public const string SolarOSFilesystem = "Solar_OS filesystem";
public const string SquashFilesystem = "Squash filesystem";
public const string UNICOSFilesystemPlugin = "UNICOS Filesystem Plugin";
public const string UniversalDiskFormat = "Universal Disk Format";
public const string UNIXBootFilesystem = "UNIX Boot filesystem";
public const string UNIXSystemVFilesystem = "UNIX System V filesystem";
public const string VeritasFilesystem = "Veritas filesystem";
public const string VMwareFilesystem = "VMware filesystem";
public const string XFSFilesystemPlugin = "XFS Filesystem Plugin";
public const string XiaFilesystem = "Xia filesystem";
public const string ZFSFilesystemPlugin = "ZFS Filesystem Plugin";
// Supported filesystems that can read their contents
public const string AppleDOSFileSystem = "Apple DOS File System";
public const string AppleLisaFileSystem = "Apple Lisa File System";
public const string AppleMacintoshFileSystem = "Apple Macintosh File System";
public const string CPMFileSystem = "CP/M File System";
public const string FATXFilesystemPlugin = "FATX Filesystem Plugin";
public const string ISO9660Filesystem = "ISO9660 Filesystem";
public const string MicrosoftFileAllocationTable = "Microsoft File Allocation Table";
public const string OperaFilesystemPlugin = "Opera Filesystem Plugin";
public const string UCSDPascalFilesystem = "U.C.S.D.Pascal filesystem";
// Supported partitioning schemes
public const string AcornFileCorePartitions = "Acorn FileCore partitions";
public const string ACTApricotPartitions = "ACT Apricot partitions";
public const string AmigaRigidDiskBlock = "Amiga Rigid Disk Block";
public const string ApplePartitionMap = "Apple Partition Map";
public const string AtariPartitions = "Atari partitions";
public const string BSDDisklabel = "BSD disklabel";
public const string DECDisklabel = "DEC disklabel";
public const string DragonFlyBSD64bitDisklabel = "DragonFly BSD 64-bit disklabel";
public const string GUIDPartitionTable = "GUID Partition Table";
public const string Human68kPartitions = "Human 68k partitions";
public const string MasterBootRecord = "Master Boot Record";
public const string NECPC9800PartitionTable = "NEC PC-9800 partition table";
public const string NeXTDisklabel = "NeXT Disklabel";
public const string Plan9PartitionTable = "Plan9 partition table";
public const string RioKarmaPartitioning = "Rio Karma partitioning";
public const string SGIDiskVolumeHeader = "SGI Disk Volume Header";
public const string SunDisklabel = "Sun Disklabel";
public const string UNIXHardwired = "UNIX hardwired";
public const string UNIXVTOC = "UNIX VTOC";
public const string XboxPartitioning = "Xbox partitioning";
public const string XENIX = "XENIX";
}
/// <summary>
/// Supported namespaces for Aaru
/// </summary>
/// TODO: Use to verify namespace settings
public static class NamespaceStrings
{
// Namespaces for Apple Lisa File System
public const string LisaOfficeSystem = "office";
public const string LisaPascalWorkshop = "workshop"; // Default
// Namespaces for ISO9660 Filesystem
public const string JolietVolumeDescriptor = "joliet"; // Default
public const string PrimaryVolumeDescriptor = "normal";
public const string PrimaryVolumeDescriptorwithEncoding = "romeo";
public const string RockRidge = "rrip";
public const string PrimaryVolumeDescriptorVersionSuffix = "vms";
// Namespaces for Microsoft File Allocation Table
public const string DOS83UpperCase = "dos";
public const string LFNWhenAvailableWithFallback = "ecs"; // Default
public const string LongFileNames = "lfn";
public const string WindowsNT83MixedCase = "nt";
public const string OS2Extended = "os2";
}
/// <summary>
/// Supported options for Aaru
/// </summary>
/// TODO: Use to verify option settings
public static class OptionStrings
{
// Aaru format
public const string AaruCompress = "compress"; // boolean, default true;
public const string AaruDeduplicate = "deduplicate"; // boolean, default true
public const string AaruDictionary = "dictionary"; // number, default 33554432
public const string AaruMaxDDTSize = "max_ddt_size"; // number, default 256
public const string AaruMD5 = "md5"; // boolean, default false
public const string AaruSectorsPerBlock = "sectors_per_block"; // number, default 4096 [power of 2]
public const string AaruSHA1 = "sha1"; // boolean, default false
public const string AaruSHA256 = "sha256"; // boolean, default false
public const string AaruSpamSum = "spamsum"; // boolean, default false
// ACT Apricot Disk Image
public const string ACTApricotDiskImageCompress = "compress"; // boolean, default false
// Apple DiskCopy 4.2
public const string AppleDiskCopyMacOSX = "macosx"; // boolean, default false
// CDRDAO tocfile
public const string CDRDAOTocfileSeparate = "separate"; // boolean, default false
// CDRWin cuesheet
public const string CDRWinCuesheetSeparate = "separate"; // boolean, default false
// ISO9660 Filesystem
public const string ISO9660FSUseEvd = "use_evd"; // boolean, default false
public const string ISO9660FSUsePathTable = "use_path_table"; // boolean, default false
public const string ISO9660FSUseTransTbl = "use_trans_tbl"; // boolean, default false
// VMware disk image
public const string VMwareDiskImageAdapterType = "adapter_type"; // string, default ide [ide, lsilogic, buslogic, legacyESX]
public const string VMwareDiskImageHWVersion = "hwversion"; // number, default 4
public const string VMwareDiskImageSparse = "sparse"; // boolean, default false
public const string VMwareDiskImageSplit = "split"; // boolean, default false
}
}

View File

@@ -0,0 +1,22 @@
using MPF.Data;
namespace MPF.Aaru
{
public static class Converters
{
#region Cross-enumeration conversions
/// <summary>
/// Get the default extension for a given disc type
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>Valid extension (with leading '.'), null on error</returns>
public static string Extension(MediaType? type)
{
// Aaru has a single, unified output format by default
return ".aaruf";
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
// Anything marked as internal can be used by the test methods
[assembly: InternalsVisibleTo("MPF.Test")]

View File

@@ -0,0 +1,304 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Data;
namespace MPF.CleanRip
{
/// <summary>
/// Represents a generic set of CleanRip parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.CleanRip;
#endregion
/// <inheritdoc/>
public Parameters(string parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
List<string> missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}-dumpinfo.txt"))
missingFiles.Add($"{basePath}-dumpinfo.txt");
if (!File.Exists($"{basePath}.bca"))
missingFiles.Add($"{basePath}.bca");
}
break;
default:
return (false, missingFiles);
}
return (!missingFiles.Any(), missingFiles);
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
{
info.TracksAndWriteOffsets.ClrMameProData = GetCleanripDatfile(basePath + ".iso", basePath + "-dumpinfo.txt");
// Get the individual hash data, as per internal
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out string crc32, out string md5, out string sha1))
{
info.SizeAndChecksums.Size = size;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
// Dual-layer discs have the same size and layerbreak
if (size == 8511160320)
info.SizeAndChecksums.Layerbreak = 2084960;
}
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (File.Exists(basePath + ".bca"))
info.Extras.BCA = GetBCA(basePath + ".bca");
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out RedumpRegion? gcRegion, out string gcVersion))
{
info.CommonDiscInfo.Region = gcRegion ?? info.CommonDiscInfo.Region;
info.VersionAndEditions.Version = gcVersion ?? info.VersionAndEditions.Version;
}
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
if (File.Exists(basePath + ".bca"))
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true));
if (File.Exists(basePath + "-dumpinfo.txt"))
info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt"));
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
List<string> logFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (File.Exists($"{basePath}-dumpinfo.txt"))
logFiles.Add($"{basePath}-dumpinfo.txt");
if (File.Exists($"{basePath}.bca"))
logFiles.Add($"{basePath}.bca");
break;
}
return logFiles;
}
#endregion
#region Information Extraction Methods
/// <summary>
/// Get the hex contents of the BCA file
/// </summary>
/// <param name="bcaPath">Path to the BCA file associated with the dump</param>
/// <returns>BCA data as a hex string if possible, null on error</returns>
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
private static string GetBCA(string bcaPath)
{
// If the file doesn't exist, we can't get the info
if (!File.Exists(bcaPath))
return null;
try
{
string hex = GetFullFile(bcaPath, true);
return Regex.Replace(hex, ".{32}", "$0\n");
}
catch
{
// We don't care what the error was right now
return null;
}
}
/// <summary>
/// Get a formatted datfile from the cleanrip output, if possible
/// </summary>
/// <param name="iso">Path to ISO file</param>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <returns></returns>
private static string GetCleanripDatfile(string iso, string dumpinfo)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dumpinfo))
return null;
using (StreamReader sr = File.OpenText(dumpinfo))
{
long size = new FileInfo(iso).Length;
string crc = string.Empty;
string md5 = string.Empty;
string sha1 = string.Empty;
try
{
// Make sure this file is a dumpinfo
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
return null;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line.Substring(5);
else if (line.StartsWith("SHA-1"))
sha1 = line.Substring(7);
}
return $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
/// <summary>
/// Get the extracted GC and Wii version
/// </summary>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <param name="region">Output region, if possible</param>
/// <param name="version">Output internal version of the game</param>
/// <returns></returns>
private static bool GetGameCubeWiiInformation(string dumpinfo, out RedumpRegion? region, out string version)
{
region = null; version = null;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dumpinfo))
return false;
using (StreamReader sr = File.OpenText(dumpinfo))
{
try
{
// Make sure this file is a dumpinfo
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
return false;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("Version"))
{
version = line.Substring(9);
}
else if (line.StartsWith("Filename"))
{
string serial = line.Substring(10);
// char gameType = serial[0];
// string gameid = serial[1] + serial[2];
// string version = serial[4] + serial[5]
switch (serial[3])
{
case 'A':
region = RedumpRegion.World;
break;
case 'D':
region = RedumpRegion.Germany;
break;
case 'E':
region = RedumpRegion.USA;
break;
case 'F':
region = RedumpRegion.France;
break;
case 'I':
region = RedumpRegion.Italy;
break;
case 'J':
region = RedumpRegion.Japan;
break;
case 'K':
region = RedumpRegion.Korea;
break;
case 'L':
region = RedumpRegion.Europe; // Japanese import to Europe
break;
case 'M':
region = RedumpRegion.Europe; // American import to Europe
break;
case 'N':
region = RedumpRegion.USA; // Japanese import to USA
break;
case 'P':
region = RedumpRegion.Europe;
break;
case 'R':
region = RedumpRegion.Russia;
break;
case 'S':
region = RedumpRegion.Spain;
break;
case 'Q':
region = RedumpRegion.Korea; // Korea with Japanese language
break;
case 'T':
region = RedumpRegion.Korea; // Korea with English language
break;
case 'X':
region = null; // Not a real region code
break;
}
}
}
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,238 @@
using System;
using System.Collections.Generic;
using System.IO;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// The audio or data files filetype
/// </summary>
public enum CueFileType
{
/// <summary>
/// Intel binary file (least significant byte first). Use for data files.
/// </summary>
BINARY,
/// <summary>
/// Motorola binary file (most significant byte first). Use for data files.
/// </summary>
MOTOROLA,
/// <summary>
/// Audio AIFF file (44.1KHz 16-bit stereo)
/// </summary>
AIFF,
/// <summary>
/// Audio WAVE file (44.1KHz 16-bit stereo)
/// </summary>
WAVE,
/// <summary>
/// Audio MP3 file (44.1KHz 16-bit stereo)
/// </summary>
MP3,
}
/// <summary>
/// Represents a single FILE in a cuesheet
/// </summary>
public class CueFile
{
/// <summary>
/// filename
/// </summary>
public string FileName { get; set; }
/// <summary>
/// filetype
/// </summary>
public CueFileType FileType { get; set; }
/// <summary>
/// List of TRACK in FILE
/// </summary>
public List<CueTrack> Tracks { get; set; }
/// <summary>
/// Create an empty FILE
/// </summary>
public CueFile()
{
}
/// <summary>
/// Fill a FILE from an array of lines
/// </summary>
/// <param name="fileName">File name to set</param>
/// <param name="fileType">File type to set</param>
/// <param name="cueLines">Lines array to pull from</param>
/// <param name="i">Reference to index in array</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueFile(string fileName, string fileType, string[] cueLines, ref int i, bool throwOnError = false)
{
if (cueLines == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(cueLines));
return;
}
else if (i < 0 || i > cueLines.Length)
{
if (throwOnError)
throw new IndexOutOfRangeException();
return;
}
// Set the current fields
this.FileName = fileName.Trim('"');
this.FileType = GetFileType(fileType);
// Increment to start
i++;
for (; i < cueLines.Length; i++)
{
string line = cueLines[i].Trim();
string[] splitLine = line.Split(' ');
// If we have an empty line, we skip
if (string.IsNullOrWhiteSpace(line))
continue;
switch (splitLine[0])
{
// Read comments
case "REM":
// We ignore all comments for now
break;
// Read track information
case "TRACK":
if (splitLine.Length < 3)
{
if (throwOnError)
throw new FormatException($"TRACK line malformed: {line}");
continue;
}
if (this.Tracks == null)
this.Tracks = new List<CueTrack>();
var track = new CueTrack(splitLine[1], splitLine[2], cueLines, ref i);
if (track == default)
{
if (throwOnError)
throw new FormatException($"TRACK line malformed: {line}");
continue;
}
this.Tracks.Add(track);
break;
// Default means return
default:
i--;
return;
}
}
}
/// <summary>
/// Write the FILE out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public void Write(StreamWriter sw, bool throwOnError = false)
{
// If we don't have any tracks, it's invalid
if (this.Tracks == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(this.Tracks));
return;
}
else if (this.Tracks.Count == 0)
{
if (throwOnError)
throw new ArgumentException("No tracks provided to write");
return;
}
sw.WriteLine($"FILE \"{this.FileName}\" {FromFileType(this.FileType)}");
foreach (var track in Tracks)
{
track.Write(sw);
}
}
/// <summary>
/// Get the file type from a given string
/// </summary>
/// <param name="fileType">String to get value from</param>
/// <returns>CueFileType, if possible</returns>
private CueFileType GetFileType(string fileType)
{
switch (fileType.ToLowerInvariant())
{
case "binary":
return CueFileType.BINARY;
case "motorola":
return CueFileType.MOTOROLA;
case "aiff":
return CueFileType.AIFF;
case "wave":
return CueFileType.WAVE;
case "mp3":
return CueFileType.MP3;
default:
return CueFileType.BINARY;
}
}
/// <summary>
/// Get the string from a given file type
/// </summary>
/// <param name="fileType">CueFileType to get value from</param>
/// <returns>String, if possible (default BINARY)</returns>
private string FromFileType(CueFileType fileType)
{
switch (fileType)
{
case CueFileType.BINARY:
return "BINARY";
case CueFileType.MOTOROLA:
return "MOTOROLA";
case CueFileType.AIFF:
return "AIFF";
case CueFileType.WAVE:
return "WAVE";
case CueFileType.MP3:
return "MP3";
default:
return string.Empty;
}
}
}
}

View File

@@ -0,0 +1,163 @@
using System;
using System.IO;
using System.Linq;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// Represents a single INDEX in a TRACK
/// </summary>
public class CueIndex
{
/// <summary>
/// INDEX number, between 0 and 99
/// </summary>
public int Index { get; set; }
/// <summary>
/// Starting time of INDEX in minutes
/// </summary>
public int Minutes { get; set; }
/// <summary>
/// Starting time of INDEX in seconds
/// </summary>
/// <remarks>There are 60 seconds in a minute</remarks>
public int Seconds { get; set; }
/// <summary>
/// Starting time of INDEX in frames.
/// </summary>
/// <remarks>There are 75 frames per second</remarks>
public int Frames { get; set; }
/// <summary>
/// Create an empty INDEX
/// </summary>
public CueIndex()
{
}
/// <summary>
/// Fill a INDEX from an array of lines
/// </summary>
/// <param name="index">Index to set</param>
/// <param name="startTime">Start time to set</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueIndex(string index, string startTime, bool throwOnError = false)
{
// Set the current fields
if (!int.TryParse(index, out int parsedIndex))
{
if (throwOnError)
throw new ArgumentException($"Index was not a number: {index}");
return;
}
else if (parsedIndex < 0 || parsedIndex > 99)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Index must be between 0 and 99: {parsedIndex}");
return;
}
// Ignore empty lines
if (string.IsNullOrWhiteSpace(startTime))
{
if (throwOnError)
throw new ArgumentException("Start time was null or whitespace");
return;
}
// Ignore lines that don't contain the correct information
if (startTime.Length != 8 || startTime.Count(c => c == ':') != 2)
{
if (throwOnError)
throw new FormatException($"Start time was not in a recognized format: {startTime}");
return;
}
// Split the line
string[] splitTime = startTime.Split(':');
if (splitTime.Length != 3)
{
if (throwOnError)
throw new FormatException($"Start time was not in a recognized format: {startTime}");
return;
}
// Parse the lengths
int[] lengthSegments = new int[3];
// Minutes
if (!int.TryParse(splitTime[0], out lengthSegments[0]))
{
if (throwOnError)
throw new FormatException($"Minutes segment was not a number: {splitTime[0]}");
return;
}
else if (lengthSegments[0] < 0)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
return;
}
// Seconds
if (!int.TryParse(splitTime[1], out lengthSegments[1]))
{
if (throwOnError)
throw new FormatException($"Seconds segment was not a number: {splitTime[1]}");
return;
}
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
return;
}
// Frames
if (!int.TryParse(splitTime[2], out lengthSegments[2]))
{
if (throwOnError)
throw new FormatException($"Frames segment was not a number: {splitTime[2]}");
return;
}
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
return;
}
// Set the values
this.Index = parsedIndex;
this.Minutes = lengthSegments[0];
this.Seconds = lengthSegments[1];
this.Frames = lengthSegments[2];
}
/// <summary>
/// Write the INDEX out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
public void Write(StreamWriter sw)
{
sw.WriteLine($" INDEX {this.Index:D2} {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
}
}
}

View File

@@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// Represents a single cuesheet
/// </summary>
public class CueSheet
{
/// <summary>
/// CATALOG
/// </summary>
public string Catalog { get; set; }
/// <summary>
/// CDTEXTFILE
/// </summary>
public string CdTextFile { get; set; }
/// <summary>
/// PERFORMER
/// </summary>
public string Performer { get; set; }
/// <summary>
/// SONGWRITER
/// </summary>
public string Songwriter { get; set; }
/// <summary>
/// TITLE
/// </summary>
public string Title { get; set; }
/// <summary>
/// List of FILE in cuesheet
/// </summary>
public List<CueFile> Files { get; set; }
/// <summary>
/// Create an empty cuesheet
/// </summary>
public CueSheet()
{
}
/// <summary>
/// Create a cuesheet from a file, if possible
/// </summary>
/// <param name="filename"></param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueSheet(string filename, bool throwOnError = false)
{
// Check that the file exists
if (!File.Exists(filename))
return;
// Check the extension
string ext = Path.GetExtension(filename).TrimStart('.');
if (!string.Equals(ext, "cue", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(ext, "txt", StringComparison.OrdinalIgnoreCase))
{
return;
}
// Open the file and begin reading
string[] cueLines = File.ReadAllLines(filename);
for (int i = 0; i < cueLines.Length; i++)
{
string line = cueLines[i].Trim();
// http://stackoverflow.com/questions/554013/regular-expression-to-split-on-spaces-unless-in-quotes
string[] splitLine = Regex
.Matches(line, @"[^\s""]+|""[^""]*""")
.Cast<Match>()
.Select(m => m.Groups[0].Value)
.ToArray();
// If we have an empty line, we skip
if (string.IsNullOrWhiteSpace(line))
continue;
switch (splitLine[0])
{
// Read comments
case "REM":
// We ignore all comments for now
break;
// Read MCN
case "CATALOG":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"CATALOG line malformed: {line}");
continue;
}
this.Catalog = splitLine[1];
break;
// Read external CD-Text file path
case "CDTEXTFILE":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"CDTEXTFILE line malformed: {line}");
continue;
}
this.CdTextFile = splitLine[1];
break;
// Read CD-Text enhanced performer
case "PERFORMER":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"PERFORMER line malformed: {line}");
continue;
}
this.Performer = splitLine[1];
break;
// Read CD-Text enhanced songwriter
case "SONGWRITER":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"SONGWRITER line malformed: {line}");
continue;
}
this.Songwriter = splitLine[1];
break;
// Read CD-Text enhanced title
case "TITLE":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"TITLE line malformed: {line}");
continue;
}
this.Title = splitLine[1];
break;
// Read file information
case "FILE":
if (splitLine.Length < 3)
{
if (throwOnError)
throw new FormatException($"FILE line malformed: {line}");
continue;
}
if (this.Files == null)
this.Files = new List<CueFile>();
var file = new CueFile(splitLine[1], splitLine[2], cueLines, ref i);
if (file == default)
{
if (throwOnError)
throw new FormatException($"FILE line malformed: {line}");
continue;
}
this.Files.Add(file);
break;
}
}
}
/// <summary>
/// Write the cuesheet out to a file
/// </summary>
/// <param name="filename">File path to write to</param>
public void Write(string filename)
{
using (var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
Write(fs);
}
}
/// <summary>
/// Write the cuesheet out to a stream
/// </summary>
/// <param name="stream">Stream to write to</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public void Write(Stream stream, bool throwOnError = false)
{
// If we don't have any files, it's invalid
if (this.Files == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(this.Files));
return;
}
else if (this.Files.Count == 0)
{
if (throwOnError)
throw new ArgumentException("No files provided to write");
return;
}
using (var sw = new StreamWriter(stream, Encoding.ASCII, 1024, true))
{
if (!string.IsNullOrEmpty(this.Catalog))
sw.WriteLine($"CATALOG {this.Catalog}");
if (!string.IsNullOrEmpty(this.CdTextFile))
sw.WriteLine($"CDTEXTFILE {this.CdTextFile}");
if (!string.IsNullOrEmpty(this.Performer))
sw.WriteLine($"PERFORMER {this.Performer}");
if (!string.IsNullOrEmpty(this.Songwriter))
sw.WriteLine($"SONGWRITER {this.Songwriter}");
if (!string.IsNullOrEmpty(this.Title))
sw.WriteLine($"TITLE {this.Title}");
foreach (var file in Files)
{
file.Write(sw);
}
}
}
}
}

View File

@@ -0,0 +1,554 @@
using System;
using System.Collections.Generic;
using System.IO;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// Track datatype
/// </summary>
public enum CueTrackDataType
{
/// <summary>
/// AUDIO, Audio/Music (2352)
/// </summary>
AUDIO,
/// <summary>
/// CDG, Karaoke CD+G (2448)
/// </summary>
CDG,
/// <summary>
/// MODE1/2048, CD-ROM Mode1 Data (cooked)
/// </summary>
MODE1_2048,
/// <summary>
/// MODE1/2352 CD-ROM Mode1 Data (raw)
/// </summary>
MODE1_2352,
/// <summary>
/// MODE2/2336, CD-ROM XA Mode2 Data
/// </summary>
MODE2_2336,
/// <summary>
/// MODE2/2352, CD-ROM XA Mode2 Data
/// </summary>
MODE2_2352,
/// <summary>
/// CDI/2336, CD-I Mode2 Data
/// </summary>
CDI_2336,
/// <summary>
/// CDI/2352, CD-I Mode2 Data
/// </summary>
CDI_2352,
}
/// <summary>
/// Special subcode flags within a track
/// </summary>
[Flags]
public enum CueTrackFlag
{
/// <summary>
/// DCP, Digital copy permitted
/// </summary>
DCP = 1 << 0,
/// <summary>
/// 4CH, Four channel audio
/// </summary>
FourCH = 1 << 1,
/// <summary>
/// PRE, Pre-emphasis enabled (audio tracks only)
/// </summary>
PRE = 1 << 2,
/// <summary>
/// SCMS, Serial Copy Management System (not supported by all recorders)
/// </summary>
SCMS = 1 << 3,
/// <summary>
/// DATA, set for data files. This flag is set automatically based on the tracks filetype
/// </summary>
DATA = 1 << 4,
}
/// <summary>
/// Represents a single TRACK in a FILE
/// </summary>
public class CueTrack
{
/// <summary>
/// Track number. The range is 1 to 99.
/// </summary>
public int Number { get; set; }
/// <summary>
/// Track datatype
/// </summary>
public CueTrackDataType DataType { get; set; }
/// <summary>
/// FLAGS
/// </summary>
public CueTrackFlag Flags { get; set; }
/// <summary>
/// ISRC
/// </summary>
/// <remarks>12 characters in length</remarks>
public string ISRC { get; set; }
/// <summary>
/// PERFORMER
/// </summary>
public string Performer { get; set; }
/// <summary>
/// SONGWRITER
/// </summary>
public string Songwriter { get; set; }
/// <summary>
/// TITLE
/// </summary>
public string Title { get; set; }
/// <summary>
/// PREGAP
/// </summary>
public PreGap PreGap { get; set; }
/// <summary>
/// List of INDEX in TRACK
/// </summary>
/// <remarks>Must start with 0 or 1 and then sequential</remarks>
public List<CueIndex> Indices { get; set; }
/// <summary>
/// POSTGAP
/// </summary>
public PostGap PostGap { get; set; }
/// <summary>
/// Create an empty TRACK
/// </summary>
public CueTrack()
{
}
/// <summary>
/// Fill a TRACK from an array of lines
/// </summary>
/// <param name="number">Number to set</param>
/// <param name="dataType">Data type to set</param>
/// <param name="cueLines">Lines array to pull from</param>
/// <param name="i">Reference to index in array</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueTrack(string number, string dataType, string[] cueLines, ref int i, bool throwOnError = false)
{
if (cueLines == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(cueLines));
return;
}
else if (i < 0 || i > cueLines.Length)
{
if (throwOnError)
throw new IndexOutOfRangeException();
return;
}
// Set the current fields
if (!int.TryParse(number, out int parsedNumber))
{
if (throwOnError)
throw new ArgumentException($"Number was not a number: {number}");
return;
}
else if (parsedNumber < 1 || parsedNumber > 99)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Index must be between 1 and 99: {parsedNumber}");
return;
}
this.Number = parsedNumber;
this.DataType = GetDataType(dataType);
// Increment to start
i++;
for (; i < cueLines.Length; i++)
{
string line = cueLines[i].Trim();
string[] splitLine = line.Split(' ');
// If we have an empty line, we skip
if (string.IsNullOrWhiteSpace(line))
continue;
switch (splitLine[0])
{
// Read comments
case "REM":
// We ignore all comments for now
break;
// Read flag information
case "FLAGS":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"FLAGS line malformed: {line}");
continue;
}
this.Flags = GetFlags(splitLine);
break;
// Read International Standard Recording Code
case "ISRC":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"ISRC line malformed: {line}");
continue;
}
this.ISRC = splitLine[1];
break;
// Read CD-Text enhanced performer
case "PERFORMER":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"PERFORMER line malformed: {line}");
continue;
}
this.Performer = splitLine[1];
break;
// Read CD-Text enhanced songwriter
case "SONGWRITER":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"SONGWRITER line malformed: {line}");
continue;
}
this.Songwriter = splitLine[1];
break;
// Read CD-Text enhanced title
case "TITLE":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"TITLE line malformed: {line}");
continue;
}
this.Title = splitLine[1];
break;
// Read pregap information
case "PREGAP":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"PREGAP line malformed: {line}");
continue;
}
var pregap = new PreGap(splitLine[1]);
if (pregap == default)
{
if (throwOnError)
throw new FormatException($"PREGAP line malformed: {line}");
continue;
}
this.PreGap = pregap;
break;
// Read index information
case "INDEX":
if (splitLine.Length < 3)
{
if (throwOnError)
throw new FormatException($"INDEX line malformed: {line}");
continue;
}
if (this.Indices == null)
this.Indices = new List<CueIndex>();
var index = new CueIndex(splitLine[1], splitLine[2]);
if (index == default)
{
if (throwOnError)
throw new FormatException($"INDEX line malformed: {line}");
continue;
}
this.Indices.Add(index);
break;
// Read postgap information
case "POSTGAP":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"POSTGAP line malformed: {line}");
continue;
}
var postgap = new PostGap(splitLine[1]);
if (postgap == default)
{
if (throwOnError)
throw new FormatException($"POSTGAP line malformed: {line}");
continue;
}
this.PostGap = postgap;
break;
// Default means return
default:
i--;
return;
}
}
}
/// <summary>
/// Write the TRACK out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public void Write(StreamWriter sw, bool throwOnError = false)
{
// If we don't have any indices, it's invalid
if (this.Indices == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(this.Indices));
return;
}
else if (this.Indices.Count == 0)
{
if (throwOnError)
throw new ArgumentException("No indices provided to write");
return;
}
sw.WriteLine($" TRACK {this.Number:D2} {FromDataType(this.DataType)}");
if (this.Flags != 0)
sw.WriteLine($" FLAGS {FromFlags(this.Flags)}");
if (!string.IsNullOrEmpty(this.ISRC))
sw.WriteLine($"ISRC {this.ISRC}");
if (!string.IsNullOrEmpty(this.Performer))
sw.WriteLine($"PERFORMER {this.Performer}");
if (!string.IsNullOrEmpty(this.Songwriter))
sw.WriteLine($"SONGWRITER {this.Songwriter}");
if (!string.IsNullOrEmpty(this.Title))
sw.WriteLine($"TITLE {this.Title}");
if (this.PreGap != null)
this.PreGap.Write(sw);
foreach (var index in Indices)
{
index.Write(sw);
}
if (this.PostGap != null)
this.PostGap.Write(sw);
}
/// <summary>
/// Get the data type from a given string
/// </summary>
/// <param name="dataType">String to get value from</param>
/// <returns>CueTrackDataType, if possible (default AUDIO)</returns>
private CueTrackDataType GetDataType(string dataType)
{
switch (dataType.ToLowerInvariant())
{
case "audio":
return CueTrackDataType.AUDIO;
case "cdg":
return CueTrackDataType.CDG;
case "mode1/2048":
return CueTrackDataType.MODE1_2048;
case "mode1/2352":
return CueTrackDataType.MODE1_2352;
case "mode2/2336":
return CueTrackDataType.MODE2_2336;
case "mode2/2352":
return CueTrackDataType.MODE2_2352;
case "cdi/2336":
return CueTrackDataType.CDI_2336;
case "cdi/2352":
return CueTrackDataType.CDI_2352;
default:
return CueTrackDataType.AUDIO;
}
}
/// <summary>
/// Get the string from a given data type
/// </summary>
/// <param name="dataType">CueTrackDataType to get value from</param>
/// <returns>string, if possible</returns>
private string FromDataType(CueTrackDataType dataType)
{
switch (dataType)
{
case CueTrackDataType.AUDIO:
return "AUDIO";
case CueTrackDataType.CDG:
return "CDG";
case CueTrackDataType.MODE1_2048:
return "MODE1/2048";
case CueTrackDataType.MODE1_2352:
return "MODE1/2352";
case CueTrackDataType.MODE2_2336:
return "MODE2/2336";
case CueTrackDataType.MODE2_2352:
return "MODE2/2352";
case CueTrackDataType.CDI_2336:
return "CDI/2336";
case CueTrackDataType.CDI_2352:
return "CDI/2352";
default:
return string.Empty;
}
}
/// <summary>
/// Get the flag value for an array of strings
/// </summary>
/// <param name="flagStrings">Possible flags as strings</param>
/// <returns>CueTrackFlag value representing the strings, if possible</returns>
private CueTrackFlag GetFlags(string[] flagStrings)
{
CueTrackFlag flag = 0;
foreach (string flagString in flagStrings)
{
switch (flagString.ToLowerInvariant())
{
case "flags":
// No-op since this is the start of the line
break;
case "dcp":
flag |= CueTrackFlag.DCP;
break;
case "4ch":
flag |= CueTrackFlag.FourCH;
break;
case "pre":
flag |= CueTrackFlag.PRE;
break;
case "scms":
flag |= CueTrackFlag.SCMS;
break;
case "data":
flag |= CueTrackFlag.DATA;
break;
}
}
return flag;
}
/// <summary>
/// Get the string value for a set of track flags
/// </summary>
/// <param name="flags">CueTrackFlag to get value from</param>
/// <returns>String value representing the CueTrackFlag, if possible</returns>
private string FromFlags(CueTrackFlag flags)
{
string outputFlagString = string.Empty;
if (flags.HasFlag(CueTrackFlag.DCP))
outputFlagString += "DCP ";
if (flags.HasFlag(CueTrackFlag.FourCH))
outputFlagString += "4CH ";
if (flags.HasFlag(CueTrackFlag.PRE))
outputFlagString += "PRE ";
if (flags.HasFlag(CueTrackFlag.SCMS))
outputFlagString += "SCMS ";
if (flags.HasFlag(CueTrackFlag.DATA))
outputFlagString += "DATA ";
return outputFlagString.Trim();
}
}
}

View File

@@ -0,0 +1,139 @@
using System;
using System.IO;
using System.Linq;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// Represents POSTGAP information of a track
/// </summary>
public class PostGap
{
/// <summary>
/// Length of POSTGAP in minutes
/// </summary>
public int Minutes { get; set; }
/// <summary>
/// Length of POSTGAP in seconds
/// </summary>
/// <remarks>There are 60 seconds in a minute</remarks>
public int Seconds { get; set; }
/// <summary>
/// Length of POSTGAP in frames.
/// </summary>
/// <remarks>There are 75 frames per second</remarks>
public int Frames { get; set; }
/// Create an empty POSTGAP
/// </summary>
public PostGap()
{
}
/// <summary>
/// Create a POSTGAP from a mm:ss:ff length
/// </summary>
/// <param name="length">String to get length information from</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public PostGap(string length, bool throwOnError = false)
{
// Ignore empty lines
if (string.IsNullOrWhiteSpace(length))
{
if (throwOnError)
throw new ArgumentException("Length was null or whitespace");
return;
}
// Ignore lines that don't contain the correct information
if (length.Length != 8 || length.Count(c => c == ':') != 2)
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Split the line
string[] splitLength = length.Split(':');
if (splitLength.Length != 3)
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Parse the lengths
int[] lengthSegments = new int[3];
// Minutes
if (!int.TryParse(splitLength[0], out lengthSegments[0]))
{
if (throwOnError)
throw new FormatException($"Minutes segment was not a number: {splitLength[0]}");
return;
}
else if (lengthSegments[0] < 0)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
return;
}
// Seconds
if (!int.TryParse(splitLength[1], out lengthSegments[1]))
{
if (throwOnError)
throw new FormatException($"Seconds segment was not a number: {splitLength[1]}");
return;
}
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
return;
}
// Frames
if (!int.TryParse(splitLength[2], out lengthSegments[2]))
{
if (throwOnError)
throw new FormatException($"Frames segment was not a number: {splitLength[2]}");
return;
}
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
return;
}
// Set the values
this.Minutes = lengthSegments[0];
this.Seconds = lengthSegments[1];
this.Frames = lengthSegments[2];
}
/// <summary>
/// Write the POSTGAP out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
public void Write(StreamWriter sw)
{
sw.WriteLine($" POSTGAP {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
}
}
}

View File

@@ -0,0 +1,140 @@
using System;
using System.IO;
using System.Linq;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// Represents PREGAP information of a track
/// </summary>
public class PreGap
{
/// <summary>
/// Length of PREGAP in minutes
/// </summary>
public int Minutes { get; set; }
/// <summary>
/// Length of PREGAP in seconds
/// </summary>
/// <remarks>There are 60 seconds in a minute</remarks>
public int Seconds { get; set; }
/// <summary>
/// Length of PREGAP in frames.
/// </summary>
/// <remarks>There are 75 frames per second</remarks>
public int Frames { get; set; }
/// <summary>
/// Create an empty PREGAP
/// </summary>
public PreGap()
{
}
/// <summary>
/// Create a PREGAP from a mm:ss:ff length
/// </summary>
/// <param name="length">String to get length information from</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public PreGap(string length, bool throwOnError = false)
{
// Ignore empty lines
if (string.IsNullOrWhiteSpace(length))
{
if (throwOnError)
throw new ArgumentException("Length was null or whitespace");
return;
}
// Ignore lines that don't contain the correct information
if (length.Length != 8 || length.Count(c => c == ':') != 2)
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Split the line
string[] splitLength = length.Split(':');
if (splitLength.Length != 3)
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Parse the lengths
int[] lengthSegments = new int[3];
// Minutes
if (!int.TryParse(splitLength[0], out lengthSegments[0]))
{
if (throwOnError)
throw new FormatException($"Minutes segment was not a number: {splitLength[0]}");
return;
}
else if (lengthSegments[0] < 0)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
return;
}
// Seconds
if (!int.TryParse(splitLength[1], out lengthSegments[1]))
{
if (throwOnError)
throw new FormatException($"Seconds segment was not a number: {splitLength[1]}");
return;
}
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
return;
}
// Frames
if (!int.TryParse(splitLength[2], out lengthSegments[2]))
{
if (throwOnError)
throw new FormatException($"Frames segment was not a number: {splitLength[2]}");
return;
}
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
return;
}
// Set the values
this.Minutes = lengthSegments[0];
this.Seconds = lengthSegments[1];
this.Frames = lengthSegments[2];
}
/// <summary>
/// Write the PREGAP out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
public void Write(StreamWriter sw)
{
sw.WriteLine($" PREGAP {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
}
}
}

View File

@@ -0,0 +1,32 @@
namespace MPF.DD
{
/// <summary>
/// Top-level commands for DD
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
public const string List = "--list";
}
/// <summary>
/// Dumping flags for DD
/// </summary>
public static class FlagStrings
{
// Boolean flags
public const string Progress = "--progress";
public const string Size = "--size";
// Int64 flags
public const string BlockSize = "bs";
public const string Count = "count";
public const string Seek = "seek";
public const string Skip = "skip";
// String flags
public const string Filter = "--filter";
public const string InputFile = "if";
public const string OutputFile = "of";
}
}

View File

@@ -0,0 +1,22 @@
using MPF.Data;
namespace MPF.DD
{
public static class Converters
{
#region Cross-enumeration conversions
/// <summary>
/// Get the default extension for a given disc type
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>Valid extension (with leading '.'), null on error</returns>
public static string Extension(MediaType? type)
{
// DD has a single, unified output format by default
return ".bin";
}
#endregion
}
}

View File

@@ -0,0 +1,401 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Data;
namespace MPF.DD
{
/// <summary>
/// Represents a generic set of DD parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Generic Dumping Information
/// <inheritdoc/>
public override string InputPath => InputFileValue?.TrimStart('\\', '?');
/// <inheritdoc/>
public override string OutputPath => OutputFileValue;
/// <inheritdoc/>
/// <inheritdoc/>
public override int? Speed
{
get { return 1; }
set { }
}
#endregion
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.DD;
#endregion
#region Flag Values
public long? BlockSizeValue { get; set; }
public long? CountValue { get; set; }
// fixed, removable, disk, partition
public string FilterValue { get; set; }
public string InputFileValue { get; set; }
public string OutputFileValue { get; set; }
public long? SeekValue { get; set; }
public long? SkipValue { get; set; }
#endregion
/// <inheritdoc/>
public Parameters(string parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
// TODO: Figure out what sort of output files are expected... just `.bin`?
return (true, new List<string>());
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
{
// TODO: Fill in submission info specifics for DD
string outputDirectory = Path.GetDirectoryName(basePath);
switch (this.Type)
{
// Determine type-specific differences
}
switch (this.System)
{
case KnownSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out RedumpRegion? pythonTwoRegion, out string pythonTwoDate))
{
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {pythonTwoSerial}\n";
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out RedumpRegion? playstationRegion, out string playstationDate))
{
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {playstationSerial}\n";
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
}
info.CopyProtection.AntiModchip = GetPlayStationAntiModchipDetected(drive?.Letter) ? YesNo.Yes : YesNo.No;
break;
case KnownSystem.SonyPlayStation2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out RedumpRegion? playstationTwoRegion, out string playstationTwoDate))
{
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {playstationTwoSerial}\n";
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
}
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation4:
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation5:
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
break;
}
}
/// <inheritdoc/>
public override string GenerateParameters()
{
List<string> parameters = new List<string>();
if (BaseCommand == null)
BaseCommand = CommandStrings.NONE;
if (!string.IsNullOrEmpty(BaseCommand))
parameters.Add(BaseCommand);
#region Boolean flags
// Progress
if (IsFlagSupported(FlagStrings.Progress))
{
if (this[FlagStrings.Progress] == true)
parameters.Add($"{FlagStrings.Progress}");
}
// Size
if (IsFlagSupported(FlagStrings.Size))
{
if (this[FlagStrings.Size] == true)
parameters.Add($"{FlagStrings.Size}");
}
#endregion
#region Int64 flags
// Block Size
if (IsFlagSupported(FlagStrings.BlockSize))
{
if (this[FlagStrings.BlockSize] == true && BlockSizeValue != null)
parameters.Add($"{FlagStrings.BlockSize}={BlockSizeValue}");
}
// Count
if (IsFlagSupported(FlagStrings.Count))
{
if (this[FlagStrings.Count] == true && CountValue != null)
parameters.Add($"{FlagStrings.Count}={CountValue}");
}
// Seek
if (IsFlagSupported(FlagStrings.Seek))
{
if (this[FlagStrings.Seek] == true && SeekValue != null)
parameters.Add($"{FlagStrings.Seek}={SeekValue}");
}
// Skip
if (IsFlagSupported(FlagStrings.Skip))
{
if (this[FlagStrings.Skip] == true && SkipValue != null)
parameters.Add($"{FlagStrings.Skip}={SkipValue}");
}
#endregion
#region String flags
// Filter
if (IsFlagSupported(FlagStrings.Filter))
{
if (this[FlagStrings.Filter] == true && FilterValue != null)
parameters.Add($"{FlagStrings.Filter}={FilterValue}");
}
// Input File
if (IsFlagSupported(FlagStrings.InputFile))
{
if (this[FlagStrings.InputFile] == true && InputFileValue != null)
parameters.Add($"{FlagStrings.InputFile}=\"{InputFileValue}\"");
else
return null;
}
// Output File
if (IsFlagSupported(FlagStrings.OutputFile))
{
if (this[FlagStrings.OutputFile] == true && OutputFileValue != null)
parameters.Add($"{FlagStrings.OutputFile}=\"{OutputFileValue}\"");
else
return null;
}
#endregion
return string.Join(" ", parameters);
}
/// <inheritdoc/>
public override Dictionary<string, List<string>> GetCommandSupport()
{
return new Dictionary<string, List<string>>()
{
[CommandStrings.NONE] = new List<string>()
{
FlagStrings.BlockSize,
FlagStrings.Count,
FlagStrings.Filter,
FlagStrings.InputFile,
FlagStrings.OutputFile,
FlagStrings.Progress,
FlagStrings.Seek,
FlagStrings.Size,
FlagStrings.Skip,
},
[CommandStrings.List] = new List<string>()
{
},
};
}
/// <inheritdoc/>
public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
/// <inheritdoc/>
public override bool IsDumpingCommand()
{
switch (this.BaseCommand)
{
case CommandStrings.List:
return false;
default:
return true;
}
}
/// <inheritdoc/>
protected override void ResetValues()
{
BaseCommand = CommandStrings.NONE;
flags = new Dictionary<string, bool?>();
BlockSizeValue = null;
CountValue = null;
InputFileValue = null;
OutputFileValue = null;
SeekValue = null;
SkipValue = null;
}
/// <inheritdoc/>
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
{
BaseCommand = CommandStrings.NONE;
this[FlagStrings.InputFile] = true;
InputFileValue = $"\\\\?\\{driveLetter}:";
this[FlagStrings.OutputFile] = true;
OutputFileValue = filename;
// TODO: Add more common block sizes
this[FlagStrings.BlockSize] = true;
switch (this.Type)
{
case MediaType.FloppyDisk:
BlockSizeValue = 1440 * 1024;
break;
default:
BlockSizeValue = 1024 * 1024 * 1024;
break;
}
this[FlagStrings.Progress] = true;
this[FlagStrings.Size] = true;
}
/// <inheritdoc/>
protected override bool ValidateAndSetParameters(string parameters)
{
BaseCommand = CommandStrings.NONE;
// The string has to be valid by itself first
if (string.IsNullOrWhiteSpace(parameters))
return false;
// Now split the string into parts for easier validation
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
parameters = parameters.Trim();
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+")
.Cast<Match>()
.Select(m => m.Value)
.ToList();
// Determine what the commandline should look like given the first item
int start = 0;
if (parts[0] == CommandStrings.List)
{
BaseCommand = parts[0];
start = 1;
}
// Loop through all auxilary flags, if necessary
for (int i = start; i < parts.Count; i++)
{
// Flag read-out values
long? longValue = null;
string stringValue = null;
// Keep a count of keys to determine if we should break out to filename handling or not
int keyCount = Keys.Count();
#region Boolean flags
// Progress
ProcessFlagParameter(parts, FlagStrings.Progress, ref i);
// Size
ProcessFlagParameter(parts, FlagStrings.Size, ref i);
#endregion
#region Int64 flags
// Block Size
longValue = ProcessInt64Parameter(parts, FlagStrings.BlockSize, ref i);
if (longValue != null)
BlockSizeValue = longValue;
// Count
longValue = ProcessInt64Parameter(parts, FlagStrings.Count, ref i);
if (longValue != null)
CountValue = longValue;
// Seek
longValue = ProcessInt64Parameter(parts, FlagStrings.Seek, ref i);
if (longValue != null)
SeekValue = longValue;
// Skip
longValue = ProcessInt64Parameter(parts, FlagStrings.Skip, ref i);
if (longValue != null)
SkipValue = longValue;
#endregion
#region String flags
// Filter (fixed, removable, disk, partition)
stringValue = ProcessStringParameter(parts, FlagStrings.Filter, ref i);
if (!string.IsNullOrEmpty(stringValue))
FilterValue = stringValue;
// Input File
stringValue = ProcessStringParameter(parts, FlagStrings.InputFile, ref i);
if (!string.IsNullOrEmpty(stringValue))
InputFileValue = stringValue;
// Output File
stringValue = ProcessStringParameter(parts, FlagStrings.OutputFile, ref i);
if (!string.IsNullOrEmpty(stringValue))
OutputFileValue = stringValue;
#endregion
}
return true;
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,115 @@
using System.Collections.Generic;
using System.Linq;
namespace MPF.Data
{
/// <summary>
/// Constant values for UI
/// </summary>
public static class Interface
{
// Button values
public const string StartDumping = "Start Dumping";
public const string StopDumping = "Stop Dumping";
// Byte arrays for signatures
public static readonly byte[] SaturnSectorZeroStart = new byte[] { 0x53, 0x45, 0x47, 0x41, 0x20, 0x53, 0x45, 0x47, 0x41, 0x53, 0x41, 0x54, 0x55, 0x52, 0x4E, 0x20 };
// Private lists of known drive speed ranges
private static IReadOnlyList<int> cd { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
private static IReadOnlyList<int> dvd { get; } = cd.Where(s => s <= 24).ToList();
private static IReadOnlyList<int> bd { get; } = cd.Where(s => s <= 16).ToList();
private static IReadOnlyList<int> unknown { get; } = new List<int> { 1 };
/// <summary>
/// Get list of all drive speeds for a given MediaType
/// </summary>
/// <param name="type">MediaType? that represents the current item</param>
/// <returns>Read-only list of drive speeds</returns>
public static IReadOnlyList<int> GetSpeedsForMediaType(MediaType? type)
{
switch (type)
{
case MediaType.CDROM:
case MediaType.GDROM:
return cd;
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
return dvd;
case MediaType.BluRay:
return bd;
default:
return unknown;
}
}
}
/// <summary>
/// Template field values for submission info
/// </summary>
public static class Template
{
// Manual information
public const string TitleField = "Title";
public const string ForeignTitleField = "Foreign Title (Non-latin)";
public const string DiscNumberField = "Disc Number / Letter";
public const string DiscTitleField = "Disc Title";
public const string SystemField = "System";
public const string MediaTypeField = "Media Type";
public const string CategoryField = "Category";
public const string RegionField = "Region";
public const string LanguagesField = "Languages";
public const string PlaystationLanguageSelectionViaField = "Language Selection Via";
public const string DiscSerialField = "Disc Serial";
public const string BarcodeField = "Barcode";
public const string CommentsField = "Comments";
public const string ContentsField = "Contents";
public const string VersionField = "Version";
public const string EditionField = "Edition/Release";
public const string PlayStation3WiiDiscKeyField = "Disc Key";
public const string PlayStation3DiscIDField = "Disc ID";
public const string GameCubeWiiBCAField = "BCA";
public const string CopyProtectionField = "Copy Protection";
public const string MasteringRingField = "Mastering Code (laser branded/etched)";
public const string MasteringSIDField = "Mastering SID Code";
public const string MouldSIDField = "Mould SID Code";
public const string AdditionalMouldField = "Additional Mould";
public const string ToolstampField = "Toolstamp or Mastering Code (engraved/stamped)";
// Automatic Information
public const string PVDField = "Primary Volume Descriptor (PVD)";
public const string DATField = "DAT";
public const string SizeField = "Size";
public const string CRC32Field = "CRC32";
public const string MD5Field = "MD5";
public const string SHA1Field = "SHA1";
public const string MatchingIDsField = "Matching IDs";
public const string ErrorCountField = "Error Count";
public const string CuesheetField = "Cuesheet";
public const string SubIntentionField = "SubIntention Data (SecuROM/LibCrypt)";
public const string WriteOffsetField = "Write Offset";
public const string LayerbreakField = "Layerbreak";
public const string EXEDateBuildDate = "EXE/Build Date";
public const string HeaderField = "Header";
public const string PICField = "Permanent Information & Control (PIC)";
public const string PlayStationEDCField = "EDC";
public const string PlayStationAntiModchipField = "Anti-modchip";
public const string PlayStationLibCryptField = "LibCrypt";
public const string XBOXDMIHash = "DMI.bin Hashes";
public const string XBOXPFIHash = "PFI.bin Hashes";
public const string XBOXSSHash = "SS.bin Hashes";
public const string XBOXSSRanges = "Security Sector Ranges";
public const string XBOXSSVersion = "Security Sector Version";
// Default values
public const string RequiredValue = "(REQUIRED)";
public const string RequiredIfExistsValue = "(REQUIRED, IF EXISTS)";
public const string OptionalValue = "(OPTIONAL)";
public const string DiscNotDetected = "Disc Not Detected";
}
}

106
MPF.Library/Data/Drive.cs Normal file
View File

@@ -0,0 +1,106 @@
using System.IO;
namespace MPF.Data
{
/// <summary>
/// Represents information for a single drive
/// </summary>
public class Drive
{
/// <summary>
/// Represents drive type
/// </summary>
public InternalDriveType? InternalDriveType { get; set; }
/// <summary>
/// Drive partition format
/// </summary>
public string DriveFormat { get { return driveInfo.DriveFormat; } }
/// <summary>
/// Windows drive letter
/// </summary>
public char Letter { get { return driveInfo?.Name[0] ?? '\0'; } }
/// <summary>
/// Represents if Windows has marked the drive as active
/// </summary>
public bool MarkedActive { get { return driveInfo.IsReady; } }
/// <summary>
/// Media label as read by Windows
/// </summary>
public string VolumeLabel
{
get
{
string volumeLabel = Template.DiscNotDetected;
if (driveInfo.IsReady)
{
if (string.IsNullOrWhiteSpace(driveInfo.VolumeLabel))
volumeLabel = "track";
else
volumeLabel = driveInfo.VolumeLabel;
}
foreach (char c in Path.GetInvalidFileNameChars())
volumeLabel = volumeLabel.Replace(c, '_');
return volumeLabel;
}
}
/// <summary>
/// DriveInfo object representing the drive, if possible
/// </summary>
private readonly DriveInfo driveInfo;
public Drive(InternalDriveType? driveType, DriveInfo driveInfo)
{
this.InternalDriveType = driveType;
this.driveInfo = driveInfo;
}
/// <summary>
/// Read a sector with a specified size from the drive
/// </summary>
/// <param name="num">Sector number, non-negative</param>
/// <param name="size">Size of a sector in bytes</param>
/// <returns>Byte array representing the sector, null on error</returns>
public byte[] ReadSector(long num, int size = 2048)
{
// Missing drive leter is not supported
if (string.IsNullOrEmpty(this.driveInfo?.Name))
return null;
// We don't support negative sectors
if (num < 0)
return null;
// Wrap the following in case of device access errors
Stream fs = null;
try
{
// Open the drive as a device
fs = File.OpenRead($"\\\\?\\{this.Letter}:");
// Seek to the start of the sector, if possible
long start = num * size;
fs.Seek(start, SeekOrigin.Begin);
// Read and return the sector
byte[] buffer = new byte[size];
fs.Read(buffer, 0, size);
return buffer;
}
catch
{
return null;
}
finally
{
fs?.Dispose();
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,777 @@
using System;
namespace MPF.Data
{
/// <summary>
/// Available hashing types
/// </summary>
[Flags]
public enum Hash
{
CRC = 1 << 0,
MD5 = 1 << 1,
SHA1 = 1 << 2,
SHA256 = 1 << 3,
SHA384 = 1 << 4,
SHA512 = 1 << 5,
// Special combinations
Standard = CRC | MD5 | SHA1,
All = CRC | MD5 | SHA1 | SHA256 | SHA384 | SHA512,
}
/// <summary>
/// Drive type for dumping
/// </summary>
public enum InternalDriveType
{
Optical,
Floppy,
HardDisk,
Removable,
}
/// <summary>
/// Program that is being used to dump media
/// </summary>
public enum InternalProgram
{
NONE = 0,
// Dumping support
Aaru,
DD,
DiscImageCreator,
// Verification support only
CleanRip,
DCDumper,
UmdImageCreator,
}
/// <summary>
/// Known systems
/// </summary>
public enum KnownSystem
{
NONE = 0,
#region Disc-Based Consoles
AtariJaguarCD,
BandaiPlaydiaQuickInteractiveSystem,
BandaiApplePippin,
CommodoreAmigaCD32,
CommodoreAmigaCDTV,
EnvizionsEVOSmartConsole,
FujitsuFMTownsMarty,
HasbroVideoNow,
HasbroVideoNowColor,
HasbroVideoNowJr,
HasbroVideoNowXP,
MattelFisherPriceiXL,
MattelHyperscan,
MicrosoftXBOX,
MicrosoftXBOX360,
MicrosoftXBOXOne,
MicrosoftXboxSeriesXS,
NECPCEngineTurboGrafxCD,
NECPCFX,
NintendoGameCube,
NintendoSonySuperNESCDROMSystem,
NintendoWii,
NintendoWiiU,
Panasonic3DOInteractiveMultiplayer, // The 3DO Company 3DO Interactive Multiplayer
PhilipsCDi,
PioneerLaserActive,
SegaCDMegaCD,
SegaDreamcast,
SegaSaturn,
SNKNeoGeoCD,
SonyPlayStation,
SonyPlayStation2,
SonyPlayStation3,
SonyPlayStation4,
SonyPlayStation5,
SonyPlayStationPortable,
TandyMemorexVisualInformationSystem,
VMLabsNuon,
VTechVFlashVSmilePro,
ZAPiTGamesGameWaveFamilyEntertainmentSystem,
MarkerDiscBasedConsoleEnd,
#endregion
#region Cartridge-Based and Other Consoles
/*
AmstradGX4000,
APFMicrocomputerSystem,
Atari2600VCS,
Atari5200,
Atari7800,
AtariJaguar,
AtariXEVideoGameSystem,
Audiosonic1292AdvancedProgrammableVideoSystem,
BallyAstrocade,
BitCorporationDina,
CasioLoopy,
CasioPV1000,
Commodore64GamesSystem,
DaewooElectronicsZemmix,
EmersonArcadia2001,
EpochCassetteVision,
EpochSuperCassetteVision,
FairchildChannelF,
FuntechSuperACan,
GeneralConsumerElectricVectrex,
HeberBBCBridgeCompanion,
IntertonVC4000,
JungleTacVii,
LeapFrogClickStart,
LJNVideoArt,
MagnavoxOdyssey2,
MattelIntellivision,
NECPCEngineTurboGrafx16,
NichibutsuMyVision,
Nintendo64,
Nintendo64DD,
NintendoFamilyComputerNintendoEntertainmentSystem,
NintendoFamilyComputerDiskSystem,
NintendoSuperFamicomSuperNintendoEntertainmentSystem,
NintendoSwitch,
PhilipsVideopacPlusG7400,
RCAStudioII,
Sega32X,
SegaMarkIIIMasterSystem,
SegaMegaDriveGenesis,
SegaSG1000,
SNKNeoGeo,
SSDCOMPANYLIMITEDXaviXPORT,
ViewMasterInteractiveVision,
VTechCreatiVision,
VTechVSmile,
VTechSocrates,
WorldsOfWonderActionMax,
MarkerOtherConsoleEnd,
*/
#endregion
#region Computers
AcornArchimedes,
AppleMacintosh,
CommodoreAmiga,
FujitsuFMTowns,
IBMPCCompatible,
NECPC88,
NECPC98,
SharpX68000,
MarkerComputerEnd,
#endregion
#region Arcade
AmigaCUBOCD32,
AmericanLaserGames3DO,
Atari3DO,
Atronic,
AUSCOMSystem1,
BallyGameMagic,
CapcomCPSystemIII,
funworldPhotoPlay,
GlobalVRVarious,
GlobalVRVortek,
GlobalVRVortekV3,
ICEPCHardware,
IncredibleTechnologiesEagle,
IncredibleTechnologiesVarious,
KonamieAmusement,
KonamiFirebeat,
KonamiGVSystem,
KonamiM2,
KonamiPython,
KonamiPython2,
KonamiSystem573,
KonamiTwinkle,
KonamiVarious,
MeritIndustriesBoardwalk,
MeritIndustriesMegaTouchForce,
MeritIndustriesMegaTouchION,
MeritIndustriesMegaTouchMaxx,
MeritIndustriesMegaTouchXL,
NamcoCapcomSystem256,
NamcoCapcomTaitoSystem246,
NamcoSegaNintendoTriforce,
NamcoSystem12,
NamcoSystem357,
NewJatreCDi,
NichibutsuHighRateSystem,
NichibutsuSuperCD,
NichibutsuXRateSystem,
PanasonicM2,
PhotoPlayVarious,
RawThrillsVarious,
SegaChihiro,
SegaEuropaR,
SegaLindbergh,
SegaNaomi,
SegaNaomi2,
SegaNu,
SegaRingEdge,
SegaRingEdge2,
SegaRingWide,
SegaTitanVideo,
SegaSystem32,
SeibuCATSSystem,
TABAustriaQuizard,
TsunamiTsuMoMultiGameMotionSystem,
MarkerArcadeEnd,
#endregion
#region Other
AudioCD,
BDVideo,
DVDAudio,
DVDVideo,
EnhancedCD,
HDDVDVideo,
NavisoftNaviken21,
PalmOS,
PhotoCD,
PlayStationGameSharkUpdates,
RainbowDisc,
SegaPrologue21,
SuperAudioCD,
TaoiKTV,
TomyKissSite,
VideoCD,
MarkerOtherEnd,
#endregion
}
/// <summary>
/// Known system category
/// </summary>
public enum KnownSystemCategory
{
DiscBasedConsole = 0,
OtherConsole,
Computer,
Arcade,
Other,
Custom
};
/// <summary>
/// Known media types
/// </summary>
public enum MediaType
{
NONE = 0,
#region Punched Media
ApertureCard,
JacquardLoomCard,
MagneticStripeCard,
OpticalPhonecard,
PunchedCard,
PunchedTape,
#endregion
#region Tape
Cassette,
DataCartridge,
OpenReel,
#endregion
#region Disc / Disc
BluRay,
CDROM,
DVD,
FloppyDisk,
Floptical,
GDROM,
HDDVD,
HardDisk,
IomegaBernoulliDisk,
IomegaJaz,
IomegaZip,
LaserDisc, // LD-ROM and LV-ROM variants
Nintendo64DD,
NintendoFamicomDiskSystem,
NintendoGameCubeGameDisc,
NintendoWiiOpticalDisc,
NintendoWiiUOpticalDisc,
UMD,
#endregion
// Unsorted Formats
Cartridge,
CED,
CompactFlash,
MMC,
SDCard,
FlashDrive,
}
/// <summary>
/// Physical media types
/// </summary>
/// <see cref="https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cimwin32a/win32-physicalmedia"/>
public enum PhysicalMediaType : ushort
{
Unknown = 0,
Other = 1,
TapeCartridge = 2,
QICCartridge = 3,
AITCartridge = 4,
DTFCartridge = 5,
DATCartridge = 6,
EightMillimeterTapeCartridge = 7,
NineteenMillimeterTapeCartridge = 8,
DLTCartridge = 9,
HalfInchMagneticTapeCartridge = 10,
CartridgeDisk = 11,
JAZDisk = 12,
ZIPDisk = 13,
SyQuestDisk = 14,
WinchesterRemovableDisk = 15,
CDROM = 16,
CDROMXA = 17,
CDI = 18,
CDRecordable = 19,
WORM = 20,
MagnetoOptical = 21,
DVD = 22,
DVDPlusRW = 23,
DVDRAM = 24,
DVDROM = 25,
DVDVideo = 26,
Divx = 27,
FloppyDiskette = 28,
HardDisk = 29,
MemoryCard = 30,
HardCopy = 31,
ClikDisk = 32,
CDRW = 33,
CDDA = 34,
CDPlus = 35,
DVDRecordable = 36,
DVDMinusRW = 37,
DVDAudio = 38,
DVD5 = 39,
DVD9 = 40,
DVD10 = 41,
DVD18 = 42,
MagnetoOpticalRewriteable = 43,
MagnetoOpticalWriteOnce = 44,
MagnetoOpticalRewriteableLIMDOW = 45,
PhaseChangeWriteOnce = 46,
PhaseChangeRewriteable = 47,
PhaseChangeDualRewriteable = 48,
AblativeWriteOnce = 49,
NearFieldRecording = 50,
MiniQic = 51,
Travan = 52,
EightMillimeterMetalParticle = 53,
EightMillimeterAdvancedMetalEvaporate = 54,
NCTP = 55,
LTOUltrium = 56,
LTOAccelis = 57,
NineTrackTape = 58,
EighteenTrackTape = 59,
ThirtySixTrackTape = 60,
Magstar3590 = 61,
MagstarMP = 62,
D2Tape = 63,
TapeDSTSmall = 64,
TapeDSTMedium = 65,
TapeDSTLarge = 66,
}
/// <summary>
/// Redump disc category
/// </summary>
public enum RedumpDiscCategory
{
Games = 1,
Demos = 2,
Video = 3,
Audio = 4,
Multimedia = 5,
Applications = 6,
Coverdiscs = 7,
Educational = 8,
BonusDiscs = 9,
Preproduction = 10,
AddOns = 11,
}
/// <summary>
/// Redump dump status
/// </summary>
public enum RedumpDumpStatus
{
BadDumpRed = 2,
PossibleBadDumpYellow = 3,
OriginalMediaBlue = 4,
TwoOrMoreDumpsGreen = 5,
}
/// <summary>
/// Redump supported langauge
/// </summary>
public enum RedumpLanguage
{
Afrikaans,
Albanian,
Arabic,
Basque,
Bulgarian,
Catalan,
Chinese,
Croatian,
Czech,
Danish,
Dutch,
English,
Estonian,
Finnish,
French,
Gaelic,
German,
Greek,
Hebrew,
Hindi,
Hungarian,
Indonesian,
Icelandic,
Italian,
Japanese,
Korean,
Latin,
Latvian,
Lithuanian,
Macedonian,
Norwegian,
Polish,
Portuguese,
Punjabi,
Romanian,
Russian,
Serbian,
Slovak,
Slovenian,
Spanish,
Swedish,
Tamil,
Thai,
Turkish,
Ukrainian,
}
/// <summary>
/// Redump PS2 language selection via
/// </summary>
public enum RedumpLanguageSelection
{
BiosSettings,
LanguageSelector,
OptionsMenu,
}
/// <summary>
/// Supported Redump region
/// </summary>
public enum RedumpRegion
{
Argentina,
Asia,
AsiaEurope,
AsiaUSA,
Australia,
AustraliaGermany,
AustraliaNewZealand,
Austria,
AustriaSwitzerland,
Belgium,
BelgiumNetherlands,
Brazil,
Bulgaria,
Canada,
China,
Croatia,
Czech,
Denmark,
Estonia,
Europe,
EuropeAsia,
EuropeAustralia,
EuropeCanada,
EuropeGermany,
Export,
Finland,
France,
FranceSpain,
Germany,
GreaterChina,
Greece,
Hungary,
Iceland,
India,
Ireland,
Israel,
Italy,
Japan,
JapanAsia,
JapanEurope,
JapanKorea,
JapanUSA,
Korea,
LatinAmerica,
Lithuania,
Netherlands,
NewZealand,
Norway,
Poland,
Portugal,
Romania,
Russia,
Scandinavia,
Serbia,
Singapore,
Slovakia,
SouthAfrica,
Spain,
SpainPortugal,
Sweden,
Switzerland,
Taiwan,
Thailand,
Turkey,
UnitedArabEmirates,
UK,
UKAustralia,
Ukraine,
USA,
USAAsia,
USAAustralia,
USABrazil,
USACanada,
USAEurope,
USAGermany,
USAJapan,
USAKorea,
World,
}
/// <summary>
/// List of all known Redump systems
/// </summary>
public enum RedumpSystem
{
// Special BIOS sets
MicrosoftXboxBIOS,
NintendoGameCubeBIOS,
SonyPlayStationBIOS,
SonyPlayStation2BIOS,
// Regular systems
AcornArchimedes,
AppleMacintosh,
AtariJaguarCDInteractiveMultimediaSystem,
AudioCD,
BandaiPippin,
BandaiPlaydiaQuickInteractiveSystem,
BDVideo,
CommodoreAmigaCD,
CommodoreAmigaCD32,
CommodoreAmigaCDTV,
DVDVideo,
EnhancedCD,
FujitsuFMTownsseries,
funworldPhotoPlay,
HasbroVideoNow,
HasbroVideoNowColor,
HasbroVideoNowJr,
HasbroVideoNowXP,
IBMPCcompatible,
IncredibleTechnologiesEagle,
KonamieAmusement,
KonamiFireBeat,
KonamiM2,
KonamiSystem573,
KonamiSystemGV,
KonamiTwinkle,
MattelFisherPriceiXL,
MattelHyperScan,
MemorexVisualInformationSystem,
MicrosoftXbox,
MicrosoftXbox360,
MicrosoftXboxOne,
MicrosoftXboxSeriesXS,
NamcoSegaNintendoTriforce,
NamcoSystem12,
NamcoSystem246,
NavisoftNaviken21,
NECPCEngineCDTurboGrafxCD,
NECPC88series,
NECPC98series,
NECPCFXPCFXGA,
NintendoGameCube,
NintendoWii,
NintendoWiiU,
PalmOS,
Panasonic3DOInteractiveMultiplayer,
PanasonicM2,
PhilipsCDi,
PhotoCD,
PlayStationGameSharkUpdates,
SegaChihiro,
SegaDreamcast,
SegaLindbergh,
SegaMegaCDSegaCD,
SegaNaomi,
SegaNaomi2,
SegaPrologue21,
SegaRingEdge,
SegaRingEdge2,
SegaSaturn,
SegaTitanVideo,
SharpX68000,
SNKNeoGeoCD,
SonyPlayStation,
SonyPlayStation2,
SonyPlayStation3,
SonyPlayStation4,
SonyPlayStation5,
SonyPlayStationPortable,
TABAustriaQuizard,
TaoiKTV,
TomyKissSite,
VideoCD,
VMLabsNUON,
VTechVFlashVSmilePro,
ZAPiTGamesGameWaveFamilyEntertainmentSystem,
}
/// <summary>
/// Generic yes/no values for Redump
/// </summary>
public enum YesNo
{
NULL = 0,
No = 1,
Yes = 2,
}
#region Win32_CDROMDrive
// https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-cdromdrive
/// <summary>
/// Availability and status of the device
/// </summary>
public enum Availability : ushort
{
Other = 1,
Unknown = 2,
RunningFullPower = 3,
Warning = 4,
InTest = 5,
NotApplicable = 6,
PowerOff = 7,
OffLine = 8,
OffDuty = 9,
Degraded = 10,
NotInstalled = 11,
InstallError = 12,
PowerSaveUnknown = 13,
PowerSaveLowPowerMode = 14,
PowerSaveStandby = 15,
PowerCycle = 16,
PowerSaveWarning = 17,
Paused = 18,
NotReady = 19,
NotConfigured = 20,
Quiesced = 21,
}
/// <summary>
/// Optical drive capabilities
/// </summary>
public enum Capabilities : ushort
{
Unknown = 0,
Other = 1,
SequentialAccess = 2,
RandomAccess = 3,
SupportsWriting = 4,
Encryption = 5,
Compression = 6,
SupportsRemoveableMedia = 7,
ManualCleaning = 8,
AutomaticCleaning = 9,
SMARTNotification = 10,
SupportsDualSidedMedia = 11,
PredismountEjectNotRequired = 12,
}
/// <summary>
/// File system flags
/// </summary>
[Flags]
public enum FileSystemFlags : uint
{
None = 0,
CaseSensitiveSearch = 1,
CasePreservedNames = 2,
UnicodeOnDisk = 4,
PersistentACLs = 8,
FileCompression = 16,
VolumeQuotas = 32,
SupportsSparseFiles = 64,
SupportsReparsePoints = 128,
SupportsRemoteStorage = 256,
SupportsLongNames = 16384,
VolumeIsCompressed = 32768,
ReadOnlyVolume = 524288,
SupportsObjectIDS = 65536,
SupportsEncryption = 131072,
SupportsNamedStreams = 262144,
}
/// <summary>
/// Specific power-related capabilities of a logical device
/// </summary>
public enum PowerManagementCapabilities : ushort
{
Unknown = 0,
NotSupported = 1,
Disabled = 2,
Enabled = 3,
PowerSavingModesEnteredAutomatically = 4,
PowerStateSettable = 5,
PowerCyclingSupported = 6,
TimedPowerOnSupported = 7,
}
#endregion
}

296
MPF.Library/Data/IniFile.cs Normal file
View File

@@ -0,0 +1,296 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace MPF.Data
{
public class IniFile : IDictionary<string, string>
{
private Dictionary<string, string> _keyValuePairs = new Dictionary<string, string>();
public string this[string key]
{
get
{
if (_keyValuePairs == null)
_keyValuePairs = new Dictionary<string, string>();
key = key.ToLowerInvariant();
if (_keyValuePairs.ContainsKey(key))
return _keyValuePairs[key];
return null;
}
set
{
if (_keyValuePairs == null)
_keyValuePairs = new Dictionary<string, string>();
key = key.ToLowerInvariant();
_keyValuePairs[key] = value;
}
}
/// <summary>
/// Create an empty INI file
/// </summary>
public IniFile()
{
}
/// <summary>
/// Populate an INI file from path
/// </summary>
public IniFile(string path)
{
this.Parse(path);
}
/// <summary>
/// Populate an INI file from stream
/// </summary>
public IniFile(Stream stream)
{
this.Parse(stream);
}
/// <summary>
/// Add or update a key and value to the INI file
/// </summary>
public void AddOrUpdate(string key, string value)
{
_keyValuePairs[key.ToLowerInvariant()] = value;
}
/// <summary>
/// Remove a key from the INI file
/// </summary>
public void Remove(string key)
{
_keyValuePairs.Remove(key.ToLowerInvariant());
}
/// <summary>
/// Read an INI file based on the path
/// </summary>
public bool Parse(string path)
{
// If we don't have a file, we can't read it
if (!File.Exists(path))
return false;
using (var fileStream = File.OpenRead(path))
{
return Parse(fileStream);
}
}
/// <summary>
/// Read an INI file from a stream
/// </summary>
public bool Parse(Stream stream)
{
// If the stream is invalid or unreadable, we can't process it
if (stream == null || !stream.CanRead || stream.Position >= stream.Length - 1)
return false;
// Keys are case-insensitive by default
try
{
using (StreamReader sr = new StreamReader(stream))
{
string section = string.Empty;
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
// Comments start with ';'
if (line.StartsWith(";"))
{
// No-op, we don't process comments
}
// Section titles are surrounded by square brackets
else if (line.StartsWith("["))
{
section = line.TrimStart('[').TrimEnd(']');
}
// Valid INI lines are in the format key=value
else if (line.Contains("="))
{
// Split the line by '=' for key-value pairs
string[] data = line.Split('=');
// If the value field contains an '=', we need to put them back in
string key = data[0].Trim();
string value = string.Join("=", data.Skip(1)).Trim();
// Section names are prepended to the key with a '.' separating
if (!string.IsNullOrEmpty(section))
key = $"{section}.{key}";
// Set or overwrite keys in the returned dictionary
_keyValuePairs[key.ToLowerInvariant()] = value;
}
// All other lines are ignored
}
}
}
catch
{
// We don't care what the error was, just catch and return
return false;
}
return true;
}
/// <summary>
/// Write an INI file to a path
/// </summary>
public bool Write(string path)
{
// If we don't have a valid dictionary with values, we can't write out
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
return false;
using (var fileStream = File.OpenWrite(path))
{
return Write(fileStream);
}
}
/// <summary>
/// Write an INI file to a stream
/// </summary>
public bool Write(Stream stream)
{
// If we don't have a valid dictionary with values, we can't write out
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
return false;
// If the stream is invalid or unwritable, we can't output to it
if (stream == null || !stream.CanWrite || stream.Position >= stream.Length - 1)
return false;
try
{
using (StreamWriter sw = new StreamWriter(stream))
{
// Order the dictionary by keys to link sections together
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
string section = string.Empty;
foreach (var keyValuePair in orderedKeyValuePairs)
{
// Extract the key and value
string key = keyValuePair.Key;
string value = keyValuePair.Value;
// We assume '.' is a section name separator
if (key.Contains('.'))
{
// Split the key by '.'
string[] data = keyValuePair.Key.Split('.');
// If the key contains an '.', we need to put them back in
string newSection = data[0].Trim();
key = string.Join(".", data.Skip(1)).Trim();
// If we have a new section, write it out
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
{
sw.WriteLine($"[{newSection}]");
section = newSection;
}
}
// Now write out the key and value in a standardized way
sw.WriteLine($"{key}={value}");
}
}
}
catch
{
// We don't care what the error was, just catch and return
return false;
}
return true;
}
#region IDictionary Impelementations
public ICollection<string> Keys => ((IDictionary<string, string>)_keyValuePairs).Keys;
public ICollection<string> Values => ((IDictionary<string, string>)_keyValuePairs).Values;
public int Count => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Count;
public bool IsReadOnly => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).IsReadOnly;
public void Add(string key, string value)
{
((IDictionary<string, string>)_keyValuePairs).Add(key.ToLowerInvariant(), value);
}
bool IDictionary<string, string>.Remove(string key)
{
return ((IDictionary<string, string>)_keyValuePairs).Remove(key.ToLowerInvariant());
}
public bool TryGetValue(string key, out string value)
{
return ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out value);
}
public void Add(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Add(newItem);
}
public void Clear()
{
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Clear();
}
public bool Contains(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Contains(newItem);
}
public bool ContainsKey(string key)
{
return _keyValuePairs.ContainsKey(key.ToLowerInvariant());
}
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).CopyTo(array, arrayIndex);
}
public bool Remove(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Remove(newItem);
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return ((IEnumerable<KeyValuePair<string, string>>)_keyValuePairs).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_keyValuePairs).GetEnumerator();
}
#endregion
}
}

569
MPF.Library/Data/Options.cs Normal file
View File

@@ -0,0 +1,569 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using MPF.Utilities;
namespace MPF.Data
{
public class Options : IDictionary<string, string>, ICloneable
{
private Dictionary<string, string> _settings;
#region Internal Program
/// <summary>
/// Path to Aaru
/// </summary>
public string AaruPath
{
get { return GetStringSetting(_settings, "AaruPath", "Programs\\Aaru\\Aaru.exe"); }
set { _settings["AaruPath"] = value; }
}
/// <summary>
/// Path to DiscImageCreator
/// </summary>
public string DiscImageCreatorPath
{
get { return GetStringSetting(_settings, "DiscImageCreatorPath", "Programs\\Creator\\DiscImageCreator.exe"); }
set { _settings["DiscImageCreatorPath"] = value; }
}
/// <summary>
/// Path to dd for Windows
/// </summary>
public string DDPath
{
get { return GetStringSetting(_settings, "DDPath", "Programs\\DD\\dd.exe"); }
set { _settings["DDPath"] = value; }
}
/// <summary>
/// Currently selected dumping program
/// </summary>
public InternalProgram InternalProgram
{
get
{
string valueString = GetStringSetting(_settings, "InternalProgram", InternalProgram.DiscImageCreator.ToString());
var valueEnum = Converters.ToInternalProgram(valueString);
return valueEnum == InternalProgram.NONE ? InternalProgram.DiscImageCreator : valueEnum;
}
set
{
_settings["InternalProgram"] = value.ToString();
}
}
#endregion
#region UI Defaults
/// <summary>
/// Enable dark mode for UI elements
/// </summary>
public bool EnableDarkMode
{
get { return GetBooleanSetting(_settings, "EnableDarkMode", false); }
set { _settings["EnableDarkMode"] = value.ToString(); }
}
/// <summary>
/// Default output path for dumps
/// </summary>
public string DefaultOutputPath
{
get { return GetStringSetting(_settings, "DefaultOutputPath", "ISO"); }
set { _settings["DefaultOutputPath"] = value; }
}
/// <summary>
/// Default system if none can be detected
/// </summary>
public KnownSystem DefaultSystem
{
get
{
string valueString = GetStringSetting(_settings, "DefaultSystem", KnownSystem.NONE.ToString());
var valueEnum = Converters.ToKnownSystem(valueString);
return valueEnum ?? KnownSystem.NONE;
}
set
{
_settings["DefaultSystem"] = Converters.GetLongName(value);
}
}
#endregion
#region Dumping Speeds
/// <summary>
/// Default CD dumping speed
/// </summary>
public int PreferredDumpSpeedCD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedCD", 72); }
set { _settings["PreferredDumpSpeedCD"] = value.ToString(); }
}
/// <summary>
/// Default DVD dumping speed
/// </summary>
public int PreferredDumpSpeedDVD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedDVD", 24); }
set { _settings["PreferredDumpSpeedDVD"] = value.ToString(); }
}
/// <summary>
/// Default BD dumping speed
/// </summary>
public int PreferredDumpSpeedBD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedBD", 16); }
set { _settings["PreferredDumpSpeedBD"] = value.ToString(); }
}
#endregion
#region Aaru
/// <summary>
/// Enable debug output while dumping by default
/// </summary>
public bool AaruEnableDebug
{
get { return GetBooleanSetting(_settings, "AaruEnableDebug", false); }
set { _settings["AaruEnableDebug"] = value.ToString(); }
}
/// <summary>
/// Enable verbose output while dumping by default
/// </summary>
public bool AaruEnableVerbose
{
get { return GetBooleanSetting(_settings, "AaruEnableVerbose", false); }
set { _settings["AaruEnableVerbose"] = value.ToString(); }
}
/// <summary>
/// Enable force dumping of media by default
/// </summary>
public bool AaruForceDumping
{
get { return GetBooleanSetting(_settings, "AaruForceDumping", true); }
set { _settings["AaruForceDumping"] = value.ToString(); }
}
/// <summary>
/// Default number of sector/subchannel rereads
/// </summary>
public int AaruRereadCount
{
get { return GetInt32Setting(_settings, "AaruRereadCount", 5); }
set { _settings["AaruRereadCount"] = value.ToString(); }
}
/// <summary>
/// Strip personal data information from Aaru metadata by default
/// </summary>
public bool AaruStripPersonalData
{
get { return GetBooleanSetting(_settings, "AaruStripPersonalData", false); }
set { _settings["AaruStripPersonalData"] = value.ToString(); }
}
#endregion
#region DiscImageCreator
/// <summary>
/// Enable overly-secure dumping flags by default
/// </summary>
/// <remarks>
/// Split this into component parts later. Currently does:
/// - Scan sector protection and set subchannel read level to 2 for CD
/// - Set scan file protect flag for DVD
/// </remarks>
public bool DICParanoidMode
{
get { return GetBooleanSetting(_settings, "DICParanoidMode", false); }
set { _settings["DICParanoidMode"] = value.ToString(); }
}
/// <summary>
/// Enable the Quiet flag by default
/// </summary>
public bool DICQuietMode
{
get { return GetBooleanSetting(_settings, "DICQuietMode", false); }
set { _settings["DICQuietMode"] = value.ToString(); }
}
/// <summary>
/// Default number of C2 rereads
/// </summary>
public int DICRereadCount
{
get { return GetInt32Setting(_settings, "DICRereadCount", 20); }
set { _settings["DICRereadCount"] = value.ToString(); }
}
/// <summary>
/// Reset drive after dumping (useful for older drives)
/// </summary>
public bool DICResetDriveAfterDump
{
get { return GetBooleanSetting(_settings, "DICResetDriveAfterDump", false); }
set { _settings["DICResetDriveAfterDump"] = value.ToString(); }
}
/// <summary>
/// Use the CMI flag for supported disc types
/// </summary>
public bool DICUseCMIFlag
{
get { return GetBooleanSetting(_settings, "DICUseCMIFlag", false); }
set { _settings["DICUseCMIFlag"] = value.ToString(); }
}
#endregion
#region Extra Dumping Options
/// <summary>
/// Scan the disc for protection after dumping
/// </summary>
public bool ScanForProtection
{
get { return GetBooleanSetting(_settings, "ScanForProtection", true); }
set { _settings["ScanForProtection"] = value.ToString(); }
}
/// <summary>
/// Add placeholder values in the submission info
/// </summary>
public bool AddPlaceholders
{
get { return GetBooleanSetting(_settings, "AddPlaceholders", true); }
set { _settings["AddPlaceholders"] = value.ToString(); }
}
/// <summary>
/// Show the disc information window after dumping
/// </summary>
public bool PromptForDiscInformation
{
get { return GetBooleanSetting(_settings, "PromptForDiscInformation", true); }
set { _settings["PromptForDiscInformation"] = value.ToString(); }
}
/// <summary>
/// Eject the disc after dumping
/// </summary>
public bool EjectAfterDump
{
get { return GetBooleanSetting(_settings, "EjectAfterDump", false); }
set { _settings["EjectAfterDump"] = value.ToString(); }
}
/// <summary>
/// Ignore fixed drives when populating the list
/// </summary>
public bool IgnoreFixedDrives
{
get { return GetBooleanSetting(_settings, "IgnoreFixedDrives", true); }
set { _settings["IgnoreFixedDrives"] = value.ToString(); }
}
/// <summary>
/// Show dumping tools in their own window instead of in the log
/// </summary>
public bool ToolsInSeparateWindow
{
get { return GetBooleanSetting(_settings, "ToolsInSeparateWindow", true); }
set { _settings["ToolsInSeparateWindow"] = value.ToString(); }
}
/// <summary>
/// Output the compressed JSON version of the submission info
/// </summary>
public bool OutputSubmissionJSON
{
get { return GetBooleanSetting(_settings, "OutputSubmissionJSON", false); }
set { _settings["OutputSubmissionJSON"] = value.ToString(); }
}
/// <summary>
/// Include log files in serialized JSON data
/// </summary>
public bool IncludeArtifacts
{
get { return GetBooleanSetting(_settings, "IncludeArtifacts", false); }
set { _settings["IncludeArtifacts"] = value.ToString(); }
}
/// <summary>
/// Compress output log files to reduce space
/// </summary>
public bool CompressLogFiles
{
get { return GetBooleanSetting(_settings, "CompressLogFiles", true); }
set { _settings["CompressLogFiles"] = value.ToString(); }
}
#endregion
#region Skip Options
/// <summary>
/// Skip detecting media type on disc scan
/// </summary>
public bool SkipMediaTypeDetection
{
get { return GetBooleanSetting(_settings, "SkipMediaTypeDetection", false); }
set { _settings["SkipMediaTypeDetection"] = value.ToString(); }
}
/// <summary>
/// Skip detecting known system on disc scan
/// </summary>
public bool SkipSystemDetection
{
get { return GetBooleanSetting(_settings, "SkipSystemDetection", false); }
set { _settings["SkipSystemDetection"] = value.ToString(); }
}
#endregion
#region Protection Scanning Options
/// <summary>
/// Scan archive contents during protection scanning
/// </summary>
public bool ScanArchivesForProtection
{
get { return GetBooleanSetting(_settings, "ScanArchivesForProtection", true); }
set { _settings["ScanArchivesForProtection"] = value.ToString(); }
}
/// <summary>
/// Scan for executable packers during protection scanning
/// </summary>
public bool ScanPackersForProtection
{
get { return GetBooleanSetting(_settings, "ScanPackersForProtection", false); }
set { _settings["ScanPackersForProtection"] = value.ToString(); }
}
/// <summary>
/// Force scanning all files for protection
/// </summary>
public bool ForceScanningForProtection
{
get { return GetBooleanSetting(_settings, "ForceScanningForProtection", false); }
set { _settings["ForceScanningForProtection"] = value.ToString(); }
}
/// <summary>
/// Include debug information with scan results
/// </summary>
public bool IncludeDebugProtectionInformation
{
get { return GetBooleanSetting(_settings, "IncludeDebugProtectionInformation", false); }
set { _settings["IncludeDebugProtectionInformation"] = value.ToString(); }
}
#endregion
#region Logging Options
/// <summary>
/// Enable verbose and debug logs to be written
/// </summary>
public bool VerboseLogging
{
get { return GetBooleanSetting(_settings, "VerboseLogging", true); }
set { _settings["VerboseLogging"] = value.ToString(); }
}
/// <summary>
/// Have the log panel expanded by default on startup
/// </summary>
public bool OpenLogWindowAtStartup
{
get { return GetBooleanSetting(_settings, "OpenLogWindowAtStartup", true); }
set { _settings["OpenLogWindowAtStartup"] = value.ToString(); }
}
/// <summary>
/// Enable fancy formatting of log statements
/// Disables EnableProgressProcessing if disabled
/// </summary>
/// <remarks>
/// This is mainly for outputting redirected console outputs. Not many
/// other bits of the logs include any specially handled outputs.
/// </remarks>
public bool EnableLogFormatting
{
get { return GetBooleanSetting(_settings, "EnableLogFormatting", false); }
set { _settings["EnableLogFormatting"] = value.ToString(); }
}
/// <summary>
/// Enable progress bar updating based on log text
/// Disabled if EnableLogFormatting is disabled
/// </summary>
public bool EnableProgressProcessing
{
get { return GetBooleanSetting(_settings, "EnableProgressProcessing", false); }
set { _settings["EnableProgressProcessing"] = value.ToString(); }
}
#endregion
#region Redump Login Information
public string RedumpUsername
{
get { return GetStringSetting(_settings, "RedumpUsername", ""); }
set { _settings["RedumpUsername"] = value; }
}
// TODO: Figure out a way to keep this encrypted in some way, BASE64 to start?
public string RedumpPassword
{
get { return GetStringSetting(_settings, "RedumpPassword", ""); }
set { _settings["RedumpPassword"] = value; }
}
/// <summary>
/// Determine if a complete set of Redump credentials might exist
/// </summary>
public bool HasRedumpLogin { get => !string.IsNullOrWhiteSpace(RedumpUsername) && !string.IsNullOrWhiteSpace(RedumpPassword); }
#endregion
/// <summary>
/// Constructor taking a dictionary for settings
/// </summary>
/// <param name="settings"></param>
public Options(Dictionary<string, string> settings = null)
{
this._settings = settings ?? new Dictionary<string, string>();
}
/// <summary>
/// Create a clone of the object
/// </summary>
public object Clone()
{
return new Options(new Dictionary<string, string>(_settings));
}
#region Helpers
/// <summary>
/// Get a Boolean setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private bool GetBooleanSetting(Dictionary<string, string> settings, string key, bool defaultValue)
{
if (settings.ContainsKey(key))
{
if (Boolean.TryParse(settings[key], out bool value))
return value;
else
return defaultValue;
}
else
{
return defaultValue;
}
}
/// <summary>
/// Get an Int32 setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private int GetInt32Setting(Dictionary<string, string> settings, string key, int defaultValue)
{
if (settings.ContainsKey(key))
{
if (Int32.TryParse(settings[key], out int value))
return value;
else
return defaultValue;
}
else
{
return defaultValue;
}
}
/// <summary>
/// Get a String setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private string GetStringSetting(Dictionary<string, string> settings, string key, string defaultValue)
{
if (settings.ContainsKey(key))
return settings[key];
else
return defaultValue;
}
#endregion
#region IDictionary implementations
public ICollection<string> Keys => _settings.Keys;
public ICollection<string> Values => _settings.Values;
public int Count => _settings.Count;
public bool IsReadOnly => ((IDictionary<string, string>)_settings).IsReadOnly;
public string this[string key]
{
get { return (_settings.ContainsKey(key) ? _settings[key] : null); }
set { _settings[key] = value; }
}
public bool ContainsKey(string key) => _settings.ContainsKey(key);
public void Add(string key, string value) => _settings.Add(key, value);
public bool Remove(string key) => _settings.Remove(key);
public bool TryGetValue(string key, out string value) => _settings.TryGetValue(key, out value);
public void Add(KeyValuePair<string, string> item) => _settings.Add(item.Key, item.Value);
public void Clear() => _settings.Clear();
public bool Contains(KeyValuePair<string, string> item) => ((IDictionary<string, string>)_settings).Contains(item);
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) => ((IDictionary<string, string>)_settings).CopyTo(array, arrayIndex);
public bool Remove(KeyValuePair<string, string> item) => ((IDictionary<string, string>)_settings).Remove(item);
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() => _settings.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _settings.GetEnumerator();
#endregion
}
}

View File

@@ -0,0 +1,78 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace MPF.Data
{
public class ProcessingQueue<T> : IDisposable
{
/// <summary>
/// Internal queue to hold data to process
/// </summary>
private readonly ConcurrentQueue<T> InternalQueue;
/// <summary>
/// Custom processing step for dequeued data
/// </summary>
private readonly Action<T> CustomProcessing;
/// <summary>
/// Cancellation method for the processing task
/// </summary>
private readonly CancellationTokenSource TokenSource;
public ProcessingQueue(Action<T> customProcessing)
{
this.InternalQueue = new ConcurrentQueue<T>();
this.CustomProcessing = customProcessing;
this.TokenSource = new CancellationTokenSource();
Task.Run(() => ProcessQueue(), this.TokenSource.Token);
}
/// <summary>
/// Dispose the current instance
/// </summary>
public void Dispose()
{
this.TokenSource.Cancel();
}
/// <summary>
/// Enqueue a new item for processing
/// </summary>
/// <param name="item"></param>
public void Enqueue(T item)
{
// Only accept new data when not cancelled
if (!this.TokenSource.IsCancellationRequested)
this.InternalQueue.Enqueue(item);
}
/// <summary>
/// Process
/// </summary>
private void ProcessQueue()
{
while (true)
{
// Nothing in the queue means we get to idle
if (this.InternalQueue.Count == 0)
{
if (this.TokenSource.IsCancellationRequested)
break;
Thread.Sleep(10);
continue;
}
// Get the next item from the queue
if (!this.InternalQueue.TryDequeue(out T nextItem))
continue;
// Invoke the lambda, if possible
this.CustomProcessing?.Invoke(nextItem);
}
}
}
}

View File

@@ -0,0 +1,52 @@
namespace MPF.Data
{
/// <summary>
/// Generic success/failure result object, with optional message
/// </summary>
public class Result
{
/// <summary>
/// Internal representation of success
/// </summary>
private readonly bool success;
/// <summary>
/// Optional message for the result
/// </summary>
public string Message { get; private set; }
private Result(bool success, string message)
{
this.success = success;
this.Message = message;
}
/// <summary>
/// Create a default success result with no message
/// </summary>
public static Result Success() => new Result(true, "");
/// <summary>
/// Create a success result with a custom message
/// </summary>
/// <param name="message">String to add as a message</param>
public static Result Success(string message) => new Result(true, message);
/// <summary>
/// Create a default failure result with no message
/// </summary>
/// <returns></returns>
public static Result Failure() => new Result(false, "");
/// <summary>
/// Create a failure result with a custom message
/// </summary>
/// <param name="message">String to add as a message</param>
public static Result Failure(string message) => new Result(false, message);
/// <summary>
/// Results can be compared to boolean values based on the success value
/// </summary>
public static implicit operator bool(Result result) => result.success;
}
}

View File

@@ -0,0 +1,343 @@
using System;
using System.Collections.Generic;
using MPF.Data;
using MPF.Utilities;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
namespace MPF.Data
{
public class SubmissionInfo
{
/// <summary>
/// Version of the current schema
/// </summary>
[JsonProperty(PropertyName = "schema_version", DefaultValueHandling = DefaultValueHandling.Ignore)]
public int SchemaVersion { get; set; } = 1;
/// <summary>
/// List of matched Redump IDs
/// </summary>
[JsonIgnore]
public List<int> MatchedIDs { get; set; }
/// <summary>
/// DateTime of when the disc was added
/// </summary>
[JsonIgnore]
public DateTime? Added { get; set; }
/// <summary>
/// DateTime of when the disc was last modified
/// </summary>
[JsonIgnore]
public DateTime? LastModified { get; set; }
[JsonProperty(PropertyName = "common_disc_info", DefaultValueHandling = DefaultValueHandling.Ignore)]
public CommonDiscInfoSection CommonDiscInfo { get; set; } = new CommonDiscInfoSection();
[JsonProperty(PropertyName = "versions_and_editions", DefaultValueHandling = DefaultValueHandling.Ignore)]
public VersionAndEditionsSection VersionAndEditions { get; set; } = new VersionAndEditionsSection();
[JsonProperty(PropertyName = "edc", DefaultValueHandling = DefaultValueHandling.Ignore)]
public EDCSection EDC { get; set; } = new EDCSection();
[JsonProperty(PropertyName = "parent_clone_relationship", DefaultValueHandling = DefaultValueHandling.Ignore)]
public ParentCloneRelationshipSection ParentCloneRelationship { get; set; } = new ParentCloneRelationshipSection();
[JsonProperty(PropertyName = "extras", DefaultValueHandling = DefaultValueHandling.Ignore)]
public ExtrasSection Extras { get; set; } = new ExtrasSection();
[JsonProperty(PropertyName = "copy_protection", DefaultValueHandling = DefaultValueHandling.Ignore)]
public CopyProtectionSection CopyProtection { get; set; } = new CopyProtectionSection();
[JsonProperty(PropertyName = "dumpers_and_status", DefaultValueHandling = DefaultValueHandling.Ignore)]
public DumpersAndStatusSection DumpersAndStatus { get; set; } = new DumpersAndStatusSection();
[JsonProperty(PropertyName = "tracks_and_write_offsets", DefaultValueHandling = DefaultValueHandling.Ignore)]
public TracksAndWriteOffsetsSection TracksAndWriteOffsets { get; set; } = new TracksAndWriteOffsetsSection();
[JsonProperty(PropertyName = "size_and_checksums", DefaultValueHandling = DefaultValueHandling.Ignore)]
public SizeAndChecksumsSection SizeAndChecksums { get; set; } = new SizeAndChecksumsSection();
[JsonProperty(PropertyName = "artifacts", DefaultValueHandling = DefaultValueHandling.Ignore)]
public Dictionary<string, string> Artifacts { get; set; } = new Dictionary<string, string>();
}
/// <summary>
/// Common disc info section of New Disc Form
/// </summary>
public class CommonDiscInfoSection
{
// Name not defined by Redump
[JsonProperty(PropertyName = "d_system", Required = Required.AllowNull)]
[JsonConverter(typeof(KnownSystemConverter))]
public KnownSystem? System { get; set; }
// Name not defined by Redump
[JsonProperty(PropertyName = "d_media", Required = Required.AllowNull)]
[JsonConverter(typeof(MediaTypeConverter))]
public MediaType? Media { get; set; }
[JsonProperty(PropertyName = "d_title", Required = Required.AllowNull)]
public string Title { get; set; }
[JsonProperty(PropertyName = "d_title_foreign", DefaultValueHandling = DefaultValueHandling.Ignore)]
public string ForeignTitleNonLatin { get; set; }
[JsonProperty(PropertyName = "d_number", NullValueHandling = NullValueHandling.Ignore)]
public string DiscNumberLetter { get; set; }
[JsonProperty(PropertyName = "d_label", NullValueHandling = NullValueHandling.Ignore)]
public string DiscTitle { get; set; }
[JsonProperty(PropertyName = "d_category", Required = Required.AllowNull)]
public RedumpDiscCategory? Category { get; set; }
[JsonProperty(PropertyName = "d_region", Required = Required.AllowNull)]
[JsonConverter(typeof(RedumpRegionConverter))]
public RedumpRegion? Region { get; set; }
[JsonProperty(PropertyName = "d_languages", Required = Required.AllowNull)]
[JsonConverter(typeof(RedumpLanguageConverter))]
public RedumpLanguage?[] Languages { get; set; }
[JsonProperty(PropertyName = "d_languages_selection", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
[JsonConverter(typeof(RedumpLanguageSelectionConverter))]
public RedumpLanguageSelection?[] LanguageSelection { get; set; }
[JsonProperty(PropertyName = "d_serial", NullValueHandling = NullValueHandling.Ignore)]
public string Serial { get; set; }
[JsonProperty(PropertyName = "d_ring", NullValueHandling = NullValueHandling.Ignore)]
public string Ring { get; }
[JsonProperty(PropertyName = "d_ring_0_id", NullValueHandling = NullValueHandling.Ignore)]
public string RingId { get; }
[JsonProperty(PropertyName = "d_ring_0_ma1", Required = Required.AllowNull)]
public string Layer0MasteringRing { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma1_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer0MasteringSID { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ts1", NullValueHandling = NullValueHandling.Ignore)]
public string Layer0ToolstampMasteringCode { get; set; }
[JsonProperty(PropertyName = "d_ring_0_mo1_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer0MouldSID { get; set; }
[JsonProperty(PropertyName = "dr_ring_0_mo1", NullValueHandling = NullValueHandling.Ignore)]
public string Layer0AdditionalMould { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma2", Required = Required.AllowNull)]
public string Layer1MasteringRing { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma2_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer1MasteringSID { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ts2", NullValueHandling = NullValueHandling.Ignore)]
public string Layer1ToolstampMasteringCode { get; set; }
[JsonProperty(PropertyName = "d_ring_0_mo2_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer1MouldSID { get; set; }
[JsonProperty(PropertyName = "dr_ring_0_mo2", NullValueHandling = NullValueHandling.Ignore)]
public string Layer1AdditionalMould { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma3", Required = Required.AllowNull)]
public string Layer2MasteringRing { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma3_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer2MasteringSID { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ts3", NullValueHandling = NullValueHandling.Ignore)]
public string Layer2ToolstampMasteringCode { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma4", Required = Required.AllowNull)]
public string Layer3MasteringRing { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma4_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer3MasteringSID { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ts4", NullValueHandling = NullValueHandling.Ignore)]
public string Layer3ToolstampMasteringCode { get; set; }
[JsonProperty(PropertyName = "d_ring_0_offsets", NullValueHandling = NullValueHandling.Ignore)]
public string RingOffsetsHidden { get { return "1"; } }
[JsonProperty(PropertyName = "d_ring_0_0_id", NullValueHandling = NullValueHandling.Ignore)]
public string RingZeroId { get; }
[JsonProperty(PropertyName = "d_ring_0_0_density", NullValueHandling = NullValueHandling.Ignore)]
public string RingZeroDensity { get; }
[JsonProperty(PropertyName = "d_ring_0_0_value", NullValueHandling = NullValueHandling.Ignore)]
public string RingWriteOffset { get; set; }
[JsonProperty(PropertyName = "d_ring_count", NullValueHandling = NullValueHandling.Ignore)]
public string RingCount { get { return "1"; } }
[JsonProperty(PropertyName = "d_barcode", NullValueHandling = NullValueHandling.Ignore)]
public string Barcode { get; set; }
[JsonProperty(PropertyName = "d_date", NullValueHandling = NullValueHandling.Ignore)]
public string EXEDateBuildDate { get; set; }
[JsonProperty(PropertyName = "d_errors", NullValueHandling = NullValueHandling.Ignore)]
public string ErrorsCount { get; set; }
[JsonProperty(PropertyName = "d_comments", NullValueHandling = NullValueHandling.Ignore)]
public string Comments { get; set; }
[JsonProperty(PropertyName = "d_contents", NullValueHandling = NullValueHandling.Ignore)]
public string Contents { get; set; }
}
/// <summary>
/// Version and editions section of New Disc form
/// </summary>
public class VersionAndEditionsSection
{
[JsonProperty(PropertyName = "d_version", NullValueHandling = NullValueHandling.Ignore)]
public string Version { get; set; }
[JsonProperty(PropertyName = "d_version_datfile", NullValueHandling = NullValueHandling.Ignore)]
public string VersionDatfile { get; set; }
[JsonProperty(PropertyName = "d_editions", NullValueHandling = NullValueHandling.Ignore)]
public string[] CommonEditions { get; set; }
[JsonProperty(PropertyName = "d_editions_text", NullValueHandling = NullValueHandling.Ignore)]
public string OtherEditions { get; set; }
}
/// <summary>
/// EDC section of New Disc form (PSX only)
/// </summary>
public class EDCSection
{
[JsonProperty(PropertyName = "d_edc", NullValueHandling = NullValueHandling.Ignore)]
public YesNo EDC { get; set; }
}
/// <summary>
/// Parent/Clone relationship section of New Disc form
/// </summary>
public class ParentCloneRelationshipSection
{
[JsonProperty(PropertyName = "d_parent_id", NullValueHandling = NullValueHandling.Ignore)]
public string ParentID { get; set; }
[JsonProperty(PropertyName = "d_is_regional_parent", NullValueHandling = NullValueHandling.Ignore)]
public bool RegionalParent { get; set; }
}
/// <summary>
/// Extras section of New Disc form
/// </summary>
public class ExtrasSection
{
[JsonProperty(PropertyName = "d_pvd", NullValueHandling = NullValueHandling.Ignore)]
public string PVD { get; set; }
[JsonProperty(PropertyName = "d_d1_key", NullValueHandling = NullValueHandling.Ignore)]
public string DiscKey { get; set; }
[JsonProperty(PropertyName = "d_d2_key", NullValueHandling = NullValueHandling.Ignore)]
public string DiscID { get; set; }
[JsonProperty(PropertyName = "d_pic_data", NullValueHandling = NullValueHandling.Ignore)]
public string PIC { get; set; }
[JsonProperty(PropertyName = "d_header", NullValueHandling = NullValueHandling.Ignore)]
public string Header { get; set; }
[JsonProperty(PropertyName = "d_bca", NullValueHandling = NullValueHandling.Ignore)]
public string BCA { get; set; }
[JsonProperty(PropertyName = "d_ssranges", NullValueHandling = NullValueHandling.Ignore)]
public string SecuritySectorRanges { get; set; }
}
/// <summary>
/// Copy protection section of New Disc form
/// </summary>
public class CopyProtectionSection
{
[JsonProperty(PropertyName = "d_protection_a", NullValueHandling = NullValueHandling.Ignore)]
public YesNo AntiModchip { get; set; }
[JsonProperty(PropertyName = "d_protection_1", NullValueHandling = NullValueHandling.Ignore)]
public YesNo LibCrypt { get; set; }
[JsonProperty(PropertyName = "d_libcrypt", NullValueHandling = NullValueHandling.Ignore)]
public string LibCryptData { get; set; }
[JsonProperty(PropertyName = "d_protection", NullValueHandling = NullValueHandling.Ignore)]
public string Protection { get; set; }
[JsonProperty(PropertyName = "d_securom", NullValueHandling = NullValueHandling.Ignore)]
public string SecuROMData { get; set; }
}
/// <summary>
/// Dumpers and status section of New Disc form (Moderator only)
/// </summary>
public class DumpersAndStatusSection
{
[JsonProperty(PropertyName = "d_status", NullValueHandling = NullValueHandling.Ignore)]
public RedumpDumpStatus Status { get; set; }
[JsonProperty(PropertyName = "d_dumpers", NullValueHandling = NullValueHandling.Ignore)]
public string[] Dumpers { get; set; }
[JsonProperty(PropertyName = "d_dumpers_text", NullValueHandling = NullValueHandling.Ignore)]
public string OtherDumpers { get; set; }
}
/// <summary>
/// Tracks and write offsets section of New Disc form (CD/GD-based)
/// </summary>
public class TracksAndWriteOffsetsSection
{
[JsonProperty(PropertyName = "d_tracks", NullValueHandling = NullValueHandling.Ignore)]
public string ClrMameProData { get; set; }
[JsonProperty(PropertyName = "d_cue", NullValueHandling = NullValueHandling.Ignore)]
public string Cuesheet { get; set; }
[JsonProperty(PropertyName = "d_offset", NullValueHandling = NullValueHandling.Ignore)]
public int[] CommonWriteOffsets { get; set; }
[JsonProperty(PropertyName = "d_offset_text", NullValueHandling = NullValueHandling.Ignore)]
public string OtherWriteOffsets { get; set; }
}
/// <summary>
/// Size &amp; checksums section of New Disc form (DVD/BD/UMD-based)
/// </summary>
public class SizeAndChecksumsSection
{
[JsonProperty(PropertyName = "d_layerbreak", NullValueHandling = NullValueHandling.Ignore)]
public long Layerbreak { get; set; }
[JsonProperty(PropertyName = "d_layerbreak_2", NullValueHandling = NullValueHandling.Ignore)]
public long Layerbreak2 { get; set; }
[JsonProperty(PropertyName = "d_layerbreak_3", NullValueHandling = NullValueHandling.Ignore)]
public long Layerbreak3 { get; set; }
[JsonProperty(PropertyName = "d_size", NullValueHandling = NullValueHandling.Ignore)]
public long Size { get; set; }
[JsonProperty(PropertyName = "d_crc32", NullValueHandling = NullValueHandling.Ignore)]
public string CRC32 { get; set; }
[JsonProperty(PropertyName = "d_md5", NullValueHandling = NullValueHandling.Ignore)]
public string MD5 { get; set; }
[JsonProperty(PropertyName = "d_sha1", NullValueHandling = NullValueHandling.Ignore)]
public string SHA1 { get; set; }
}
}

View File

@@ -0,0 +1,74 @@
namespace MPF.DiscImageCreator
{
/// <summary>
/// Top-level commands for DiscImageCreator
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
public const string Audio = "audio";
public const string BluRay = "bd";
public const string Close = "close";
public const string CompactDisc = "cd";
public const string Data = "data";
public const string DigitalVideoDisc = "dvd";
public const string Disk = "disk";
public const string DriveSpeed = "ls";
public const string Eject = "eject";
public const string Floppy = "fd";
public const string GDROM = "gd";
public const string MDS = "mds";
public const string Merge = "merge";
public const string Reset = "reset";
public const string SACD = "sacd";
public const string Start = "start";
public const string Stop = "stop";
public const string Sub = "sub";
public const string Swap = "swap";
public const string Tape = "tape";
public const string XBOX = "xbox";
public const string XBOXSwap = "xboxswap";
public const string XGD2Swap = "xgd2swap";
public const string XGD3Swap = "xgd3swap";
}
/// <summary>
/// Dumping flags for DiscImageCreator
/// </summary>
public static class FlagStrings
{
public const string AddOffset = "/a";
public const string AMSF = "/p";
public const string AtariJaguar = "/aj";
public const string BEOpcode = "/be";
public const string C2Opcode = "/c2";
public const string CopyrightManagementInformation = "/c";
public const string D8Opcode = "/d8";
public const string DisableBeep = "/q";
public const string ExtractMicroSoftCabFile = "/mscf";
public const string Fix = "/fix";
public const string ForceUnitAccess = "/f";
public const string MultiSectorRead = "/mr";
public const string MultiSession = "/ms";
public const string NoFixSubP = "/np";
public const string NoFixSubQ = "/nq";
public const string NoFixSubQLibCrypt = "/nl";
public const string NoFixSubRtoW = "/nr";
public const string NoFixSubQSecuROM = "/ns";
public const string NoSkipSS = "/nss";
public const string PadSector = "/ps";
public const string Raw = "/raw";
public const string Resume = "/re";
public const string Reverse = "/r";
public const string ScanAntiMod = "/am";
public const string ScanFileProtect = "/sf";
public const string ScanSectorProtect = "/ss";
public const string SeventyFour = "/74";
public const string SkipSector = "/sk";
public const string SubchannelReadLevel = "/s";
public const string UseAnchorVolumeDescriptorPointer = "/avdp";
public const string VideoNow = "/vn";
public const string VideoNowColor = "/vnc";
public const string VideoNowXP = "/vnx";
}
}

View File

@@ -0,0 +1,124 @@
using MPF.Data;
namespace MPF.DiscImageCreator
{
public static class Converters
{
#region Cross-enumeration conversions
/// <summary>
/// Get the most common known system for a given MediaType
/// </summary>
/// <param name="baseCommand">Command value to check</param>
/// <returns>KnownSystem if possible, null on error</returns>
public static KnownSystem? ToKnownSystem(string baseCommand)
{
switch (baseCommand)
{
case CommandStrings.Audio:
return KnownSystem.AudioCD;
case CommandStrings.CompactDisc:
case CommandStrings.Data:
case CommandStrings.DigitalVideoDisc:
case CommandStrings.Disk:
case CommandStrings.Floppy:
case CommandStrings.Tape:
return KnownSystem.IBMPCCompatible;
case CommandStrings.GDROM:
case CommandStrings.Swap:
return KnownSystem.SegaDreamcast;
case CommandStrings.BluRay:
return KnownSystem.SonyPlayStation3;
case CommandStrings.SACD:
return KnownSystem.SuperAudioCD;
case CommandStrings.XBOX:
case CommandStrings.XBOXSwap:
return KnownSystem.MicrosoftXBOX;
case CommandStrings.XGD2Swap:
case CommandStrings.XGD3Swap:
return KnownSystem.MicrosoftXBOX360;
default:
return null;
}
}
/// <summary>
/// Get the MediaType associated with a given base command
/// </summary>
/// <param name="baseCommand">Command value to check</param>
/// <returns>MediaType if possible, null on error</returns>
/// <remarks>This takes the "safe" route by assuming the larger of any given format</remarks>
public static MediaType? ToMediaType(string baseCommand)
{
switch (baseCommand)
{
case CommandStrings.Audio:
case CommandStrings.CompactDisc:
case CommandStrings.Data:
case CommandStrings.SACD:
return MediaType.CDROM;
case CommandStrings.GDROM:
case CommandStrings.Swap:
return MediaType.GDROM;
case CommandStrings.DigitalVideoDisc:
case CommandStrings.XBOX:
case CommandStrings.XBOXSwap:
case CommandStrings.XGD2Swap:
case CommandStrings.XGD3Swap:
return MediaType.DVD;
case CommandStrings.BluRay:
return MediaType.BluRay;
// Non-optical
case CommandStrings.Floppy:
return MediaType.FloppyDisk;
case CommandStrings.Disk:
return MediaType.HardDisk;
case CommandStrings.Tape:
return MediaType.DataCartridge;
default:
return null;
}
}
/// <summary>
/// Get the default extension for a given disc type
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>Valid extension (with leading '.'), null on error</returns>
public static string Extension(MediaType? type)
{
switch (type)
{
case MediaType.CDROM:
case MediaType.GDROM:
case MediaType.Cartridge:
case MediaType.HardDisk:
case MediaType.CompactFlash:
case MediaType.MMC:
case MediaType.SDCard:
case MediaType.FlashDrive:
return ".bin";
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
case MediaType.NintendoWiiOpticalDisc:
return ".iso";
case MediaType.LaserDisc:
case MediaType.NintendoGameCubeGameDisc:
return ".raw";
case MediaType.NintendoWiiUOpticalDisc:
return ".wud";
case MediaType.FloppyDisk:
return ".img";
case MediaType.Cassette:
return ".wav";
case MediaType.NONE:
default:
return null;
}
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,160 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using MPF.Data;
using OptimizedCRC;
namespace MPF.Hashing
{
/// <summary>
/// Async hashing class wraper
/// </summary>
public class Hasher
{
public Hash HashType { get; private set; }
private IDisposable _hasher;
public Hasher(Hash hashType)
{
this.HashType = hashType;
GetHasher();
}
/// <summary>
/// Generate the correct hashing class based on the hash type
/// </summary>
private void GetHasher()
{
switch (HashType)
{
case Hash.CRC:
_hasher = new OptimizedCRC.OptimizedCRC();
break;
case Hash.MD5:
_hasher = MD5.Create();
break;
case Hash.SHA1:
_hasher = SHA1.Create();
break;
case Hash.SHA256:
_hasher = SHA256.Create();
break;
case Hash.SHA384:
_hasher = SHA384.Create();
break;
case Hash.SHA512:
_hasher = SHA512.Create();
break;
}
}
public void Dispose()
{
_hasher.Dispose();
}
/// <summary>
/// Process a buffer of some length with the internal hash algorithm
/// </summary>
public void Process(byte[] buffer, int size)
{
switch (HashType)
{
case Hash.CRC:
(_hasher as OptimizedCRC.OptimizedCRC).Update(buffer, 0, size);
break;
case Hash.MD5:
case Hash.SHA1:
case Hash.SHA256:
case Hash.SHA384:
case Hash.SHA512:
(_hasher as HashAlgorithm).TransformBlock(buffer, 0, size, null, 0);
break;
}
}
/// <summary>
/// Finalize the internal hash algorigthm
/// </summary>
public void Terminate()
{
byte[] emptyBuffer = new byte[0];
switch (HashType)
{
case Hash.CRC:
(_hasher as OptimizedCRC.OptimizedCRC).Update(emptyBuffer, 0, 0);
break;
case Hash.MD5:
case Hash.SHA1:
case Hash.SHA256:
case Hash.SHA384:
case Hash.SHA512:
(_hasher as HashAlgorithm).TransformFinalBlock(emptyBuffer, 0, 0);
break;
}
}
/// <summary>
/// Get internal hash as a byte array
/// </summary>
public byte[] GetHash()
{
switch (HashType)
{
case Hash.CRC:
return BitConverter.GetBytes((_hasher as OptimizedCRC.OptimizedCRC).Value).Reverse().ToArray();
case Hash.MD5:
case Hash.SHA1:
case Hash.SHA256:
case Hash.SHA384:
case Hash.SHA512:
return (_hasher as HashAlgorithm).Hash;
}
return null;
}
/// <summary>
/// Get internal hash as a string
/// </summary>
public string GetHashString()
{
byte[] hash = GetHash();
if (hash == null)
return null;
return ByteArrayToString(hash);
}
/// <summary>
/// Convert a byte array to a hex string
/// </summary>
/// <param name="bytes">Byte array to convert</param>
/// <returns>Hex string representing the byte array</returns>
/// <link>http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa</link>
private static string ByteArrayToString(byte[] bytes)
{
// If we get null in, we send null out
if (bytes == null)
return null;
try
{
string hex = BitConverter.ToString(bytes);
return hex.Replace("-", string.Empty).ToLowerInvariant();
}
catch
{
return null;
}
}
}
}

View File

@@ -0,0 +1,153 @@
/*
Copyright (c) 2012-2015 Eugene Larchenko (spct@mail.ru)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
using System;
namespace OptimizedCRC
{
internal class OptimizedCRC : IDisposable
{
private const uint kCrcPoly = 0xEDB88320;
private const uint kInitial = 0xFFFFFFFF;
private const int CRC_NUM_TABLES = 8;
private static readonly uint[] Table;
static OptimizedCRC()
{
unchecked
{
Table = new uint[256 * CRC_NUM_TABLES];
int i;
for (i = 0; i < 256; i++)
{
uint r = (uint)i;
for (int j = 0; j < 8; j++)
{
r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
}
Table[i] = r;
}
for (; i < 256 * CRC_NUM_TABLES; i++)
{
uint r = Table[i - 256];
Table[i] = Table[r & 0xFF] ^ (r >> 8);
}
}
}
public uint UnsignedValue;
public OptimizedCRC()
{
Init();
}
/// <summary>
/// Reset CRC
/// </summary>
public void Init()
{
UnsignedValue = kInitial;
}
public int Value
{
get { return (int)~UnsignedValue; }
}
public void Update(byte[] data, int offset, int count)
{
new ArraySegment<byte>(data, offset, count); // check arguments
if (count == 0)
{
return;
}
var table = OptimizedCRC.Table;
uint crc = UnsignedValue;
for (; (offset & 7) != 0 && count != 0; count--)
{
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
}
if (count >= 8)
{
/*
* Idea from 7-zip project sources (http://7-zip.org/sdk.html)
*/
int end = (count - 8) & ~7;
count -= end;
end += offset;
while (offset != end)
{
crc ^= (uint)(data[offset] + (data[offset + 1] << 8) + (data[offset + 2] << 16) + (data[offset + 3] << 24));
uint high = (uint)(data[offset + 4] + (data[offset + 5] << 8) + (data[offset + 6] << 16) + (data[offset + 7] << 24));
offset += 8;
crc = table[(byte)crc + 0x700]
^ table[(byte)(crc >>= 8) + 0x600]
^ table[(byte)(crc >>= 8) + 0x500]
^ table[/*(byte)*/(crc >> 8) + 0x400]
^ table[(byte)(high) + 0x300]
^ table[(byte)(high >>= 8) + 0x200]
^ table[(byte)(high >>= 8) + 0x100]
^ table[/*(byte)*/(high >> 8) + 0x000];
}
}
while (count-- != 0)
{
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
}
UnsignedValue = crc;
}
static public int Compute(byte[] data, int offset, int count)
{
var crc = new OptimizedCRC();
crc.Update(data, offset, count);
return crc.Value;
}
static public int Compute(byte[] data)
{
return Compute(data, 0, data.Length);
}
static public int Compute(ArraySegment<byte> block)
{
return Compute(block.Array, block.Offset, block.Count);
}
public void Dispose()
{
UnsignedValue = 0;
}
}
}

View File

@@ -0,0 +1,79 @@
using System;
using System.IO;
using System.Threading;
namespace Compress.ThreadReaders
{
public class ThreadLoadBuffer : IDisposable
{
private readonly AutoResetEvent _waitEvent;
private readonly AutoResetEvent _outEvent;
private readonly Thread _tWorker;
private byte[] _buffer;
private int _size;
private readonly Stream _ds;
private bool _finished;
public bool errorState;
public int SizeRead;
public ThreadLoadBuffer(Stream ds)
{
_waitEvent = new AutoResetEvent(false);
_outEvent = new AutoResetEvent(false);
_finished = false;
_ds = ds;
errorState = false;
_tWorker = new Thread(MainLoop);
_tWorker.Start();
}
public void Dispose()
{
_waitEvent.Close();
_outEvent.Close();
}
private void MainLoop()
{
while (true)
{
_waitEvent.WaitOne();
if (_finished)
{
break;
}
try
{
SizeRead = _ds.Read(_buffer, 0, _size);
}
catch (Exception)
{
errorState = true;
}
_outEvent.Set();
}
}
public void Trigger(byte[] buffer, int size)
{
_buffer = buffer;
_size = size;
_waitEvent.Set();
}
public void Wait()
{
_outEvent.WaitOne();
}
public void Finish()
{
_finished = true;
_waitEvent.Set();
_tWorker.Join();
}
}
}

View File

@@ -0,0 +1,97 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<Title>MPF Library</Title>
<AssemblyName>MPF.Library</AssemblyName>
<Description>Library code for MPF and MPF.Check</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'!='netcoreapp3.1'">
<DefineConstants>NET_FRAMEWORK</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'!='netcoreapp3.1'">
<COMReference Include="IMAPI2">
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
<COMReference Include="IMAPI2FS">
<Guid>{2C941FD0-975B-59BE-A960-9A2A262853A5}</Guid>
<VersionMajor>1</VersionMajor>
<VersionMinor>0</VersionMinor>
<Lcid>0</Lcid>
<WrapperTool>tlbimp</WrapperTool>
<Isolated>False</Isolated>
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMReference>
</ItemGroup>
<ItemGroup>
<Compile Remove="Aaru\CICMMetadata\CICMMetadataEditor\**" />
<Compile Remove="Aaru\CICMMetadata\java\**" />
<Compile Remove="Aaru\CICMMetadata\samples\**" />
<EmbeddedResource Remove="Aaru\CICMMetadata\CICMMetadataEditor\**" />
<EmbeddedResource Remove="Aaru\CICMMetadata\java\**" />
<EmbeddedResource Remove="Aaru\CICMMetadata\samples\**" />
<None Remove="Aaru\CICMMetadata\CICMMetadataEditor\**" />
<None Remove="Aaru\CICMMetadata\java\**" />
<None Remove="Aaru\CICMMetadata\samples\**" />
</ItemGroup>
<ItemGroup>
<None Remove="Aaru\CICMMetadata\.editorconfig" />
<None Remove="Aaru\CICMMetadata\.git" />
<None Remove="Aaru\CICMMetadata\.gitignore" />
<None Remove="Aaru\CICMMetadata\.project" />
<None Remove="Aaru\CICMMetadata\build.sh" />
<None Remove="Aaru\CICMMetadata\cicm.xml" />
<None Remove="Aaru\CICMMetadata\cicm.xsd" />
<None Remove="Aaru\CICMMetadata\CICMMetadata.iml" />
<None Remove="Aaru\CICMMetadata\dotnet\cicm.vb" />
<None Remove="Aaru\CICMMetadata\README.md" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.7.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.Management" Version="6.0.0-preview.6.21352.12" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Management" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,229 @@
using MPF.Data;
namespace MPF.Redump
{
/// <summary>
/// Information pertaining to Redump systems
/// </summary>
public static class Extras
{
#region Special lists
/// <summary>
/// List of systems that are not publically accessible
/// </summary>
public static readonly RedumpSystem?[] BannedSystems = new RedumpSystem?[]
{
RedumpSystem.AudioCD,
RedumpSystem.BDVideo,
RedumpSystem.DVDVideo,
RedumpSystem.HasbroVideoNow,
RedumpSystem.HasbroVideoNowColor,
RedumpSystem.HasbroVideoNowJr,
RedumpSystem.HasbroVideoNowXP,
RedumpSystem.KonamiM2,
RedumpSystem.MicrosoftXbox360,
RedumpSystem.MicrosoftXboxOne,
//RedumpSystem.MicrosoftXboxSeriesXS,
RedumpSystem.NavisoftNaviken21,
RedumpSystem.NintendoWii,
RedumpSystem.NintendoWiiU,
RedumpSystem.PanasonicM2,
RedumpSystem.SegaPrologue21,
RedumpSystem.SegaRingEdge,
RedumpSystem.SegaRingEdge2,
RedumpSystem.SonyPlayStation3,
RedumpSystem.SonyPlayStation4,
//RedumpSystem.SonyPlayStation5,
RedumpSystem.VideoCD,
};
/// <summary>
/// List of systems that have a Cues pack
/// </summary>
public static readonly RedumpSystem?[] HasCues = new RedumpSystem?[]
{
RedumpSystem.AppleMacintosh,
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem,
RedumpSystem.AudioCD,
RedumpSystem.BandaiPippin,
RedumpSystem.BandaiPlaydiaQuickInteractiveSystem,
RedumpSystem.CommodoreAmigaCD,
RedumpSystem.CommodoreAmigaCD32,
RedumpSystem.CommodoreAmigaCDTV,
RedumpSystem.FujitsuFMTownsseries,
RedumpSystem.funworldPhotoPlay,
RedumpSystem.HasbroVideoNow,
RedumpSystem.HasbroVideoNowColor,
RedumpSystem.HasbroVideoNowJr,
RedumpSystem.HasbroVideoNowXP,
RedumpSystem.IBMPCcompatible,
RedumpSystem.IncredibleTechnologiesEagle,
RedumpSystem.KonamieAmusement,
RedumpSystem.KonamiFireBeat,
RedumpSystem.KonamiM2,
RedumpSystem.KonamiSystemGV,
RedumpSystem.MattelFisherPriceiXL,
RedumpSystem.MattelHyperScan,
RedumpSystem.MemorexVisualInformationSystem,
RedumpSystem.MicrosoftXbox,
RedumpSystem.MicrosoftXbox360,
RedumpSystem.NamcoSegaNintendoTriforce,
RedumpSystem.NamcoSystem246,
RedumpSystem.NavisoftNaviken21,
RedumpSystem.NECPCEngineCDTurboGrafxCD,
RedumpSystem.NECPC88series,
RedumpSystem.NECPC98series,
RedumpSystem.NECPCFXPCFXGA,
RedumpSystem.PalmOS,
RedumpSystem.Panasonic3DOInteractiveMultiplayer,
RedumpSystem.PanasonicM2,
RedumpSystem.PhilipsCDi,
RedumpSystem.PhotoCD,
RedumpSystem.PlayStationGameSharkUpdates,
RedumpSystem.SegaChihiro,
RedumpSystem.SegaDreamcast,
RedumpSystem.SegaMegaCDSegaCD,
RedumpSystem.SegaNaomi,
RedumpSystem.SegaNaomi2,
RedumpSystem.SegaPrologue21,
RedumpSystem.SegaSaturn,
RedumpSystem.SNKNeoGeoCD,
RedumpSystem.SonyPlayStation,
RedumpSystem.SonyPlayStation2,
RedumpSystem.SonyPlayStation3,
RedumpSystem.TABAustriaQuizard,
RedumpSystem.TomyKissSite,
RedumpSystem.VideoCD,
RedumpSystem.VTechVFlashVSmilePro,
};
/// <summary>
/// List of systems that has a Dat pack
/// </summary>
public static readonly RedumpSystem?[] HasDat = new RedumpSystem?[]
{
RedumpSystem.MicrosoftXboxBIOS,
RedumpSystem.NintendoGameCubeBIOS,
RedumpSystem.SonyPlayStationBIOS,
RedumpSystem.SonyPlayStation2BIOS,
RedumpSystem.AppleMacintosh,
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem,
RedumpSystem.AudioCD,
RedumpSystem.BandaiPippin,
RedumpSystem.BandaiPlaydiaQuickInteractiveSystem,
RedumpSystem.BDVideo,
RedumpSystem.CommodoreAmigaCD,
RedumpSystem.CommodoreAmigaCD32,
RedumpSystem.CommodoreAmigaCDTV,
RedumpSystem.DVDVideo,
RedumpSystem.FujitsuFMTownsseries,
RedumpSystem.funworldPhotoPlay,
RedumpSystem.HasbroVideoNow,
RedumpSystem.HasbroVideoNowColor,
RedumpSystem.HasbroVideoNowJr,
RedumpSystem.HasbroVideoNowXP,
RedumpSystem.IBMPCcompatible,
RedumpSystem.IncredibleTechnologiesEagle,
RedumpSystem.KonamieAmusement,
RedumpSystem.KonamiFireBeat,
RedumpSystem.KonamiM2,
RedumpSystem.KonamiSystemGV,
RedumpSystem.MattelFisherPriceiXL,
RedumpSystem.MattelHyperScan,
RedumpSystem.MemorexVisualInformationSystem,
RedumpSystem.MicrosoftXbox,
RedumpSystem.MicrosoftXbox360,
RedumpSystem.MicrosoftXboxOne,
//RedumpSystem.MicrosoftXboxSeriesXS,
RedumpSystem.NamcoSegaNintendoTriforce,
RedumpSystem.NamcoSystem246,
RedumpSystem.NavisoftNaviken21,
RedumpSystem.NECPCEngineCDTurboGrafxCD,
RedumpSystem.NECPC88series,
RedumpSystem.NECPC98series,
RedumpSystem.NECPCFXPCFXGA,
RedumpSystem.NintendoGameCube,
RedumpSystem.NintendoWii,
RedumpSystem.NintendoWiiU,
RedumpSystem.PalmOS,
RedumpSystem.Panasonic3DOInteractiveMultiplayer,
RedumpSystem.PanasonicM2,
RedumpSystem.PhilipsCDi,
RedumpSystem.PhotoCD,
RedumpSystem.PlayStationGameSharkUpdates,
RedumpSystem.SegaChihiro,
RedumpSystem.SegaDreamcast,
RedumpSystem.SegaLindbergh,
RedumpSystem.SegaMegaCDSegaCD,
RedumpSystem.SegaNaomi,
RedumpSystem.SegaNaomi2,
RedumpSystem.SegaRingEdge,
RedumpSystem.SegaRingEdge2,
RedumpSystem.SegaSaturn,
RedumpSystem.SNKNeoGeoCD,
RedumpSystem.SonyPlayStation,
RedumpSystem.SonyPlayStation2,
RedumpSystem.SonyPlayStation3,
RedumpSystem.SonyPlayStation4,
//RedumpSystem.SonyPlayStation5,
RedumpSystem.SonyPlayStationPortable,
RedumpSystem.TABAustriaQuizard,
RedumpSystem.TomyKissSite,
RedumpSystem.VideoCD,
RedumpSystem.VMLabsNUON,
RedumpSystem.VTechVFlashVSmilePro,
RedumpSystem.ZAPiTGamesGameWaveFamilyEntertainmentSystem,
};
/// <summary>
/// List of systems that has a Decrypted Keys pack
/// </summary>
public static readonly RedumpSystem?[] HasDkeys = new RedumpSystem?[]
{
RedumpSystem.SonyPlayStation3,
};
/// <summary>
/// List of systems that has a GDI pack
/// </summary>
public static readonly RedumpSystem?[] HasGdi = new RedumpSystem?[]
{
RedumpSystem.NamcoSegaNintendoTriforce,
RedumpSystem.SegaChihiro,
RedumpSystem.SegaDreamcast,
RedumpSystem.SegaNaomi,
RedumpSystem.SegaNaomi2,
};
/// <summary>
/// List of systems that has a Keys pack
/// </summary>
public static readonly RedumpSystem?[] HasKeys = new RedumpSystem?[]
{
RedumpSystem.NintendoWiiU,
RedumpSystem.SonyPlayStation3,
};
/// <summary>
/// List of systems that has an LSD pack
/// </summary>
public static readonly RedumpSystem?[] HasLsd = new RedumpSystem?[]
{
RedumpSystem.IBMPCcompatible,
RedumpSystem.SonyPlayStation,
};
/// <summary>
/// List of systems that has an SBI pack
/// </summary>
public static readonly RedumpSystem?[] HasSbi = new RedumpSystem?[]
{
RedumpSystem.IBMPCcompatible,
RedumpSystem.SonyPlayStation,
};
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Data;
namespace MPF.UmdImageCreator
{
/// <summary>
/// Represents a generic set of UmdImageCreator parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.UmdImageCreator;
#endregion
/// <inheritdoc/>
public Parameters(string parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
List<string> missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.UMD:
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}_disc.txt"))
missingFiles.Add($"{basePath}_disc.txt");
if (!File.Exists($"{basePath}_mainError.txt"))
missingFiles.Add($"{basePath}_mainError.txt");
if (!File.Exists($"{basePath}_mainInfo.txt"))
missingFiles.Add($"{basePath}_mainInfo.txt");
if (!File.Exists($"{basePath}_volDesc.txt"))
missingFiles.Add($"{basePath}_volDesc.txt");
}
break;
default:
return (false, missingFiles);
}
return (!missingFiles.Any(), missingFiles);
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
{
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.UMD:
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? "";
if (GetFileHashes(basePath + ".iso", out long filesize, out string crc32, out string md5, out string sha1))
{
info.SizeAndChecksums.Size = filesize;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
}
if (GetUMDAuxInfo(basePath + "_disc.txt", out string title, out RedumpDiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize))
{
info.CommonDiscInfo.Title = title ?? "";
info.CommonDiscInfo.Category = umdcat ?? RedumpDiscCategory.Games;
info.VersionAndEditions.Version = umdversion ?? "";
info.SizeAndChecksums.Size = umdsize;
if (!string.IsNullOrWhiteSpace(umdlayer))
info.SizeAndChecksums.Layerbreak = Int64.Parse(umdlayer ?? "-1");
}
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
if (File.Exists(basePath + "_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt"));
if (File.Exists(basePath + "_mainError.txt"))
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt"));
if (File.Exists(basePath + "_mainInfo.txt"))
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt"));
if (File.Exists(basePath + "_volDesc.txt"))
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt"));
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
List<string> logFiles = new List<string>();
switch (this.Type)
{
case MediaType.UMD:
if (File.Exists($"{basePath}_disc.txt"))
logFiles.Add($"{basePath}_disc.txt");
if (File.Exists($"{basePath}_mainError.txt"))
logFiles.Add($"{basePath}_mainError.txt");
if (File.Exists($"{basePath}_mainInfo.txt"))
logFiles.Add($"{basePath}_mainInfo.txt");
if (File.Exists($"{basePath}_volDesc.txt"))
logFiles.Add($"{basePath}_volDesc.txt");
break;
}
return logFiles;
}
#endregion
#region Information Extraction Methods
/// <summary>
/// Get the PVD from the input file, if possible
/// </summary>
/// <param name="mainInfo">_mainInfo.txt file location</param>
/// <returns>Newline-deliminated PVD if possible, null on error</returns>
private static string GetPVD(string mainInfo)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(mainInfo))
return null;
using (StreamReader sr = File.OpenText(mainInfo))
{
try
{
// Make sure we're in the right sector
while (!sr.ReadLine().StartsWith("========== LBA[000016, 0x0000010]: Main Channel ==========")) ;
// Fast forward to the PVD
while (!sr.ReadLine().StartsWith("0310")) ;
// Now that we're at the PVD, read each line in and concatenate
string pvd = "";
for (int i = 0; i < 6; i++)
pvd += sr.ReadLine() + "\n"; // 320-370
return pvd;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
/// <summary>
/// Get the UMD auxiliary info from the outputted files, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
private static bool GetUMDAuxInfo(string disc, out string title, out RedumpDiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize)
{
title = null; umdcat = null; umdversion = null; umdlayer = null; umdsize = -1;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(disc))
return false;
using (StreamReader sr = File.OpenText(disc))
{
try
{
// Loop through everything to get the first instance of each required field
string line = string.Empty;
while (!sr.EndOfStream)
{
line = sr.ReadLine().Trim();
if (line.StartsWith("TITLE") && title == null)
title = line.Substring("TITLE: ".Length);
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
umdversion = line.Split(' ')[1];
else if (line.StartsWith("pspUmdTypes"))
umdcat = GetUMDCategory(line.Split(' ')[1]);
else if (line.StartsWith("L0 length"))
umdlayer = line.Split(' ')[2];
else if (line.StartsWith("FileSize:"))
umdsize = Int64.Parse(line.Split(' ')[1]);
}
// If the L0 length is the size of the full disc, there's no layerbreak
if (Int64.Parse(umdlayer) * 2048 == umdsize)
umdlayer = null;
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
}
#endregion
}
}

View File

@@ -0,0 +1,174 @@
using System;
using System.IO;
namespace MPF.Utilities
{
/// <summary>
/// Big endian reading overloads for BinaryReader
/// </summary>
internal static class BinaryReaderExtensions
{
/// <summary>
/// Reads the specified number of bytes from the stream, starting from a specified point in the byte array.
/// </summary>
/// <param name="buffer">The buffer to read data into.</param>
/// <param name="index">The starting point in the buffer at which to begin reading into the buffer.</param>
/// <param name="count">The number of bytes to read.</param>
/// <returns>The number of bytes read into buffer. This might be less than the number of bytes requested if that many bytes are not available, or it might be zero if the end of the stream is reached.</returns>
public static int ReadBigEndian(this BinaryReader reader, byte[] buffer, int index, int count)
{
int retval = reader.Read(buffer, index, count);
Array.Reverse(buffer);
return retval;
}
/// <summary>
/// Reads the specified number of characters from the stream, starting from a specified point in the character array.
/// </summary>
/// <param name="buffer">The buffer to read data into.</param>
/// <param name="index">The starting point in the buffer at which to begin reading into the buffer.</param>
/// <param name="count">The number of characters to read.</param>
/// <returns>The total number of characters read into the buffer. This might be less than the number of characters requested if that many characters are not currently available, or it might be zero if the end of the stream is reached.</returns>
public static int ReadBigEndian(this BinaryReader reader, char[] buffer, int index, int count)
{
int retval = reader.Read(buffer, index, count);
Array.Reverse(buffer);
return retval;
}
/// <summary>
/// Reads the specified number of bytes from the current stream into a byte array and advances the current position by that number of bytes.
/// </summary>
/// <param name="count">The number of bytes to read. This value must be 0 or a non-negative number or an exception will occur.</param>
/// <returns>A byte array containing data read from the underlying stream. This might be less than the number of bytes requested if the end of the stream is reached.</returns>
public static byte[] ReadBytesBigEndian(this BinaryReader reader, int count)
{
byte[] retval = reader.ReadBytes(count);
Array.Reverse(retval);
return retval;
}
/// <summary>
/// Reads the specified number of characters from the current stream, returns the data in a character array, and advances the current position in accordance with the Encoding used and the specific character being read from the stream.
/// </summary>
/// <param name="count">The number of characters to read. This value must be 0 or a non-negative number or an exception will occur.</param>
/// <returns>A character array containing data read from the underlying stream. This might be less than the number of bytes requested if the end of the stream is reached.</returns>
public static char[] ReadCharsBigEndian(this BinaryReader reader, int count)
{
char[] retval = reader.ReadChars(count);
Array.Reverse(retval);
return retval;
}
/// <summary>
/// Reads a decimal value from the current stream and advances the current position of the stream by sixteen bytes.
/// </summary>
/// <returns>A decimal value read from the current stream.</returns>
public static decimal ReadDecimalBigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(16);
Array.Reverse(retval);
int i1 = BitConverter.ToInt32(retval, 0);
int i2 = BitConverter.ToInt32(retval, 4);
int i3 = BitConverter.ToInt32(retval, 8);
int i4 = BitConverter.ToInt32(retval, 12);
return new decimal(new int[] { i1, i2, i3, i4 });
}
/// <summary>
/// eads an 8-byte floating point value from the current stream and advances the current position of the stream by eight bytes.
/// </summary>
/// <returns>An 8-byte floating point value read from the current stream.</returns>
public static double ReadDoubleBigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(8);
Array.Reverse(retval);
return BitConverter.ToDouble(retval, 0);
}
/// <summary>
/// Reads a 2-byte signed integer from the current stream and advances the current position of the stream by two bytes.
/// </summary>
/// <returns>A 2-byte signed integer read from the current stream.</returns>
public static short ReadInt16BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(2);
Array.Reverse(retval);
return BitConverter.ToInt16(retval, 0);
}
/// <summary>
/// Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes.
/// </summary>
/// <returns>A 4-byte signed integer read from the current stream.</returns>
public static int ReadInt32BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(4);
Array.Reverse(retval);
return BitConverter.ToInt32(retval, 0);
}
/// <summary>
/// Reads an 8-byte signed integer from the current stream and advances the current position of the stream by eight bytes.
/// </summary>
/// <returns>An 8-byte signed integer read from the current stream.</returns>
public static long ReadInt64BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(8);
Array.Reverse(retval);
return BitConverter.ToInt64(retval, 0);
}
/// <summary>
/// Reads a 4-byte floating point value from the current stream and advances the current position of the stream by four bytes.
/// </summary>
/// <returns>A 4-byte floating point value read from the current stream.</returns>
public static float ReadSingleBigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(4);
Array.Reverse(retval);
return BitConverter.ToSingle(retval, 0);
}
/// <summary>
/// Reads a 2-byte unsigned integer from the current stream using little-endian encoding and advances the position of the stream by two bytes.
///
/// This API is not CLS-compliant.
/// </summary>
/// <returns>A 2-byte unsigned integer read from this stream.</returns>
public static ushort ReadUInt16BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(2);
Array.Reverse(retval);
return BitConverter.ToUInt16(retval, 0);
}
/// <summary>
/// Reads a 4-byte unsigned integer from the current stream and advances the position of the stream by four bytes.
///
/// This API is not CLS-compliant.
/// </summary>
/// <returns>A 4-byte unsigned integer read from this stream.</returns>
public static uint ReadUInt32BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(4);
Array.Reverse(retval);
return BitConverter.ToUInt32(retval, 0);
}
/// <summary>
/// Reads an 8-byte unsigned integer from the current stream and advances the position of the stream by eight bytes.
///
/// This API is not CLS-compliant.
/// </summary>
/// <returns>An 8-byte unsigned integer read from this stream.</returns>
public static ulong ReadUInt64BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(8);
Array.Reverse(retval);
return BitConverter.ToUInt64(retval, 0);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,119 @@
using MPF.Data;
namespace MPF.Utilities
{
public static class EnumExtensions
{
/// <summary>
/// Determine the category based on the system
/// </summary>
/// <param name="system">KnownSystem value to check</param>
/// <returns>KnownSystemCategory related to the system</returns>
public static KnownSystemCategory Category(this KnownSystem? system)
{
if (system < KnownSystem.MarkerDiscBasedConsoleEnd)
return KnownSystemCategory.DiscBasedConsole;
/*
else if (system < KnownSystem.MarkerOtherConsoleEnd)
return KnownSystemCategory.OtherConsole;
*/
else if (system < KnownSystem.MarkerComputerEnd)
return KnownSystemCategory.Computer;
else if (system < KnownSystem.MarkerArcadeEnd)
return KnownSystemCategory.Arcade;
else if (system < KnownSystem.MarkerOtherEnd)
return KnownSystemCategory.Other;
else
return KnownSystemCategory.Custom;
}
/// <summary>
/// Determine if the media supports drive speeds
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>True if the media has variable dumping speeds, false otherwise</returns>
public static bool DoesSupportDriveSpeed(this MediaType? type)
{
switch (type)
{
case MediaType.CDROM:
case MediaType.DVD:
case MediaType.GDROM:
case MediaType.HDDVD:
case MediaType.BluRay:
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
return true;
default:
return false;
}
}
/// <summary>
/// Determine if a system is considered audio-only
/// </summary>
/// <param name="system">KnownSystem value to check</param>
/// <returns>True if the system is audio-only, false otherwise</returns>
/// <remarks>
/// Philips CD-i should NOT be in this list. It's being included until there's a
/// reasonable distinction between CD-i and CD-i ready on the database side.
/// </remarks>
public static bool IsAudio(this KnownSystem? system)
{
switch (system)
{
case KnownSystem.AtariJaguarCD:
case KnownSystem.AudioCD:
case KnownSystem.DVDAudio:
case KnownSystem.HasbroVideoNow:
case KnownSystem.HasbroVideoNowColor:
case KnownSystem.HasbroVideoNowJr:
case KnownSystem.HasbroVideoNowXP:
case KnownSystem.PhilipsCDi:
case KnownSystem.SuperAudioCD:
return true;
default:
return false;
}
}
/// <summary>
/// Determine if a system is a marker value
/// </summary>
/// <param name="system">KnownSystem value to check</param>
/// <returns>True if the system is a marker value, false otherwise</returns>
public static bool IsMarker(this KnownSystem? system)
{
switch (system)
{
case KnownSystem.MarkerArcadeEnd:
case KnownSystem.MarkerComputerEnd:
case KnownSystem.MarkerDiscBasedConsoleEnd:
// case KnownSystem.MarkerOtherConsoleEnd:
case KnownSystem.MarkerOtherEnd:
return true;
default:
return false;
}
}
/// <summary>
/// Determine if a system is considered XGD
/// </summary>
/// <param name="system">KnownSystem value to check</param>
/// <returns>True if the system is XGD, false otherwise</returns>
public static bool IsXGD(this KnownSystem? system)
{
switch (system)
{
case KnownSystem.MicrosoftXBOX:
case KnownSystem.MicrosoftXBOX360:
case KnownSystem.MicrosoftXBOXOne:
case KnownSystem.MicrosoftXboxSeriesXS:
return true;
default:
return false;
}
}
}
}

View File

@@ -0,0 +1,122 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MPF.Utilities
{
internal static class Logging
{
/// <summary>
/// Process a chunk of text and send it to a handler
/// </summary>
/// <param name="reader">TextReader representing the input</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string> handler)
{
// Initialize the required variables
char[] buffer = new char[256];
int read = 0;
var sb = new StringBuilder();
try
{
while (true)
{
// Try to read the next chunk of characters
read = await reader.ReadAsync(buffer, 0, buffer.Length);
if (read == 0)
{
Thread.Sleep(10);
continue;
}
// Convert the characters into a string
string line = new string(buffer, 0, read);
// If we have no newline characters, store in the string builder
if (!line.Contains('\r') && !line.Contains('\n'))
sb.Append(line);
// If we have a newline, append and log
else if (line.Contains('\n') || line.Contains("\r\n"))
ProcessNewLines(sb, line, baseClass, handler);
// If we have a carriage return only, append and log first and last instances
else if (line.Contains('\r'))
ProcessCarriageReturns(sb, line, baseClass, handler);
}
}
catch { }
finally
{
handler?.Invoke(baseClass, sb.ToString());
}
}
/// <summary>
/// Process a chunk that contains newlines
/// </summary>
/// <param name="sb">StringBuilder to write from and append to</param>
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
{
line = line.Replace("\r\n", "\n");
var split = line.Split('\n');
for (int i = 0; i < split.Length; i++)
{
// If the chunk contains a carriage return, handle it like a separate line
if (split[i].Contains('\r'))
{
ProcessCarriageReturns(sb, split[i], baseClass, handler);
continue;
}
// For the first item, append to anything existing and then write out
if (i == 0)
{
sb.Append(split[i]);
handler?.Invoke(baseClass, sb.ToString());
sb.Clear();
}
// For the last item, just append so it's dealt with the next time
else if (i == split.Length - 1)
{
sb.Append(split[i]);
}
// For everything else, directly write out
else
{
handler?.Invoke(baseClass, split[i]);
}
}
}
/// <summary>
/// Process a chunk that contains carriage returns
/// </summary>
/// <param name="sb">StringBuilder to write from and append to</param>
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
{
var split = line.Split('\r');
// Append and log the first
sb.Append(split[0]);
handler?.Invoke(baseClass, sb.ToString());
// Append the last
sb.Clear();
sb.Append($"\r{split[split.Length - 1]}");
}
}
}

View File

@@ -0,0 +1,116 @@
using System;
using System.Reflection;
using MPF.Redump;
namespace MPF.Utilities
{
public static class Tools
{
#region Byte Arrays
/// <summary>
/// Search for a byte array in another array
/// </summary>
public static bool Contains(this byte[] stack, byte[] needle, out int position, int start = 0, int end = -1)
{
// Initialize the found position to -1
position = -1;
// If either array is null or empty, we can't do anything
if (stack == null || stack.Length == 0 || needle == null || needle.Length == 0)
return false;
// If the needle array is larger than the stack array, it can't be contained within
if (needle.Length > stack.Length)
return false;
// If start or end are not set properly, set them to defaults
if (start < 0)
start = 0;
if (end < 0)
end = stack.Length - needle.Length;
for (int i = start; i < end; i++)
{
if (stack.EqualAt(needle, i))
{
position = i;
return true;
}
}
return false;
}
/// <summary>
/// See if a byte array starts with another
/// </summary>
public static bool StartsWith(this byte[] stack, byte[] needle)
{
return stack.Contains(needle, out int _, start: 0, end: 1);
}
/// <summary>
/// Get if a stack at a certain index is equal to a needle
/// </summary>
private static bool EqualAt(this byte[] stack, byte[] needle, int index)
{
// If we're too close to the end of the stack, return false
if (needle.Length >= stack.Length - index)
return false;
for (int i = 0; i < needle.Length; i++)
{
if (stack[i + index] != needle[i])
return false;
}
return true;
}
#endregion
#region Versioning
/// <summary>
/// Check for a new MPF version
/// </summary>
/// <returns>
/// Bool representing if the values are different.
/// String representing the message to display the the user.
/// String representing the new release URL.
/// </returns>
public static (bool different, string message, string url) CheckForNewVersion()
{
// Get current assembly version
var assemblyVersion = Assembly.GetEntryAssembly().GetName().Version;
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}" + (assemblyVersion.Build != 0 ? $".{assemblyVersion.Build}" : string.Empty);
// Get the latest tag from GitHub
using (var client = new RedumpWebClient())
{
(string tag, string url) = client.GetRemoteVersionAndUrl();
bool different = version != tag;
string message = $"Local version: {version}"
+ $"{Environment.NewLine}Remote version: {tag}"
+ (different
? $"{Environment.NewLine}The update URL has been added copied to your clipboard"
: $"{Environment.NewLine}You have the newest version!");
return (different, message, url);
}
}
/// <summary>
/// Get the current informational version formatted as a string
/// </summary>
public static string GetCurrentVersion()
{
var assemblyVersion = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
return assemblyVersion.InformationalVersion;
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

37
MPF.Test/MPF.Test.csproj Normal file
View File

@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj">
<Project>{51ab0928-13f9-44bf-a407-b6957a43a056}</Project>
<Name>MPF.Library</Name>
</ProjectReference>
<ProjectReference Include="..\MPF\MPF.csproj">
<Project>{7b1b75eb-8940-466f-bd51-76471a57f9be}</Project>
<Name>MPF</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="16.11.0-release-20210626-04" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0-release-20210626-04" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
<PackageReference Include="xunit.assert" Version="2.4.1" />
<PackageReference Include="xunit.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageReference Include="xunit.runner.console" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

30
MPF.Test/ResultTest.cs Normal file
View File

@@ -0,0 +1,30 @@
using MPF.Data;
using Xunit;
namespace MPF.Test
{
public class ResultTest
{
[Fact]
public void ResultSuccessTest()
{
Result actual = Result.Success();
Assert.Empty(actual.Message);
string message = "Success!";
actual = Result.Success(message);
Assert.Equal(message, actual.Message);
}
[Fact]
public void ResultFailureTest()
{
Result actual = Result.Failure();
Assert.Empty(actual.Message);
string message = "Failure!";
actual = Result.Failure(message);
Assert.Equal(message, actual.Message);
}
}
}

View File

@@ -0,0 +1,21 @@
using System.Linq;
using MPF.Data;
using Xunit;
namespace MPF.Test.Data
{
public class UIElementsTest
{
[Theory]
[InlineData(MediaType.CDROM, 72)]
[InlineData(MediaType.DVD, 24)]
[InlineData(MediaType.BluRay, 16)]
[InlineData(MediaType.LaserDisc, 1)]
[InlineData(null, 1)]
public void GetAllowedDriveSpeedForMediaTypeTest(MediaType? mediaType, int maxExpected)
{
var actual = Interface.GetSpeedsForMediaType(mediaType);
Assert.Equal(maxExpected, actual.Last());
}
}
}

View File

@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Data;
using MPF.Utilities;
using Xunit;
namespace MPF.Test.Utilities
{
public class ConvertersTest
{
/// <summary>
/// Set of all known systems for testing
/// </summary>
public static IEnumerable<object[]> KnownSystems = KnownSystemComboBoxItem.GenerateElements().Select(e => new object[] { e });
[Theory]
[InlineData(DiscImageCreator.CommandStrings.Audio, MediaType.CDROM)]
[InlineData(DiscImageCreator.CommandStrings.BluRay, MediaType.BluRay)]
[InlineData(DiscImageCreator.CommandStrings.Close, null)]
[InlineData(DiscImageCreator.CommandStrings.CompactDisc, MediaType.CDROM)]
[InlineData(DiscImageCreator.CommandStrings.Data, MediaType.CDROM)]
[InlineData(DiscImageCreator.CommandStrings.DigitalVideoDisc, MediaType.DVD)]
[InlineData(DiscImageCreator.CommandStrings.Eject, null)]
[InlineData(DiscImageCreator.CommandStrings.Floppy, MediaType.FloppyDisk)]
[InlineData(DiscImageCreator.CommandStrings.GDROM, MediaType.GDROM)]
[InlineData(DiscImageCreator.CommandStrings.MDS, null)]
[InlineData(DiscImageCreator.CommandStrings.Reset, null)]
[InlineData(DiscImageCreator.CommandStrings.SACD, MediaType.CDROM)]
[InlineData(DiscImageCreator.CommandStrings.Start, null)]
[InlineData(DiscImageCreator.CommandStrings.Stop, null)]
[InlineData(DiscImageCreator.CommandStrings.Sub, null)]
[InlineData(DiscImageCreator.CommandStrings.Swap, MediaType.GDROM)]
[InlineData(DiscImageCreator.CommandStrings.XBOX, MediaType.DVD)]
public void BaseCommandToMediaTypeTest(string command, MediaType? expected)
{
MediaType? actual = DiscImageCreator.Converters.ToMediaType(command);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(DiscImageCreator.CommandStrings.Audio, KnownSystem.AudioCD)]
[InlineData(DiscImageCreator.CommandStrings.BluRay, KnownSystem.SonyPlayStation3)]
[InlineData(DiscImageCreator.CommandStrings.Close, null)]
[InlineData(DiscImageCreator.CommandStrings.CompactDisc, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.CommandStrings.Data, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.CommandStrings.DigitalVideoDisc, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.CommandStrings.Eject, null)]
[InlineData(DiscImageCreator.CommandStrings.Floppy, KnownSystem.IBMPCCompatible)]
[InlineData(DiscImageCreator.CommandStrings.GDROM, KnownSystem.SegaDreamcast)]
[InlineData(DiscImageCreator.CommandStrings.MDS, null)]
[InlineData(DiscImageCreator.CommandStrings.Reset, null)]
[InlineData(DiscImageCreator.CommandStrings.SACD, KnownSystem.SuperAudioCD)]
[InlineData(DiscImageCreator.CommandStrings.Start, null)]
[InlineData(DiscImageCreator.CommandStrings.Stop, null)]
[InlineData(DiscImageCreator.CommandStrings.Sub, null)]
[InlineData(DiscImageCreator.CommandStrings.Swap, KnownSystem.SegaDreamcast)]
[InlineData(DiscImageCreator.CommandStrings.XBOX, KnownSystem.MicrosoftXBOX)]
public void BaseCommandToKnownSystemTest(string command, KnownSystem? expected)
{
KnownSystem? actual = DiscImageCreator.Converters.ToKnownSystem(command);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(MediaType.CDROM, ".bin")]
[InlineData(MediaType.DVD, ".iso")]
[InlineData(MediaType.LaserDisc, ".raw")]
[InlineData(MediaType.NintendoWiiUOpticalDisc, ".wud")]
[InlineData(MediaType.FloppyDisk, ".img")]
[InlineData(MediaType.Cassette, ".wav")]
[InlineData(MediaType.NONE, null)]
public void MediaTypeToExtensionTest(MediaType? mediaType, string expected)
{
string actual = DiscImageCreator.Converters.Extension(mediaType);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(MediaType.CDROM, "CD-ROM")]
[InlineData(MediaType.LaserDisc, "LD-ROM / LV-ROM")]
[InlineData(MediaType.NONE, "Unknown")]
public void MediaTypeToStringTest(MediaType? mediaType, string expected)
{
string actual = Converters.LongName(mediaType);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(KnownSystem.MicrosoftXBOX, "Microsoft XBOX")]
[InlineData(KnownSystem.NECPC88, "NEC PC-88")]
[InlineData(KnownSystem.KonamiPython, "Konami Python")]
[InlineData(KnownSystem.HDDVDVideo, "HD-DVD-Video")]
[InlineData(KnownSystem.NONE, "Unknown")]
public void KnownSystemToStringTest(KnownSystem? knownSystem, string expected)
{
string actual = Converters.LongName(knownSystem);
Assert.Equal(expected, actual);
}
[Theory]
[MemberData(nameof(KnownSystems))]
public void KnownSystemHasValidCategory(KnownSystemComboBoxItem system)
{
KnownSystem[] markers = { KnownSystem.MarkerArcadeEnd, KnownSystem.MarkerDiscBasedConsoleEnd, /* KnownSystem.MarkerOtherConsoleEnd, */ KnownSystem.MarkerComputerEnd, KnownSystem.MarkerOtherEnd };
// Non-system items won't map
if (system.IsHeader)
return;
// NONE will never map
if (system == KnownSystem.NONE)
return;
// we check that the category is the first category value higher than the system
KnownSystemCategory category = ((KnownSystem?)system).Category();
KnownSystem marker = KnownSystem.NONE;
switch (category)
{
case KnownSystemCategory.Arcade: marker = KnownSystem.MarkerArcadeEnd; break;
case KnownSystemCategory.DiscBasedConsole: marker = KnownSystem.MarkerDiscBasedConsoleEnd; break;
/* case KnownSystemCategory.OtherConsole: marker = KnownSystem.MarkerOtherConsoleEnd; break; */
case KnownSystemCategory.Computer: marker = KnownSystem.MarkerComputerEnd; break;
case KnownSystemCategory.Other: marker = KnownSystem.MarkerOtherEnd; break;
}
Assert.NotEqual(KnownSystem.NONE, marker);
Assert.True(marker > system);
Array.ForEach(markers, mmarker =>
{
// a marker can be the same of the found one, or one of a category before or a category after but never in the middle between
// the system and the mapped category
Assert.True(mmarker == marker || mmarker < system || mmarker > marker);
});
}
}
}

View File

@@ -0,0 +1,88 @@
using System.IO;
using MPF.Data;
using MPF.Utilities;
using Xunit;
namespace MPF.Test
{
public class DumpEnvironmentTest
{
[Theory]
[InlineData(null, 'D', false, MediaType.NONE, false)]
[InlineData("", 'D', false, MediaType.NONE, false)]
[InlineData("cd F test.bin 8 /c2 20", 'F', false, MediaType.CDROM, true)]
[InlineData("fd A test.img", 'A', true, MediaType.FloppyDisk, true)]
[InlineData("dvd X test.iso 8 /raw", 'X', false, MediaType.FloppyDisk, false)]
[InlineData("stop D", 'D', false, MediaType.DVD, true)]
public void ParametersValidTest(string parameters, char letter, bool isFloppy, MediaType? mediaType, bool expected)
{
var options = new Options() { InternalProgram = InternalProgram.DiscImageCreator };
var drive = isFloppy
? new Drive(InternalDriveType.Floppy, new DriveInfo(letter.ToString()))
: new Drive(InternalDriveType.Optical, new DriveInfo(letter.ToString()));
var env = new DumpEnvironment(options, string.Empty, string.Empty, drive, KnownSystem.IBMPCCompatible, mediaType, parameters);
bool actual = env.ParametersValid();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(null, null, null, null)]
[InlineData(" ", "", " ", "")]
[InlineData("super", "blah.bin", "super", "blah.bin")]
[InlineData("super\\hero", "blah.bin", "super\\hero", "blah.bin")]
[InlineData("super.hero", "blah.bin", "super.hero", "blah.bin")]
[InlineData("superhero", "blah.rev.bin", "superhero", "blah.rev.bin")]
[InlineData("super&hero", "blah.bin", "super&hero", "blah.bin")]
[InlineData("superhero", "blah&foo.bin", "superhero", "blah&foo.bin")]
public void FixOutputPathsTest(string outputDirectory, string outputFilename, string expectedOutputDirectory, string expectedOutputFilename)
{
(string actualOutputDirectory, string actualOutputFilename) = DumpEnvironment.NormalizeOutputPaths(outputDirectory, outputFilename, false);
Assert.Equal(expectedOutputDirectory, actualOutputDirectory);
Assert.Equal(expectedOutputFilename, actualOutputFilename);
}
[Fact]
public void GetFirstTrackTest()
{
// TODO: Implement
Assert.True(true);
}
[Fact]
public void FormatOutputDataTest()
{
// TODO: Implement
Assert.True(true);
}
[Fact]
public void WriteOutputDataTest()
{
// TODO: Implement
Assert.True(true);
}
[Fact]
public void EjectDiscTest()
{
// TODO: Implement
Assert.True(true);
}
[Fact]
public void CancelDumpingTest()
{
// TODO: Implement
Assert.True(true);
}
[Fact]
public void StartDumpingTest()
{
// TODO: Implement
Assert.True(true);
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
using MPF.Data;
using MPF.Utilities;
using Xunit;
namespace MPF.Test.Utilities
{
public class KnownSystemExtensionsTest
{
[Fact]
public void IsMarkerTest()
{
var values = (KnownSystem[])Enum.GetValues(typeof(KnownSystem));
foreach(var system in values)
{
bool expected = system == KnownSystem.MarkerArcadeEnd || system == KnownSystem.MarkerComputerEnd ||
system == KnownSystem.MarkerOtherEnd || system == KnownSystem.MarkerDiscBasedConsoleEnd;
// || system == KnownSystem.MarkerOtherConsoleEnd;
bool actual = ((KnownSystem?)system).IsMarker();
Assert.Equal(expected, actual);
}
}
[Fact]
public void CategoryNameNotEmptyTest()
{
var values = (KnownSystemCategory[])Enum.GetValues(typeof(KnownSystemCategory));
foreach (var system in values)
{
string actual = ((KnownSystem?)system).LongName();
Assert.NotEqual("", actual);
}
}
}
}

View File

@@ -0,0 +1,46 @@
using MPF.Data;
using MPF.Utilities;
using Xunit;
namespace MPF.Test.Utilities
{
public class MediaTypeExtensionsTest
{
[Theory]
[InlineData(MediaType.CDROM, "CD-ROM")]
[InlineData(MediaType.LaserDisc, "LD-ROM / LV-ROM")]
[InlineData(MediaType.NONE, "Unknown")]
public void NameTest(MediaType? mediaType, string expected)
{
string actual = mediaType.LongName();
Assert.NotNull(actual);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(MediaType.CDROM, ".bin")]
[InlineData(MediaType.DVD, ".iso")]
[InlineData(MediaType.LaserDisc, ".raw")]
[InlineData(MediaType.FloppyDisk, ".img")]
[InlineData(MediaType.NONE, null)]
public void ExtensionTest(MediaType? mediaType, string expected)
{
string actual = DiscImageCreator.Converters.Extension(mediaType);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(MediaType.CDROM, true)]
[InlineData(MediaType.DVD, true)]
[InlineData(MediaType.FloppyDisk, false)]
[InlineData(MediaType.BluRay, true)]
[InlineData(MediaType.LaserDisc, false)]
public void DriveSpeedSupportedTest(MediaType? mediaType, bool expected)
{
bool actual = mediaType.DoesSupportDriveSpeed();
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,71 @@
using System.Collections.Generic;
using System.Linq;
using MPF.Data;
using MPF.DiscImageCreator;
using MPF.Utilities;
using Xunit;
namespace MPF.Test.Utilities
{
public class ParametersTest
{
[Theory]
[InlineData(KnownSystem.MicrosoftXBOX, MediaType.CDROM, CommandStrings.CompactDisc)]
[InlineData(KnownSystem.MicrosoftXBOX, MediaType.DVD, CommandStrings.XBOX)]
[InlineData(KnownSystem.MicrosoftXBOX, MediaType.LaserDisc, null)]
[InlineData(KnownSystem.SegaNu, MediaType.BluRay, CommandStrings.BluRay)]
[InlineData(KnownSystem.AppleMacintosh, MediaType.FloppyDisk, CommandStrings.Floppy)]
[InlineData(KnownSystem.RawThrillsVarious, MediaType.GDROM, null)]
public void ParametersFromSystemAndTypeTest(KnownSystem? knownSystem, MediaType? mediaType, string expected)
{
var options = new Options { };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
Assert.Equal(expected, actual.BaseCommand);
}
[Theory]
[InlineData(KnownSystem.AppleMacintosh, MediaType.LaserDisc, true, 20, null, null)]
[InlineData(KnownSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, false, 20, null, new string[] { FlagStrings.Raw })]
[InlineData(KnownSystem.IBMPCCompatible, MediaType.DVD, false, 20, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
/* paranoid mode tests */
[InlineData(KnownSystem.IBMPCCompatible, MediaType.CDROM, true, 1000, 2, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
[InlineData(KnownSystem.AppleMacintosh, MediaType.CDROM, false, 20, null, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
[InlineData(KnownSystem.IBMPCCompatible, MediaType.DVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
[InlineData(KnownSystem.HDDVDVideo, MediaType.HDDVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
[InlineData(KnownSystem.IBMPCCompatible, MediaType.DVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
[InlineData(KnownSystem.HDDVDVideo, MediaType.HDDVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
/* reread c2 */
[InlineData(KnownSystem.SegaDreamcast, MediaType.GDROM, false, 1000, null, new string[] { FlagStrings.C2Opcode })]
[InlineData(KnownSystem.SegaDreamcast, MediaType.GDROM, false, -1, null, new string[] { FlagStrings.C2Opcode })]
public void ParametersFromOptionsTest(KnownSystem? knownSystem, MediaType? mediaType, bool paranoid, int rereadC2, int? subchannelLevel, string[] expected)
{
var options = new Options { DICParanoidMode = paranoid, DICRereadCount = rereadC2 };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
HashSet<string> actualSet = new HashSet<string>(actual.Keys.Cast<string>() ?? new string[0]);
Assert.Equal(expectedSet, actualSet);
if (rereadC2 == -1 || !Validators.GetValidMediaTypes(knownSystem).Contains(mediaType))
Assert.Null(actual.C2OpcodeValue[0]);
else
Assert.Equal(rereadC2, actual.C2OpcodeValue[0]);
Assert.Equal(subchannelLevel, actual.SubchannelReadLevelValue);
}
[Theory]
[InlineData(null, false)]
[InlineData("", false)]
[InlineData("cd F test.bin 8 /c2 20", true)]
[InlineData("fd A test.img", true)]
[InlineData("dvd X super\\test.iso 8 /raw", true)]
[InlineData("bd D longer\\path_test.iso 16", true)]
[InlineData("stop D", true)]
[InlineData("ls", false)]
public void ValidateParametersTest(string parameters, bool expected)
{
var actual = new Parameters(parameters);
Assert.Equal(expected, actual.IsValid());
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Linq;
using MPF.Data;
using MPF.Utilities;
using Xunit;
namespace MPF.Test.Utilities
{
public class ValidatorsTest
{
[Theory]
[InlineData(KnownSystem.BandaiApplePippin, MediaType.CDROM)]
[InlineData(KnownSystem.MicrosoftXBOX, MediaType.DVD)]
[InlineData(KnownSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc)]
[InlineData(KnownSystem.NintendoWii, MediaType.NintendoWiiOpticalDisc)]
[InlineData(KnownSystem.NintendoWiiU, MediaType.NintendoWiiUOpticalDisc)]
[InlineData(KnownSystem.SonyPlayStationPortable, MediaType.UMD)]
public void GetValidMediaTypesTest(KnownSystem? knownSystem, MediaType? expected)
{
var actual = Validators.GetValidMediaTypes(knownSystem);
Assert.Contains(expected, actual);
}
[Fact]
public void CreateListOfSystemsTest()
{
int expected = Enum.GetValues(typeof(KnownSystem)).Length;
var actual = KnownSystemComboBoxItem.GenerateElements().ToList();
Assert.Equal(expected, actual.Count);
}
[Fact]
public void CreateListOfDrivesTest()
{
// TODO: Implement
Assert.True(true);
}
[Fact]
public void GetDiscTypeTest()
{
// TODO: Implement
Assert.True(true);
}
}
}

50
MPF.sln Normal file
View File

@@ -0,0 +1,50 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.156
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF", "MPF\MPF.csproj", "{7B1B75EB-8940-466F-BD51-76471A57F9BE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Test", "MPF.Test\MPF.Test.csproj", "{7CC064D2-38AB-4A05-8519-28660DE4562A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Library", "MPF.Library\MPF.Library.csproj", "{51AB0928-13F9-44BF-A407-B6957A43A056}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Check", "MPF.Check\MPF.Check.csproj", "{8CFDE289-E171-4D49-A40D-5293265C1253}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{4D1DCF5A-F0B0-4E81-A05B-F1A7D37C9D9D}"
ProjectSection(SolutionItems) = preProject
appveyor.yml = appveyor.yml
CHANGELIST.md = CHANGELIST.md
README.md = README.md
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7B1B75EB-8940-466F-BD51-76471A57F9BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7B1B75EB-8940-466F-BD51-76471A57F9BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B1B75EB-8940-466F-BD51-76471A57F9BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B1B75EB-8940-466F-BD51-76471A57F9BE}.Release|Any CPU.Build.0 = Release|Any CPU
{7CC064D2-38AB-4A05-8519-28660DE4562A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7CC064D2-38AB-4A05-8519-28660DE4562A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7CC064D2-38AB-4A05-8519-28660DE4562A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7CC064D2-38AB-4A05-8519-28660DE4562A}.Release|Any CPU.Build.0 = Release|Any CPU
{51AB0928-13F9-44BF-A407-B6957A43A056}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{51AB0928-13F9-44BF-A407-B6957A43A056}.Debug|Any CPU.Build.0 = Debug|Any CPU
{51AB0928-13F9-44BF-A407-B6957A43A056}.Release|Any CPU.ActiveCfg = Release|Any CPU
{51AB0928-13F9-44BF-A407-B6957A43A056}.Release|Any CPU.Build.0 = Release|Any CPU
{8CFDE289-E171-4D49-A40D-5293265C1253}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CFDE289-E171-4D49-A40D-5293265C1253}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CFDE289-E171-4D49-A40D-5293265C1253}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CFDE289-E171-4D49-A40D-5293265C1253}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {73C62E6A-6584-4D93-83B5-ECB1FBDB469B}
EndGlobalSection
EndGlobal

772
MPF/App.xaml Normal file
View File

@@ -0,0 +1,772 @@
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MPF"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero" x:Class="MPF.App"
StartupUri="Windows\MainWindow.xaml">
<Application.Resources>
<!-- Button -->
<SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
<SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
<Style x:Key="CustomButtonStyle" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource Button.Static.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource Button.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{DynamicResource Button.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="border" Value="{DynamicResource Button.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.Pressed.Border}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="{DynamicResource Button.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource Button.Disabled.Border}"/>
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{DynamicResource Button.Disabled.Foreground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ComboBox -->
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<LinearGradientBrush x:Key="ComboBox.Static.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF0F0F0" Offset="0.0"/>
<GradientStop Color="#FFE5E5E5" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Static.Border" Color="#FFACACAC"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Border" Color="#FFABADB3"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Glyph" Color="#FF000000"/>
<LinearGradientBrush x:Key="ComboBox.MouseOver.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFECF4FC" Offset="0.0"/>
<GradientStop Color="#FFDCECFC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.MouseOver.Border" Color="#FF7EB4EA"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Border" Color="#FF7EB4EA"/>
<LinearGradientBrush x:Key="ComboBox.MouseOver.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFEBF4FC" Offset="0.0"/>
<GradientStop Color="#FFDCECFC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Button.Border" Color="#FF7EB4EA"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Glyph" Color="#FF000000"/>
<LinearGradientBrush x:Key="ComboBox.Pressed.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFDAECFC" Offset="0.0"/>
<GradientStop Color="#FFC4E0FC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Pressed.Border" Color="#FF569DE5"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Border" Color="#FF569DE5"/>
<LinearGradientBrush x:Key="ComboBox.Pressed.Editable.Button.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFDAEBFC" Offset="0.0"/>
<GradientStop Color="#FFC4E0FC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Button.Border" Color="#FF569DE5"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Glyph" Color="#FFBFBFBF"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Background" Color="#FFF0F0F0"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Border" Color="#FFD9D9D9"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Border" Color="#FFBFBFBF"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060"/>
<Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="ClickMode" Value="Press"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="templateRoot" BorderBrush="{DynamicResource ComboBox.Static.Border}" BorderThickness="{TemplateBinding BorderThickness}" Background="{DynamicResource ComboBox.Static.Background}" SnapsToDevicePixels="true">
<Border x:Name="splitBorder" BorderBrush="Transparent" BorderThickness="1" HorizontalAlignment="Right" Margin="0" SnapsToDevicePixels="true" Width="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}">
<Path x:Name="arrow" Data="F1 M 0,0 L 2.667,2.66665 L 5.3334,0 L 5.3334,-1.78168 L 2.6667,0.88501 L0,-1.78168 L0,0 Z" Fill="{DynamicResource ComboBox.Static.Glyph}" HorizontalAlignment="Center" Margin="0" VerticalAlignment="Center"/>
</Border>
</Border>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.Static.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.Static.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{DynamicResource ComboBox.Static.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{DynamicResource ComboBox.Static.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="arrow" Value="{DynamicResource ComboBox.MouseOver.Glyph}"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.MouseOver.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.MouseOver.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.MouseOver.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{DynamicResource ComboBox.MouseOver.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{DynamicResource ComboBox.MouseOver.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Fill" TargetName="arrow" Value="{DynamicResource ComboBox.Pressed.Glyph}"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.Pressed.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.Pressed.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.Pressed.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{DynamicResource ComboBox.Pressed.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{DynamicResource ComboBox.Pressed.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" TargetName="arrow" Value="{DynamicResource ComboBox.Disabled.Glyph}"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.Disabled.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{DynamicResource ComboBox.Disabled.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{DynamicResource ComboBox.Disabled.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{DynamicResource ComboBox.Disabled.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{DynamicResource ComboBox.Disabled.Editable.Button.Border}"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{DynamicResource ComboBoxToggleButton}"/>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<SolidColorBrush x:Key="TextBox.Static.Background" Color="#FFFFFFFF"/>
<Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="MinWidth" Value="0"/>
<Setter Property="MinHeight" Value="0"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer x:Name="PART_ContentHost" Background="Transparent" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{DynamicResource ComboBoxToggleButton}"/>
<Border x:Name="border" Background="{DynamicResource TextBox.Static.Background}" Margin="{TemplateBinding BorderThickness}">
<TextBox x:Name="PART_EditableTextBox" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" Style="{DynamicResource ComboBoxEditableTextBox}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="CustomComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource ComboBox.Static.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ComboBox.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Padding" Value="6,3,5,3"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template" Value="{DynamicResource ComboBoxTemplate}"/>
<Style.Triggers>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Template" Value="{DynamicResource ComboBoxEditableTemplate}"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- ContextMenu -->
<SolidColorBrush x:Key="ContextMenu.Static.Border" Color="#FF888888"/>
<Style x:Key="CustomContextMenuStyle" TargetType="{x:Type ContextMenu}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Grid.IsSharedSizeScope" Value="true" />
<Setter Property="HasDropShadow" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border x:Name="Border" Background="{Binding Background}" BorderBrush="{DynamicResource ContextMenu.Static.Border}" BorderThickness="1">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" Value="true">
<Setter TargetName="Border" Property="Padding" Value="0,3,0,3" />
<Setter TargetName="Border" Property="CornerRadius" Value="4" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- CustomMessageBox -->
<SolidColorBrush x:Key="CustomMessageBox.Static.Background" Color="#FFE5E5E5"/>
<!-- MenuItem -->
<SolidColorBrush x:Key="MenuItem.SubMenu.Background" Color="#FFF0F0F0"/>
<SolidColorBrush x:Key="MenuItem.SubMenu.Border" Color="#FF999999"/>
<SolidColorBrush x:Key="MenuItem.SubMenu.HoverRectangle" Color="Transparent"/>
<ControlTemplate x:Key="CustomMenuItemTemplate" TargetType="{x:Type MenuItem}">
<Border x:Name="templateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<Grid VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ContentPresenter x:Name="Icon" Content="{TemplateBinding Icon}" ContentSource="Icon" HorizontalAlignment="Center" Height="16" Margin="3" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="Center" Width="16"/>
<Path x:Name="GlyphPanel" Data="F1M10,1.2L4.7,9.1 4.5,9.1 0,5.2 1.3,3.5 4.3,6.1 8.3,0 10,1.2z" Fill="{TemplateBinding Foreground}" FlowDirection="LeftToRight" Margin="3" Visibility="Collapsed" VerticalAlignment="Center"/>
<ContentPresenter ContentTemplate="{TemplateBinding HeaderTemplate}" Content="{TemplateBinding Header}" Grid.Column="1" ContentStringFormat="{TemplateBinding HeaderStringFormat}" ContentSource="Header" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom">
<Border x:Name="SubMenuBorder" BorderBrush="{DynamicResource MenuItem.SubMenu.Border}" BorderThickness="1" Background="{DynamicResource MenuItem.SubMenu.Background}" Padding="2">
<ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
<Grid RenderOptions.ClearTypeHint="Enabled">
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
</Canvas>
<Rectangle Fill="{DynamicResource MenuItem.SubMenu.HoverRectangle}" HorizontalAlignment="Left" Margin="29,2,0,2" Width="1"/>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Cycle" Grid.IsSharedSizeScope="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" KeyboardNavigation.TabNavigation="Cycle"/>
</Grid>
</ScrollViewer>
</Border>
</Popup>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSuspendingPopupAnimation" Value="True">
<Setter Property="PopupAnimation" TargetName="PART_Popup" Value="None"/>
</Trigger>
<Trigger Property="Icon" Value="{x:Null}">
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Visibility" TargetName="GlyphPanel" Value="Visible"/>
<Setter Property="Visibility" TargetName="Icon" Value="Collapsed"/>
</Trigger>
<Trigger Property="IsHighlighted" Value="True">
<Setter Property="Background" TargetName="templateRoot" Value="#3D26A0DA"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="#FF26A0DA"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="#FF707070"/>
<Setter Property="Fill" TargetName="GlyphPanel" Value="#FF707070"/>
</Trigger>
<Trigger Property="CanContentScroll" SourceName="SubMenuScrollViewer" Value="False">
<Setter Property="Canvas.Top" TargetName="OpaqueRect" Value="{Binding VerticalOffset, ElementName=SubMenuScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="OpaqueRect" Value="{Binding HorizontalOffset, ElementName=SubMenuScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<!-- ProgressBar -->
<SolidColorBrush x:Key="ProgressBar.Progress" Color="#FF06B025"/>
<SolidColorBrush x:Key="ProgressBar.Background" Color="#FFE6E6E6"/>
<SolidColorBrush x:Key="ProgressBar.Border" Color="#FFBCBCBC"/>
<Style x:Key="CustomProgressBarStyle" TargetType="{x:Type ProgressBar}">
<Setter Property="Foreground" Value="{DynamicResource ProgressBar.Progress}"/>
<Setter Property="Background" Value="{DynamicResource ProgressBar.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource ProgressBar.Border}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ProgressBar}">
<Grid x:Name="TemplateRoot">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Determinate"/>
<VisualState x:Name="Indeterminate">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="Animation">
<EasingDoubleKeyFrame KeyTime="0" Value="0.25"/>
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.25"/>
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="0.25"/>
</DoubleAnimationUsingKeyFrames>
<PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="Animation">
<EasingPointKeyFrame KeyTime="0" Value="-0.5,0.5"/>
<EasingPointKeyFrame KeyTime="0:0:1" Value="0.5,0.5"/>
<EasingPointKeyFrame KeyTime="0:0:2" Value="1.5,0.5"/>
</PointAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/>
<Rectangle x:Name="PART_Track"/>
<Grid x:Name="PART_Indicator" ClipToBounds="true" HorizontalAlignment="Left">
<Rectangle x:Name="Indicator" Fill="{TemplateBinding Foreground}"/>
<Rectangle x:Name="Animation" Fill="{TemplateBinding Foreground}" RenderTransformOrigin="0.5,0.5">
<Rectangle.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform/>
<RotateTransform/>
<TranslateTransform/>
</TransformGroup>
</Rectangle.RenderTransform>
</Rectangle>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Orientation" Value="Vertical">
<Setter Property="LayoutTransform" TargetName="TemplateRoot">
<Setter.Value>
<RotateTransform Angle="-90"/>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsIndeterminate" Value="true">
<Setter Property="Visibility" TargetName="Indicator" Value="Collapsed"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ScrollViewer -->
<SolidColorBrush x:Key="ScrollViewer.ScrollBar.Background" Color="LightGray"/>
<SolidColorBrush x:Key="ScrollViewer.ScrollBar.Foreground" Color="DarkGray"/>
<ControlTemplate x:Key="CustomScrollViewerControlStyle" TargetType="{x:Type ScrollViewer}">
<Grid x:Name="Grid" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
<ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" Background="{DynamicResource ScrollViewer.ScrollBar.Background}" Foreground="{DynamicResource ScrollViewer.ScrollBar.Foreground}"/>
<ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}" Background="{DynamicResource ScrollViewer.ScrollBar.Background}" Foreground="{DynamicResource ScrollViewer.ScrollBar.Foreground}"/>
</Grid>
</ControlTemplate>
<!-- TabControl -->
<SolidColorBrush x:Key="TabItem.Selected.Background" Color="#FFFFFF"/>
<SolidColorBrush x:Key="TabItem.Selected.Border" Color="#ACACAC"/>
<Style x:Key="CustomTabControlStyle" TargetType="{x:Type TabControl}">
<Setter Property="Padding" Value="2"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Background" Value="{DynamicResource TabItem.Selected.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TabItem.Selected.Border}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabControl}">
<Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="0"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<TabPanel x:Name="headerPanel" Background="Transparent" Grid.Column="0" IsItemsHost="true" Margin="2,2,2,0" Grid.Row="0" KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"/>
<Border x:Name="contentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local">
<ContentPresenter x:Name="PART_SelectedContentHost" ContentSource="SelectedContent" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TabStripPlacement" Value="Bottom">
<Setter Property="Grid.Row" TargetName="headerPanel" Value="1"/>
<Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
<Setter Property="Margin" TargetName="headerPanel" Value="2,0,2,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Left">
<Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
<Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="headerPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="contentPanel" Value="1"/>
<Setter Property="Width" TargetName="ColumnDefinition0" Value="Auto"/>
<Setter Property="Width" TargetName="ColumnDefinition1" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
<Setter Property="Margin" TargetName="headerPanel" Value="2,2,0,2"/>
</Trigger>
<Trigger Property="TabStripPlacement" Value="Right">
<Setter Property="Grid.Row" TargetName="headerPanel" Value="0"/>
<Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
<Setter Property="Grid.Column" TargetName="headerPanel" Value="1"/>
<Setter Property="Grid.Column" TargetName="contentPanel" Value="0"/>
<Setter Property="Width" TargetName="ColumnDefinition0" Value="*"/>
<Setter Property="Width" TargetName="ColumnDefinition1" Value="Auto"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="0"/>
<Setter Property="Margin" TargetName="headerPanel" Value="0,2,2,2"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- TabItem -->
<LinearGradientBrush x:Key="TabItem.Static.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#F0F0F0" Offset="0.0"/>
<GradientStop Color="#E5E5E5" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="TabItem.Static.Border" Color="#ACACAC"/>
<LinearGradientBrush x:Key="TabItem.MouseOver.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#ECF4FC" Offset="0.0"/>
<GradientStop Color="#DCECFC" Offset="1.0"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="TabItem.MouseOver.Border" Color="#7EB4EA"/>
<SolidColorBrush x:Key="TabItem.Disabled.Background" Color="#F0F0F0"/>
<SolidColorBrush x:Key="TabItem.Disabled.Border" Color="#D9D9D9"/>
<Style x:Key="CustomTabItemStyle" TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
<Setter Property="Background" Value="{DynamicResource TabItem.Static.Background}"/>
<Setter Property="BorderBrush" Value="{DynamicResource TabItem.Static.Border}"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="6,2,6,2"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Border x:Name="mainBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" Background="{TemplateBinding Background}" Margin="0">
<Border x:Name="innerBorder" BorderBrush="{DynamicResource TabItem.Selected.Border}" BorderThickness="1,1,1,0" Background="{DynamicResource TabItem.Selected.Background}" Margin="-1" Opacity="0"/>
</Border>
<ContentPresenter x:Name="contentPresenter" ContentSource="Header" Focusable="False" HorizontalAlignment="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
</Grid>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.MouseOver.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/>
</MultiDataTrigger.Conditions>
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/>
</MultiDataTrigger.Conditions>
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/>
</MultiDataTrigger.Conditions>
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.56"/>
<Setter Property="Background" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="mainBorder" Value="{DynamicResource TabItem.Disabled.Border}"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Left"/>
</MultiDataTrigger.Conditions>
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Margin" Value="-2,-2,0,-2"/>
<Setter Property="Opacity" TargetName="innerBorder" Value="1"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,0,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,0,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Bottom"/>
</MultiDataTrigger.Conditions>
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Margin" Value="-2,0,-2,-2"/>
<Setter Property="Opacity" TargetName="innerBorder" Value="1"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,0,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,0,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Right"/>
</MultiDataTrigger.Conditions>
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Margin" Value="0,-2,-2,-2"/>
<Setter Property="Opacity" TargetName="innerBorder" Value="1"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="0,1,1,1"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="0,1,1,1"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding TabStripPlacement, RelativeSource={RelativeSource AncestorType={x:Type TabControl}}}" Value="Top"/>
</MultiDataTrigger.Conditions>
<Setter Property="Panel.ZIndex" Value="1"/>
<Setter Property="Margin" Value="-2,-2,-2,0"/>
<Setter Property="Opacity" TargetName="innerBorder" Value="1"/>
<Setter Property="BorderThickness" TargetName="innerBorder" Value="1,1,1,0"/>
<Setter Property="BorderThickness" TargetName="mainBorder" Value="1,1,1,0"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Application.Resources>
</Application>

11
MPF/App.xaml.cs Normal file
View File

@@ -0,0 +1,11 @@
using System.Windows;
namespace MPF
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

4
MPF/AssemblyInfo.cs Normal file
View File

@@ -0,0 +1,4 @@
using System.Runtime.CompilerServices;
// Anything marked as internal can be used by the test methods
[assembly: InternalsVisibleTo("MPF.Test")]

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Utilities;
namespace MPF
{
/// <summary>
/// A generic combo box element
/// </summary>
/// <typeparam name="T">Enum type representing the possible values</typeparam>
public class Element<T> : IElement where T : struct, Enum
{
private readonly T Data;
public Element(T data) => Data = data;
/// <summary>
/// Allow elements to be used as their internal enum type
/// </summary>
/// <param name="item"></param>
public static implicit operator T? (Element<T> item) => item?.Data;
/// <inheritdoc/>
public string Name => Converters.GetLongName(Data);
public override string ToString() => Name;
/// <summary>
/// Internal enum value
/// </summary>
public T Value => Data;
/// <summary>
/// Determine if the item is selected or not
/// </summary>
/// <remarks>Only applies to CheckBox type</remarks>
public bool IsChecked { get; set; }
/// <summary>
/// Generate all elements associated with the data enum type
/// </summary>
/// <returns></returns>
public static IEnumerable<Element<T>> GenerateElements()
{
return Enum.GetValues(typeof(T))
.OfType<T>()
.Select(e => new Element<T>(e));
}
}
}

View File

@@ -0,0 +1,10 @@
namespace MPF
{
public interface IElement
{
/// <summary>
/// Display name for the combo box element
/// </summary>
string Name { get; }
}
}

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Data;
using MPF.Utilities;
namespace MPF
{
/// <summary>
/// Represents a single item in the System combo box
/// </summary>
public class KnownSystemComboBoxItem : IElement
{
private readonly object Data;
public KnownSystemComboBoxItem(KnownSystem? system) => Data = system;
public KnownSystemComboBoxItem(KnownSystemCategory? category) => Data = category;
public static implicit operator KnownSystem?(KnownSystemComboBoxItem item) => item.Data as KnownSystem?;
/// <inheritdoc/>
public string Name
{
get
{
if (IsHeader)
return "---------- " + Converters.GetLongName(Data as KnownSystemCategory?) + " ----------";
else
return Converters.GetLongName(Data as KnownSystem?);
}
}
public override string ToString() => Name;
/// <summary>
/// Internal enum value
/// </summary>
public KnownSystem? Value => Data as KnownSystem?;
/// <summary>
/// Determines if the item is a header value
/// </summary>
public bool IsHeader => Data is KnownSystemCategory?;
/// <summary>
/// Determines if the item is a standard system value
/// </summary>
public bool IsSystem => Data is KnownSystem?;
/// <summary>
/// Generate all elements for the known system combo box
/// </summary>
/// <returns></returns>
public static IEnumerable<KnownSystemComboBoxItem> GenerateElements()
{
var knownSystems = Enum.GetValues(typeof(KnownSystem))
.OfType<KnownSystem?>()
.Where(s => !s.IsMarker() && s != KnownSystem.NONE)
.ToList();
Dictionary<KnownSystemCategory, List<KnownSystem?>> mapping = knownSystems
.GroupBy(s => s.Category())
.ToDictionary(
k => k.Key,
v => v
.OrderBy(s => s.LongName())
.ToList()
);
var systemsValues = new List<KnownSystemComboBoxItem>
{
new KnownSystemComboBoxItem(KnownSystem.NONE),
};
foreach (var group in mapping)
{
systemsValues.Add(new KnownSystemComboBoxItem(group.Key));
group.Value.ForEach(system => systemsValues.Add(new KnownSystemComboBoxItem(system)));
}
return systemsValues;
}
}
}

25
MPF/Constants.cs Normal file
View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
namespace MPF
{
/// <summary>
/// Variables for UI elements
/// </summary>
public static class Constants
{
// Private lists of known drive speed ranges
private static IReadOnlyList<int> cd { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
private static IReadOnlyList<int> dvd { get; } = cd.Where(s => s <= 24).ToList();
private static IReadOnlyList<int> bd { get; } = cd.Where(s => s <= 16).ToList();
// Create collections for UI based on known drive speeds
public static DoubleCollection SpeedsForCDAsCollection { get; } = GetDoubleCollectionFromIntList(cd);
public static DoubleCollection SpeedsForDVDAsCollection { get; } = GetDoubleCollectionFromIntList(dvd);
public static DoubleCollection SpeedsForBDAsCollection { get; } = GetDoubleCollectionFromIntList(bd);
private static DoubleCollection GetDoubleCollectionFromIntList(IReadOnlyList<int> list)
=> new DoubleCollection(list.Select(i => Convert.ToDouble(i)).ToList());
}
}

View File

@@ -0,0 +1,596 @@
// -----------------------------------------------------------------------
// <copyright file="MessageBox.cs">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace WPFCustomMessageBox
{
using System;
using System.Windows;
/// <summary>
/// Displays a message box.
/// </summary>
public static class CustomMessageBox
{
/// <summary>
/// Global parameter to enable (true) or disable (false) the removal of the title bar icon.
/// If you are using a custom window style, the icon removal may cause issues like displaying two title bar (the default windows one and the custom one).
/// </summary>
public static bool RemoveTitleBarIcon = true;
/// <summary>
/// Displays a message box that has a message and returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, string.Empty, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message and a title bar caption; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, string caption, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box in front of the specified window. The message box displays a message and returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, string.Empty, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box in front of the specified window. The message box displays a message and title bar caption; and it returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and button; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(null, messageBoxText, caption, null, null, null, null, timeout, timeoutResult);
default:
return ShowOKMessage(null, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and button; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(owner, messageBoxText, caption, null, null, null, null, timeout, timeoutResult);
default:
return ShowOKMessage(owner, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(null, messageBoxText, caption, null, null, null, icon, timeout, timeoutResult);
default:
return ShowOKMessage(null, messageBoxText, caption, null, null, icon, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(owner, messageBoxText, caption, null, null, null, icon, timeout, timeoutResult);
default:
return ShowOKMessage(owner, messageBoxText, caption, null, null, icon, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and OK button with a custom System.String value for the button's text; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(string messageBoxText, string caption, string okButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and OK button with a custom System.String value for the button's text; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(Window owner, string messageBoxText, string caption, string okButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, OK button with a custom System.String value for the button's text, and icon; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(string messageBoxText, string caption, string okButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, OK button with a custom System.String value for the button's text, and icon; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(Window owner, string messageBoxText, string caption, string okButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and OK/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(string messageBoxText, string caption, string okButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and OK/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, OK/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, OK/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(string messageBoxText, string caption, string yesButtonText, string noButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(string messageBoxText, string caption, string yesButtonText, string noButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, OK/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
private static MessageBoxResult ShowOKMessage(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.OK : MessageBoxButton.OKCancel;
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
if (!string.IsNullOrEmpty(okButtonText))
msg.OkButtonText = okButtonText;
if (!string.IsNullOrEmpty(cancelButtonText))
msg.CancelButtonText = cancelButtonText;
ShowDialog(msg, timeout, timeoutResult);
return msg.Result;
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
private static MessageBoxResult ShowYesNoMessage(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.YesNo : MessageBoxButton.YesNoCancel;
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
if (!string.IsNullOrEmpty(yesButtonText))
msg.YesButtonText = yesButtonText;
if (!string.IsNullOrEmpty(noButtonText))
msg.NoButtonText = noButtonText;
if (!string.IsNullOrEmpty(cancelButtonText))
msg.CancelButtonText = cancelButtonText;
ShowDialog(msg, timeout, timeoutResult);
return msg.Result;
}
private static void ShowDialog(CustomMessageBoxWindow dialog, int? timeout, MessageBoxResult timeoutResult)
{
if (timeout.HasValue && timeout.Value <= 0)
{
throw new ArgumentOutOfRangeException("timeout", string.Format("Timeout must be greater than 0."));
}
if (timeout.HasValue)
{
//System.Threading.Timer timer = null;
//timer = new System.Threading.Timer(s => { msg.Close(); timer.Dispose(); }, null, timeout.Value, System.Threading.Timeout.Infinite);
System.Timers.Timer timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
timer.Elapsed += delegate {
Application.Current.Dispatcher.Invoke(new Action(() =>
{
dialog.Result = timeoutResult;
dialog.Close();
//timer.Stop();
//timer.Dispose();
//timer = null;
}));
};
timer.Start();
dialog.ShowDialog();
timer.Stop();
timer.Dispose();
}
else
dialog.ShowDialog();
}
}
}

View File

@@ -0,0 +1,84 @@
<Window x:Class="WPFCustomMessageBox.CustomMessageBoxWindow"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
ShowInTaskbar="False"
ResizeMode="NoResize" SizeToContent="WidthAndHeight"
TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType" UseLayoutRounding="True"
Title="" MinHeight="155" MaxWidth="470" MinWidth="154"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown" Content="{Binding Path=Title,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
</Grid>
<!-- Make the window width fit the title by embedding the title invisibly -->
<TextBlock Text="{Binding Path=Title,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
Visibility="Hidden" Height="0" Margin="50 0 0 0" />
<Grid Grid.Row="1" Background="{DynamicResource CustomMessageBox.Static.Background}" MinHeight="69">
<DockPanel>
<Image Name="Image_MessageBox" Width="32" Height="32" HorizontalAlignment="Left" DockPanel.Dock="Left" Margin="30,0,0,0" Visibility="Collapsed"/>
<TextBlock Name="TextBlock_Message" TextWrapping="Wrap" MaxWidth="500" Width="Auto"
VerticalAlignment="Center" Margin="12,20,41,15" />
</DockPanel>
</Grid>
<Grid Grid.Row="2" Background="{DynamicResource CustomMessageBox.Static.Background}" MinHeight="49">
<DockPanel Margin="5,0">
<!-- Cancel Button -->
<Button Name="Button_Cancel" MinWidth="88" MaxWidth="160" Height="26" Margin="5,0" HorizontalAlignment="Right" Visibility="Collapsed" IsCancel="True"
DockPanel.Dock="Right" Click="Button_Cancel_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_Cancel" Padding="0" Margin="10,0">_Cancel</Label>
</Button>
<!-- End Cancel Button -->
<!-- No Button -->
<Button Name="Button_No" MinWidth="88" MaxWidth="160" Height="26" Margin="5,0" HorizontalAlignment="Right" Visibility="Collapsed"
DockPanel.Dock="Right" Click="Button_No_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_No" Padding="0" Margin="10,0">_No</Label>
</Button>
<!-- End No Button -->
<!-- Yes Button -->
<Button Name="Button_Yes" MinWidth="88" MaxWidth="160" Height="26" Margin="35,0,5,0" HorizontalAlignment="Right" Visibility="Collapsed"
DockPanel.Dock="Right" Click="Button_Yes_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_Yes" Padding="0" Margin="10,0">_Yes</Label>
</Button>
<!-- End Yes Button -->
<!-- OK Button -->
<Button Name="Button_OK" MinWidth="88" MaxWidth="160" Margin="35,0,5,0" HorizontalAlignment="Right" Height="26"
Click="Button_OK_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_Ok" Padding="0" Margin="10,0">_OK</Label>
</Button>
<!-- End OK Button -->
</DockPanel>
</Grid>
</Grid>
</Window>

View File

@@ -0,0 +1,223 @@
using System;
using System.Drawing;
using System.Windows;
using System.Windows.Input;
namespace WPFCustomMessageBox
{
/// <summary>
/// Interaction logic for ModalDialog.xaml
/// </summary>
internal partial class CustomMessageBoxWindow : Window
{
private bool _removeTitleBarIcon = true;
public string Caption
{
get
{
return Title;
}
set
{
Title = value;
}
}
public string Message
{
get
{
return TextBlock_Message.Text;
}
set
{
TextBlock_Message.Text = value;
}
}
public string OkButtonText
{
get
{
return Label_Ok.Content.ToString();
}
set
{
Label_Ok.Content = value.TryAddKeyboardAccellerator();
}
}
public string CancelButtonText
{
get
{
return Label_Cancel.Content.ToString();
}
set
{
Label_Cancel.Content = value.TryAddKeyboardAccellerator();
}
}
public string YesButtonText
{
get
{
return Label_Yes.Content.ToString();
}
set
{
Label_Yes.Content = value.TryAddKeyboardAccellerator();
}
}
public string NoButtonText
{
get
{
return Label_No.Content.ToString();
}
set
{
Label_No.Content = value.TryAddKeyboardAccellerator();
}
}
public MessageBoxResult Result { get; set; }
internal CustomMessageBoxWindow(Window owner, string message, string caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
{
InitializeComponent();
_removeTitleBarIcon = removeTitleBarIcon;
if (owner != null)
{
Owner = owner;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
Message = message;
Caption = caption;
DisplayButtons(button ?? MessageBoxButton.OK);
if (image.HasValue)
DisplayImage(image.Value);
else
Image_MessageBox.Visibility = System.Windows.Visibility.Collapsed;
}
protected override void OnSourceInitialized(EventArgs e)
{
if (_removeTitleBarIcon)
Util.RemoveIcon(this);
base.OnSourceInitialized(e);
}
private void DisplayButtons(MessageBoxButton button)
{
switch (button)
{
case MessageBoxButton.OKCancel:
// Hide all but OK, Cancel
Button_OK.Visibility = System.Windows.Visibility.Visible;
Button_OK.Focus();
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
Button_No.Visibility = System.Windows.Visibility.Collapsed;
break;
case MessageBoxButton.YesNo:
// Hide all but Yes, No
Button_Yes.Visibility = System.Windows.Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = System.Windows.Visibility.Visible;
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
break;
case MessageBoxButton.YesNoCancel:
// Hide only OK
Button_Yes.Visibility = System.Windows.Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = System.Windows.Visibility.Visible;
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
break;
default:
// Hide all but OK
Button_OK.Visibility = System.Windows.Visibility.Visible;
Button_OK.Focus();
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
Button_No.Visibility = System.Windows.Visibility.Collapsed;
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
break;
}
}
private void DisplayImage(MessageBoxImage image)
{
Icon icon;
switch (image)
{
case MessageBoxImage.Exclamation: // Enumeration value 48 - also covers "Warning"
icon = SystemIcons.Exclamation;
break;
case MessageBoxImage.Error: // Enumeration value 16, also covers "Hand" and "Stop"
icon = SystemIcons.Hand;
break;
case MessageBoxImage.Information: // Enumeration value 64 - also covers "Asterisk"
icon = SystemIcons.Information;
break;
case MessageBoxImage.Question:
icon = SystemIcons.Question;
break;
default:
icon = SystemIcons.Information;
break;
}
Image_MessageBox.Source = icon.ToImageSource();
Image_MessageBox.Visibility = System.Windows.Visibility.Visible;
}
private void Button_OK_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.OK;
Close();
}
private void Button_Cancel_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.Cancel;
Close();
}
private void Button_Yes_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.Yes;
Close();
}
private void Button_No_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.No;
Close();
}
/// <summary>
/// Handler for Title MouseDown event
/// </summary>
private void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
}
}

View File

@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2021 Thomas Absenger
Copyright (c) 2013 Evan Wondrasek / Apricity Software LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,92 @@
// -----------------------------------------------------------------------
// <copyright file="Util.cs">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace WPFCustomMessageBox
{
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
/// <summary>
/// TODO: Update summary.
/// </summary>
internal static class Util
{
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
const int GWL_EXSTYLE = -20;
const int WS_EX_DLGMODALFRAME = 0x0001;
const int SWP_NOSIZE = 0x0001;
const int SWP_NOMOVE = 0x0002;
const int SWP_NOZORDER = 0x0004;
const int SWP_FRAMECHANGED = 0x0020;
const uint WM_SETICON = 0x0080;
internal static ImageSource ToImageSource(this Icon icon)
{
ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return imageSource;
}
/// <summary>
/// Keyboard Accellerators are used in Windows to allow easy shortcuts to controls like Buttons and
/// MenuItems. These allow users to press the Alt key, and a shortcut key will be highlighted on the
/// control. If the user presses that key, that control will be activated.
/// This method checks a string if it contains a keyboard accellerator. If it doesn't, it adds one to the
/// beginning of the string. If there are two strings with the same accellerator, Windows handles it.
/// The keyboard accellerator character for WPF is underscore (_). It will not be visible.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
internal static string TryAddKeyboardAccellerator(this string input)
{
if (input == null)
return input;
const string accellerator = "_"; // This is the default WPF accellerator symbol - used to be & in WinForms
// If it already contains an accellerator, do nothing
if (input.Contains(accellerator)) return input;
return accellerator + input;
}
/// <summary>
/// Removes the icon from the given window.
/// See https://stackoverflow.com/a/18581096
/// </summary>
/// <param name="window">The window to remove the icon from.</param>
internal static void RemoveIcon(Window window)
{
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(window).Handle;
// Change the extended window style to not show a window icon
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
// Update the window's non-client area to reflect the changes
SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
}
}

View File

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 KiB

60
MPF/MPF.csproj Normal file
View File

@@ -0,0 +1,60 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<ApplicationIcon>Images\Icon.ico</ApplicationIcon>
<Title>MPF</Title>
<AssemblyName>MPF</AssemblyName>
<Description>Frontend for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
<PropertyGroup>
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.7.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0-preview.6.21352.12" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Icon.ico" />
<Resource Include="Images\ring-code-guide.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj">
<Project>{51ab0928-13f9-44bf-a407-b6957a43a056}</Project>
<Name>MPF.Library</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationFramework.Aero" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,32 @@
<UserControl x:Class="MPF.UserControls.LogOutput"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel Orientation="Vertical">
<Grid Height="22" Margin="10 10 10 0">
<ProgressBar x:Name="ProgressBar" Style="{DynamicResource CustomProgressBarStyle}" />
<TextBlock x:Name="ProgressLabel" Grid.Row="0" Height="22" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="0 2 0 0" />
</Grid>
<Border Height="180" Background="White" BorderBrush="Gainsboro" BorderThickness="1" Margin="10">
<ScrollViewer Name="OutputViewer" SizeChanged="OutputViewerSizeChanged" Template="{DynamicResource CustomScrollViewerControlStyle}">
<RichTextBox Name="Output" FontFamily="Consolas" Background="#FF202020" IsReadOnly="true" TextChanged="OnTextChanged" />
</ScrollViewer>
</Border>
<GroupBox Grid.Row="2" Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
<Label/> <!-- Empty label for padding -->
<Button Name="ClearButton" Height="25" Width="80" Content="Clear" Click="OnClearButton" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="SaveButton" Height="25" Width="80" Content="Save" Click="OnSaveButton" Style="{DynamicResource CustomButtonStyle}" />
<Label/> <!-- Empty label for padding -->
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,546 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using MPF.Data;
namespace MPF.UserControls
{
public partial class LogOutput : UserControl
{
/// <summary>
/// Paragraph backing the log
/// </summary>
private readonly Paragraph _paragraph;
/// <summary>
/// Cached value of the last line written
/// </summary>
private Run lastLine = null;
/// <summary>
/// Queue of items that need to be logged
/// </summary>
private readonly ProcessingQueue<LogLine> logQueue;
/// <summary>
/// List of Matchers for progress tracking
/// </summary>
private readonly List<Matcher?> _matchers;
/// <summary>
/// Cached value of the last matcher used
/// </summary>
private Matcher? lastUsedMatcher = null;
/// <summary>
/// Regex pattern to find DiscImageCreator progress messages
/// </summary>
private const string DiscImageCreatorProgressPattern = @"\s*(\d+)\/\s*(\d+)$";
public LogOutput()
{
InitializeComponent();
DataContext = this;
var document = new FlowDocument()
{
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
};
_paragraph = new Paragraph();
document.Blocks.Add(_paragraph);
Output.Document = document;
// TODO: Can we dynamically add matchers *only* during dumping?
_matchers = new List<Matcher?>();
AddAaruMatchers();
AddDiscImageCreatorMatchers();
logQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
}
#region Matching
/// <summary>
/// Matching wrapper
/// </summary>
private struct Matcher
{
private readonly string prefix;
private readonly Regex regex;
private readonly int start;
private readonly string progressBarText;
private readonly Action<Match, string> lambda;
public Matcher(string prefix, string regex, string progressBarText, Action<Match, string> lambda)
{
this.prefix = prefix;
this.regex = new Regex(regex);
this.start = prefix.Length;
this.progressBarText = progressBarText;
this.lambda = lambda;
}
/// <summary>
/// Check if the text matches the prefix
/// </summary>
/// <param name="text">Text to check</param>
/// <returns>True if the line starts with the prefix, false otherwise</returns>
public bool Matches(string text) => text.StartsWith(prefix);
/// <summary>
/// Generate a Match and apply the lambda
/// </summary>
/// <param name="text">Text to match and apply from</param>
public void Apply(string text)
{
Match match = regex?.Match(text, start);
lambda?.Invoke(match, progressBarText);
}
}
/// <summary>
/// Add all Matchers for Aaru
/// </summary>
private void AddAaruMatchers()
{
// TODO: Determine matchers that can be added
}
/// <summary>
/// Add all Matchers for DiscImageCreator
/// </summary>
private void AddDiscImageCreatorMatchers()
{
#region Pre-dump Checking
_matchers.Add(new Matcher(
"Checking EXE",
DiscImageCreatorProgressPattern,
"Checking executables...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Checking Pregap sync, msf, mode (LBA)",
@"\s*-(\d+)$",
"Checking Pregap sync, msf, mode",
(match, text) =>
{
ProgressBar.Value = 0;
ProgressLabel.Text = text;
}));
_matchers.Add(new Matcher(
"Checking SubQ adr (Track)",
DiscImageCreatorProgressPattern,
"Checking SubQ adr...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Checking SubQ ctl (Track)",
DiscImageCreatorProgressPattern,
"Checking SubQ ctl...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Checking SubRtoW (Track)",
DiscImageCreatorProgressPattern,
"Checking SubRtoW...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Reading DirectoryRecord",
DiscImageCreatorProgressPattern,
"Reading directory records...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Scanning sector for anti-mod string (LBA)",
DiscImageCreatorProgressPattern,
"Scanning sectors for anti-mod string...",
StandardDiscImageCreatorProgress
));
#endregion
#region Dumping
_matchers.Add(new Matcher(
@"Creating iso(LBA)",
DiscImageCreatorProgressPattern,
"Creating ISO...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
@"Creating .scm (LBA)",
DiscImageCreatorProgressPattern,
"Creating scrambled image...",
StandardDiscImageCreatorProgress
));
#endregion
#region Post-Dump Processing
_matchers.Add(new Matcher(
"Checking sectors:",
DiscImageCreatorProgressPattern,
"Checking for errors...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Creating bin (Track)",
DiscImageCreatorProgressPattern,
"Creating BIN(s)...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Creating cue and ccd (Track)",
DiscImageCreatorProgressPattern,
"Creating CUE and CCD...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Descrambling data sector of img:",
DiscImageCreatorProgressPattern,
"Descrambling image...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Scanning sector (LBA)",
DiscImageCreatorProgressPattern,
"Scanning sectors for protection...",
StandardDiscImageCreatorProgress
));
#endregion
}
#endregion
#region Logging
/// <summary>
/// Log level for output
/// </summary>
private enum LogLevel
{
USER,
VERBOSE,
ERROR,
SECRET,
}
/// <summary>
/// Log line wrapper
/// </summary>
private struct LogLine
{
public readonly string Text;
public readonly LogLevel LogLevel;
public LogLine(string text, LogLevel logLevel)
{
this.Text = text;
this.LogLevel = logLevel;
}
/// <summary>
/// Get the foreground Brush for the current LogLevel
/// </summary>
/// <returns>Brush representing the color</returns>
public Brush GetForegroundColor()
{
switch (this.LogLevel)
{
case LogLevel.SECRET:
return Brushes.Blue;
case LogLevel.ERROR:
return Brushes.Red;
case LogLevel.VERBOSE:
return Brushes.Yellow;
case LogLevel.USER:
default:
return Brushes.White;
}
}
/// <summary>
/// Generate a Run object from the current LogLine
/// </summary>
/// <returns>Run object based on internal values</returns>
public Run GenerateRun()
{
return new Run { Text = this.Text, Foreground = GetForegroundColor() };
}
}
/// <summary>
/// Enqueue text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void Log(string text) => LogInternal(text);
/// <summary>
/// Enqueue text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void LogLn(string text) => Log(text + "\n");
/// <summary>
/// Enqueue error text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void ErrorLog(string text) => LogInternal(text, LogLevel.ERROR);
/// <summary>
/// Enqueue error text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void ErrorLogLn(string text) => ErrorLog(text + "\n");
/// <summary>
/// Enqueue secret text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void SecretLog(string text) => LogInternal(text, LogLevel.SECRET);
/// <summary>
/// Enqueue secret text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void SecretLogLn(string text) => SecretLog(text + "\n");
/// <summary>
/// Enqueue verbose text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void VerboseLog(string text) => LogInternal(text, LogLevel.VERBOSE);
/// <summary>
/// Enqueue verbose text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void VerboseLogLn(string text) => VerboseLog(text + "\n");
/// <summary>
/// Reset the progress bar state
/// </summary>
public void ResetProgressBar()
{
Dispatcher.Invoke(() =>
{
ProgressBar.Value = 0;
ProgressLabel.Text = string.Empty;
});
}
/// <summary>
/// Enqueue text to the log with formatting
/// </summary>
/// <param name="text">Text to write to the log</param>
/// <param name="logLevel">LogLevel for the log, defaults to USER</param>
private void LogInternal(string text, LogLevel logLevel = LogLevel.USER)
{
// Null text gets ignored
if (text == null)
return;
// If we have verbose logs but not enabled, ignore
if (logLevel == LogLevel.VERBOSE && !ViewModels.OptionsViewModel.VerboseLogging)
return;
// Enqueue the text
logQueue.Enqueue(new LogLine(text, logLevel));
}
/// <summary>
/// Process the log lines in the queue
/// </summary>
/// <param name="nextLogLine">LogLine item to process</param>
private void ProcessLogLine(LogLine nextLogLine)
{
// Null text gets ignored
string nextText = Dispatcher.Invoke(() => nextLogLine.Text);
if (nextText == null)
return;
try
{
// If we're not processing log formatting, just append and continue
if (!ViewModels.OptionsViewModel.EnableLogFormatting)
{
if (nextText.StartsWith("\r"))
ReplaceLastLine(nextLogLine);
else
AppendToTextBox(nextLogLine);
return;
}
// Get last line
lastLine = lastLine ?? GetLastLine();
// Always append if there's no previous line
if (lastLine == null)
{
AppendToTextBox(nextLogLine);
lastUsedMatcher = _matchers.FirstOrDefault(m => m?.Matches(nextText) == true);
}
// Return always means overwrite
else if (nextText.StartsWith("\r"))
{
ReplaceLastLine(nextLogLine);
lastUsedMatcher = _matchers.FirstOrDefault(m => m?.Matches(nextText.TrimStart('\r')) == true);
}
// If we have a cached matcher and we match
else if (lastUsedMatcher?.Matches(nextText) == true)
{
ReplaceLastLine(nextLogLine);
}
else
{
// Get the first matching Matcher
var firstMatcher = _matchers.FirstOrDefault(m => m?.Matches(nextText) == true);
if (firstMatcher.HasValue)
{
string lastText = Dispatcher.Invoke(() => { return lastLine.Text; });
if (firstMatcher.Value.Matches(lastText))
ReplaceLastLine(nextLogLine);
else if (string.IsNullOrWhiteSpace(lastText))
ReplaceLastLine(nextLogLine);
else
AppendToTextBox(nextLogLine);
// Cache the last used Matcher
lastUsedMatcher = firstMatcher;
}
// Default case for all other text
else
{
AppendToTextBox(nextLogLine);
lastUsedMatcher = null;
}
}
// Update the bar if needed
if (ViewModels.OptionsViewModel.EnableProgressProcessing)
ProcessStringForProgressBar(nextText, lastUsedMatcher);
}
catch (Exception ex)
{
// In the event that something fails horribly, we want to log
AppendToTextBox(new LogLine(ex.ToString(), LogLevel.ERROR));
}
}
/// <summary>
/// Append log line to the log text box
/// </summary>
/// <param name="logLine">LogLine value to append</param>
private void AppendToTextBox(LogLine logLine)
{
Dispatcher.Invoke(() =>
{
var run = logLine.GenerateRun();
_paragraph.Inlines.Add(run);
lastLine = run;
});
}
/// <summary>
/// Get the last line written to the log text box
/// </summary>
private Run GetLastLine()
{
return Dispatcher.Invoke(() =>
{
if (!_paragraph.Inlines.Any())
return null;
return _paragraph.Inlines.LastInline as Run;
});
}
/// <summary>
/// Process text if it should update the progress bar
/// </summary>
/// <param name="text">Text to check and update with</param>
private void ProcessStringForProgressBar(string text, Matcher? matcher)
{
Dispatcher.Invoke(() => { matcher?.Apply(text); });
}
/// <summary>
/// Replace the last line written to the log text box
/// </summary>
/// <param name="logLine">LogLine value to append</param>
private void ReplaceLastLine(LogLine logLine)
{
Dispatcher.Invoke(() =>
{
lastLine.Text = logLine.Text;
lastLine.Foreground = logLine.GetForegroundColor();
});
}
#endregion
#region EventHandlers
private void OnClearButton(object sender, EventArgs e)
{
_paragraph.Inlines.Clear();
ResetProgressBar();
}
private void OnSaveButton(object sender, EventArgs e)
{
using (StreamWriter tw = new StreamWriter(File.OpenWrite("console.log")))
{
foreach (var inline in _paragraph.Inlines)
{
if (inline is Run run)
tw.Write(run.Text);
}
}
}
private void OnTextChanged(object sender, TextChangedEventArgs e)
{
OutputViewer.ScrollToBottom();
}
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e)
{
OutputViewer.ScrollToBottom();
}
private void StandardDiscImageCreatorProgress(Match match, string text)
{
if (uint.TryParse(match.Groups[1].Value, out uint current) && uint.TryParse(match.Groups[2].Value, out uint total))
{
float percentProgress = (current / (float)total) * 100;
ProgressBar.Value = percentProgress;
ProgressLabel.Text = string.Format($"{text} ({percentProgress:N2}%)");
}
}
#endregion
}
}

View File

@@ -0,0 +1,20 @@
<UserControl x:Class="MPF.UserControls.UserInput"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="{Binding Label}" />
<TextBox Grid.Row="0" Grid.Column="1" Height="{Binding TextHeight}" HorizontalAlignment="Stretch" Text="{Binding Text}"
AcceptsTab="{Binding Tab}" AcceptsReturn="{Binding Enter}" TextWrapping="{Binding TextWrapping}"
VerticalContentAlignment="{Binding VerticalContentAlignmentValue}" VerticalScrollBarVisibility="{Binding ScrollBarVisibility}"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,26 @@
using System.Windows;
using System.Windows.Controls;
namespace MPF.UserControls
{
/// <summary>
/// Interaction logic for UserInput.xaml
/// </summary>
public partial class UserInput : UserControl
{
public string Label { get; set; }
public string Text { get; set; }
public string TextHeight { get; set; } = "22";
public bool Tab { get; set; } = false;
public bool Enter { get; set; } = false;
public TextWrapping TextWrapping { get; set; } = TextWrapping.NoWrap;
public VerticalAlignment VerticalContentAlignmentValue { get; set; } = VerticalAlignment.Center;
public ScrollBarVisibility ScrollBarVisibility { get; set; } = ScrollBarVisibility.Auto;
public UserInput()
{
InitializeComponent();
DataContext = this;
}
}
}

38
MPF/ViewModels.cs Normal file
View File

@@ -0,0 +1,38 @@
using MPF.Data;
namespace MPF
{
/// <summary>
/// Globally referencable options
/// </summary>
public class OptionsViewModel
{
private readonly Options options;
public bool VerboseLogging
{
get { return options.VerboseLogging; }
}
public bool EnableLogFormatting
{
get { return options.EnableLogFormatting; }
}
public bool EnableProgressProcessing
{
get { return options.EnableProgressProcessing; }
}
public OptionsViewModel(Options options)
{
this.options = options;
}
}
public static class ViewModels
{
public static OptionsViewModel OptionsViewModel { get; set; }
}
}

View File

@@ -0,0 +1,199 @@
<Window x:Class="MPF.Windows.DiscInformationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MPF"
xmlns:controls="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
Title="Disc Information" Width="515" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical" Width="500">
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock TextAlignment="Center"><Bold>Disc Information</Bold></TextBlock>
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<TabControl Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomTabControlStyle}">
<TabItem x:Name="CommonInfo" Header="Common Info" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel Orientation="Vertical">
<Expander Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Common Disc Information" IsExpanded="True">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="GameTitle" Label="Title"/>
<controls:UserInput x:Name="ForeignTitle" Label="Foreign Title (Non-Latin)"/>
<controls:UserInput x:Name="DiscNumberLetter" Label="Disc Number / Letter"/>
<controls:UserInput x:Name="DiscTitle" Label="Disc Title"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label x:Name="CategoryLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Category" />
<ComboBox x:Name="CategoryComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" ItemsSource="{Binding Categories}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label x:Name="RegionLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Region" />
<ComboBox x:Name="RegionComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" ItemsSource="{Binding Regions}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label x:Name="LanguagesLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Languages" />
<ComboBox x:Name="LanguagesComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch" ItemsSource="{Binding Languages}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
<Grid x:Name="LanguageSelectionGrid" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label x:Name="LanguageSelectionLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Language Selection Via" />
<ComboBox x:Name="LanguageSelectionComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch" ItemsSource="{Binding LanguageSelections}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
<controls:UserInput x:Name="Serial" Label="Serial"/>
<controls:UserInput x:Name="Barcode" Label="Barcode"/>
<controls:UserInput x:Name="Comments" Label="Comments" TextHeight="50" Tab="True" Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top" />
<controls:UserInput x:Name="Contents" Label="Contents" TextHeight="50" Tab="True" Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
</StackPanel>
</Expander>
<Expander Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Version and Editions" IsExpanded="True">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="Version" Label="Version"/>
<controls:UserInput x:Name="Edition" Label="Edition"/>
</StackPanel>
</Expander>
</StackPanel>
</TabItem>
<TabItem x:Name="L0Info" Header="Data/L0 Info" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L0MasteringRing" Label="Data/L0 Mastering Ring" Tab="True"/>
<controls:UserInput x:Name="L0MasteringSID" Label="Data/L0 Mastering SID"/>
<controls:UserInput x:Name="L0Toolstamp" Label="Data/L0 Toolstamp/Mastering Code"/>
<controls:UserInput x:Name="L0MouldSID" Label="Data/L0 Mould SID"/>
<controls:UserInput x:Name="L0AdditionalMould" Label="Data/L0 Additional Mould"/>
</StackPanel>
</TabItem>
<TabItem x:Name="L1Info" Header="Label/L1 Info" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L1MasteringRing" Label="Label/L1 Mastering Ring" Tab="True"/>
<controls:UserInput x:Name="L1MasteringSID" Label="Label/L1 Mastering SID"/>
<controls:UserInput x:Name="L1Toolstamp" Label="Label/L1 Toolstamp/Mastering Code"/>
<controls:UserInput x:Name="L1MouldSID" Label="Label/L1 Mould SID"/>
<controls:UserInput x:Name="L1AdditionalMould" Label="Label/L1 Additional Mould"/>
</StackPanel>
</TabItem>
<TabItem x:Name="L2Info" Header="L2 Info" Visibility="Collapsed" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L2MasteringRing" Label="L2 Mastering Ring" Tab="True"/>
<controls:UserInput x:Name="L2MasteringSID" Label="L2 Mastering SID"/>
<controls:UserInput x:Name="L2Toolstamp" Label="L2 Toolstamp/Mastering Code"/>
</StackPanel>
</TabItem>
<TabItem x:Name="L3Info" Header="L3 Info" Visibility="Collapsed" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L3MasteringRing" Label="L3 Mastering Ring" Tab="True"/>
<controls:UserInput x:Name="L3MasteringSID" Label="L3 Mastering SID"/>
<controls:UserInput x:Name="L3Toolstamp" Label="L3 Toolstamp/Mastering Code"/>
</StackPanel>
</TabItem>
</TabControl>
<!-- Accept / Cancel -->
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="3" Margin="5,5,5,5" Height="28">
<Button Name="AcceptButton" Grid.Column="0" Height="25" Width="120" IsDefault="True" Content="Accept"
Click="OnAcceptClick" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Grid.Column="1" Height="25" Width="120" IsCancel="True" Content="Cancel"
Click="OnCancelClick" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="RingCodeGuideButton" Grid.Column="2" Height="25" Width="120" Content="Ring Code Guide"
Click="OnRingCodeGuideClick" Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,355 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using MPF.Data;
namespace MPF.Windows
{
/// <summary>
/// Interaction logic for DiscInformationWindow.xaml
/// </summary>
public partial class DiscInformationWindow : Window
{
#region Fields
/// <summary>
/// List of available disc categories
/// </summary>
public List<Element<RedumpDiscCategory>> Categories { get; private set; } = Element<RedumpDiscCategory>.GenerateElements().ToList();
/// <summary>
/// SubmissionInfo object to fill and save
/// </summary>
public SubmissionInfo SubmissionInfo { get; set; }
/// <summary>
/// List of available regions
/// </summary>
public List<Element<RedumpRegion>> Regions { get; private set; } = Element<RedumpRegion>.GenerateElements().ToList();
/// <summary>
/// List of available languages
/// </summary>
public List<Element<RedumpLanguage>> Languages { get; private set; } = Element<RedumpLanguage>.GenerateElements().ToList();
/// <summary>
/// List of available languages
/// </summary>
public List<Element<RedumpLanguageSelection>> LanguageSelections { get; private set; } = Element<RedumpLanguageSelection>.GenerateElements().ToList();
#endregion
public DiscInformationWindow(SubmissionInfo submissionInfo)
{
InitializeComponent();
DataContext = this;
this.SubmissionInfo = submissionInfo;
ManipulateFields();
Load();
}
/// <summary>
/// Manipulate fields based on the current disc
/// </summary>
private void ManipulateFields()
{
// Sony-printed discs have layers in the opposite order
var system = SubmissionInfo?.CommonDiscInfo?.System;
bool reverseOrder = (system == KnownSystem.SonyPlayStation2
|| system == KnownSystem.SonyPlayStation3
|| system == KnownSystem.SonyPlayStation4
|| system == KnownSystem.SonyPlayStation5);
// Different media types mean different fields available
switch (SubmissionInfo?.CommonDiscInfo?.Media)
{
case MediaType.CDROM:
case MediaType.GDROM:
L0Info.Header = "Data Side";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Mould SID";
L0AdditionalMould.Label = "Additional Mould";
L1Info.Header = "Label Side";
L1MasteringRing.Visibility = Visibility.Collapsed;
L1MasteringSID.Visibility = Visibility.Collapsed;
L1Toolstamp.Visibility = Visibility.Collapsed;
L1MouldSID.Label = "Mould SID";
L1AdditionalMould.Label = "Additional Mould";
break;
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
case MediaType.NintendoWiiUOpticalDisc:
// Quad-layer discs
if (SubmissionInfo?.SizeAndChecksums?.Layerbreak3 != default(long))
{
L2Info.Visibility = Visibility.Visible;
L3Info.Visibility = Visibility.Visible;
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
L1Info.Header = "Layer 1";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
L2Info.Header = "Layer 2";
L2MasteringRing.Label = "Mastering Ring";
L2MasteringSID.Label = "Mastering SID";
L2Toolstamp.Label = "Toolstamp/Mastering Code";
L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
L3MasteringRing.Label = "Mastering Ring";
L3MasteringSID.Label = "Mastering SID";
L3Toolstamp.Label = "Toolstamp/Mastering Code";
}
// Triple-layer discs
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak2 != default(long))
{
L2Info.Visibility = Visibility.Visible;
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
L1Info.Header = "Layer 1";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
L2MasteringRing.Label = "Mastering Ring";
L2MasteringSID.Label = "Mastering SID";
L2Toolstamp.Label = "Toolstamp/Mastering Code";
}
// Double-layer discs
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
{
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
}
// Single-layer discs
else
{
L0Info.Header = "Data Side";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Mould SID";
L0AdditionalMould.Label = "Additional Mould";
L1Info.Header = "Label Side";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Mould SID";
L1AdditionalMould.Label = "Additional Mould";
}
break;
// All other media we assume to have no rings
default:
L0Info.Visibility = Visibility.Collapsed;
L1Info.Visibility = Visibility.Collapsed;
L2Info.Visibility = Visibility.Collapsed;
L3Info.Visibility = Visibility.Collapsed;
break;
}
// Different systems mean different fields available
switch (system)
{
case KnownSystem.SonyPlayStation2:
LanguageSelectionGrid.Visibility = Visibility.Visible;
break;
}
}
/// <summary>
/// Load the current contents of the base SubmissionInfo to the UI
/// </summary>
private void Load()
{
GameTitle.Text = SubmissionInfo.CommonDiscInfo.Title ?? "";
ForeignTitle.Text = SubmissionInfo.CommonDiscInfo.ForeignTitleNonLatin ?? "";
DiscNumberLetter.Text = SubmissionInfo.CommonDiscInfo.DiscNumberLetter ?? "";
DiscTitle.Text = SubmissionInfo.CommonDiscInfo.DiscTitle ?? "";
CategoryComboBox.SelectedIndex = Categories.FindIndex(r => r == SubmissionInfo.CommonDiscInfo.Category);
RegionComboBox.SelectedIndex = Regions.FindIndex(r => r == SubmissionInfo.CommonDiscInfo.Region);
if (SubmissionInfo.CommonDiscInfo.Languages != null)
Languages.ForEach(l => l.IsChecked = SubmissionInfo.CommonDiscInfo.Languages.Contains(l));
if (SubmissionInfo.CommonDiscInfo.LanguageSelection != null)
LanguageSelections.ForEach(ls => ls.IsChecked = SubmissionInfo.CommonDiscInfo.LanguageSelection.Contains(ls));
Serial.Text = SubmissionInfo.CommonDiscInfo.Serial ?? "";
L0MasteringRing.Text = SubmissionInfo.CommonDiscInfo.Layer0MasteringRing ?? "";
L0MasteringSID.Text = SubmissionInfo.CommonDiscInfo.Layer0MasteringSID ?? "";
L0Toolstamp.Text = SubmissionInfo.CommonDiscInfo.Layer0ToolstampMasteringCode ?? "";
L0MouldSID.Text = SubmissionInfo.CommonDiscInfo.Layer0MouldSID ?? "";
L0AdditionalMould.Text = SubmissionInfo.CommonDiscInfo.Layer0AdditionalMould ?? "";
L1MasteringRing.Text = SubmissionInfo.CommonDiscInfo.Layer1MasteringRing ?? "";
L1MasteringSID.Text = SubmissionInfo.CommonDiscInfo.Layer1MasteringSID ?? "";
L1Toolstamp.Text = SubmissionInfo.CommonDiscInfo.Layer1ToolstampMasteringCode ?? "";
L1MouldSID.Text = SubmissionInfo.CommonDiscInfo.Layer1MouldSID ?? "";
L1AdditionalMould.Text = SubmissionInfo.CommonDiscInfo.Layer1AdditionalMould ?? "";
L2MasteringRing.Text = SubmissionInfo.CommonDiscInfo.Layer2MasteringRing ?? "";
L2MasteringSID.Text = SubmissionInfo.CommonDiscInfo.Layer2MasteringSID ?? "";
L2Toolstamp.Text = SubmissionInfo.CommonDiscInfo.Layer2ToolstampMasteringCode ?? ""; ;
L3MasteringRing.Text = SubmissionInfo.CommonDiscInfo.Layer3MasteringRing ?? "";
L3MasteringSID.Text = SubmissionInfo.CommonDiscInfo.Layer3MasteringSID ?? "";
L3Toolstamp.Text = SubmissionInfo.CommonDiscInfo.Layer3ToolstampMasteringCode ?? "";
Barcode.Text = SubmissionInfo.CommonDiscInfo.Barcode ?? "";
Comments.Text = SubmissionInfo.CommonDiscInfo.Comments ?? "";
Contents.Text = SubmissionInfo.CommonDiscInfo.Contents ?? "";
Version.Text = SubmissionInfo.VersionAndEditions.Version ?? "";
Edition.Text = SubmissionInfo.VersionAndEditions.OtherEditions ?? "";
}
/// <summary>
/// Save the current contents of the UI to the base SubmissionInfo
/// </summary>
private void Save()
{
SubmissionInfo.CommonDiscInfo.Title = GameTitle.Text ?? "";
SubmissionInfo.CommonDiscInfo.ForeignTitleNonLatin = ForeignTitle.Text ?? "";
SubmissionInfo.CommonDiscInfo.DiscNumberLetter = DiscNumberLetter.Text ?? "";
SubmissionInfo.CommonDiscInfo.DiscTitle = DiscTitle.Text ?? "";
SubmissionInfo.CommonDiscInfo.Category = (CategoryComboBox.SelectedItem as Element<RedumpDiscCategory>)?.Value ?? RedumpDiscCategory.Games;
SubmissionInfo.CommonDiscInfo.Region = (RegionComboBox.SelectedItem as Element<RedumpRegion>)?.Value ?? RedumpRegion.World;
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
SubmissionInfo.CommonDiscInfo.Languages = new RedumpLanguage?[] { null };
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
SubmissionInfo.CommonDiscInfo.Serial = Serial.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer0MasteringRing = L0MasteringRing.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer0MasteringSID = L0MasteringSID.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer0ToolstampMasteringCode = L0Toolstamp.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer0MouldSID = L0MouldSID.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer0AdditionalMould = L0AdditionalMould.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer1MasteringRing = L1MasteringRing.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer1MasteringSID = L1MasteringSID.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer1ToolstampMasteringCode = L1Toolstamp.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer1MouldSID = L1MouldSID.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer1AdditionalMould = L1AdditionalMould.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer2MasteringRing = L2MasteringRing.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer2MasteringSID = L2MasteringSID.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer2ToolstampMasteringCode = L2Toolstamp.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer3MasteringRing = L3MasteringRing.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer3MasteringSID = L3MasteringSID.Text ?? "";
SubmissionInfo.CommonDiscInfo.Layer3ToolstampMasteringCode = L3Toolstamp.Text ?? "";
SubmissionInfo.CommonDiscInfo.Barcode = Barcode.Text ?? "";
SubmissionInfo.CommonDiscInfo.Comments = Comments.Text ?? "";
SubmissionInfo.CommonDiscInfo.Contents = Contents.Text ?? "";
SubmissionInfo.VersionAndEditions.Version = this.Version.Text ?? "";
SubmissionInfo.VersionAndEditions.OtherEditions = Edition.Text ?? "";
}
#region Event Handlers
/// <summary>
/// Handler for CloseButton Click event
/// </summary>
private void CloseButtonClick(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
Close();
}
/// <summary>
/// Handler for MinimizeButton Click event
/// </summary>
private void MinimizeButtonClick(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
/// <summary>
/// Handler for AcceptButton Click event
/// </summary>
private void OnAcceptClick(object sender, RoutedEventArgs e)
{
Save();
this.DialogResult = true;
Close();
}
/// <summary>
/// Handler for CancelButton Click event
/// </summary>
private void OnCancelClick(object sender, RoutedEventArgs e)
{
this.DialogResult = false;
Close();
}
/// <summary>
/// Handler for RingCodeGuideButton Click event
/// </summary>
private void OnRingCodeGuideClick(object sender, RoutedEventArgs e)
{
var ringCodeGuideWindow = new RingCodeGuideWindow()
{
Owner = this,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
ringCodeGuideWindow.Show();
}
/// <summary>
/// Handler for Title MouseDown event
/// </summary>
private void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
#endregion
}
}

178
MPF/Windows/MainWindow.xaml Normal file
View File

@@ -0,0 +1,178 @@
<Window x:Class="MPF.Windows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MPF"
xmlns:controls="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,2,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<StackPanel Panel.ZIndex="1" Grid.Column="1" VerticalAlignment="Center" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<Menu x:Name="TopMenuBar" Width="Auto" Height="20"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">
<MenuItem x:Name="FileMenuItem" Header="_File"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}">
<MenuItem x:Name="AppExitMenuItem" Header="E_xit" HorizontalAlignment="Left" Width="185" Click="AppExitClick"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
</MenuItem>
<MenuItem x:Name="ToolsMenuItem" Header="_Tools"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}">
<MenuItem x:Name="OptionsMenuItem" Header="_Options" HorizontalAlignment="Left" Width="185" Click="OptionsMenuItemClick"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
</MenuItem>
<MenuItem x:Name="HelpMenuItem" Header="_Help"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}">
<MenuItem x:Name="AboutMenuItem" Header="_About" HorizontalAlignment="Left" Width="185" Click="AboutClick"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
<MenuItem x:Name="CheckForUpdatesMenuItem" Header="_Check for Updates" HorizontalAlignment="Left" Width="185" Click="CheckForUpdatesClick"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
</MenuItem>
</Menu>
</StackPanel>
<Label Panel.ZIndex="0" Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock TextAlignment="Center"><Bold>Media Preservation Frontend</Bold></TextBlock>
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Settings">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="SystemMediaTypeLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left" ItemsSource="{Binding Systems}" SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHeader}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right" Style="{DynamicResource CustomComboBoxStyle}" />
<Label x:Name="OutputFilenameLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="Output Filename"/>
<TextBox x:Name="OutputFilenameTextBox" Grid.Row="1" Grid.Column="1" Height="22" VerticalContentAlignment="Center" />
<Label x:Name="OutputDirectoryLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Output Directory"/>
<TextBox x:Name="OutputDirectoryTextBox" Grid.Row="2" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center" />
<Button x:Name="OutputDirectoryBrowseButton" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
Click="OutputDirectoryBrowseButtonClick" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="DriveLetterLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="Drive Letter"/>
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Letter}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Label x:Name="DriveSpeedLabel" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Content="Drive Speed"/>
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
<Label x:Name="ParametersLabel" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" Content="Parameters"/>
<TextBox x:Name="ParametersTextBox" Grid.Row="5" Grid.Column="1" Height="22" Width="370" HorizontalAlignment="Left" IsEnabled="False" VerticalContentAlignment="Center" />
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right" IsChecked="False" Click="EnableParametersCheckBoxClick" />
</Grid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Controls">
<UniformGrid Columns="3" Margin="5,5,5,5">
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True" Content="Start Dumping"
Click="StartStopButtonClick" IsEnabled="False" Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="MediaScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for disks"
Click="MediaScanButtonClick" Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="CopyProtectScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for protection"
Click="CopyProtectScanButtonClick" Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
<Label x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Waiting for media..." />
</UniformGrid>
</GroupBox>
<Expander Margin="5,5 5 5" MaxHeight="300" HorizontalAlignment="Stretch" Header="Log Output" x:Name="LogPanel">
<controls:LogOutput x:Name="LogOutput"/>
</Expander>
</StackPanel>
</Grid>
</Window>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,373 @@
<Window x:Class="MPF.Windows.OptionsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MPF"
xmlns:controls="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
Title="Options" Width="515.132" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock TextAlignment="Center"><Bold>Options</Bold></TextBlock>
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<TabControl Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomTabControlStyle}">
<TabItem Header="User Interface" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="3">
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
IsChecked="{Binding Options.EnableDarkMode}"
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Paths" Style="{DynamicResource CustomTabItemStyle}">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2.0*" />
<ColumnDefinition Width="0.2*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Aaru Path" />
<TextBox x:Name="AaruPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.AaruPath}" VerticalContentAlignment="Center" />
<Button x:Name="AaruPathButton" Grid.Row="0" Grid.Column="2" Height="22" Width="22" Content="..."
Click="BrowseForPathClick" Style="{DynamicResource CustomButtonStyle}" />
<Label Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DiscImageCreator Path" />
<TextBox x:Name="DiscImageCreatorPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DiscImageCreatorPath}" VerticalContentAlignment="Center" />
<Button x:Name="DiscImageCreatorPathButton" Grid.Row="1" Grid.Column="2" Height="22" Width="22" Content="..."
Click="BrowseForPathClick" Style="{DynamicResource CustomButtonStyle}" />
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DD Path" />
<TextBox x:Name="DDPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DDPath}" VerticalContentAlignment="Center" />
<Button x:Name="DDPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..."
Click="BrowseForPathClick" Style="{DynamicResource CustomButtonStyle}" />
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Dumping Program" />
<ComboBox x:Name="InternalProgramComboBox" Grid.Row="3" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
ItemsSource="{Binding InternalPrograms}" Style="{DynamicResource CustomComboBoxStyle}" />
<Label Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Output Path" />
<TextBox x:Name="DefaultOutputPathTextBox" Grid.Row="4" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DefaultOutputPath}" VerticalContentAlignment="Center" />
<Button x:Name="DefaultOutputPathButton" Grid.Row="4" Grid.Column="2" Height="22" Width="22" Content="..."
Click="BrowseForPathClick" Style="{DynamicResource CustomButtonStyle}" />
</Grid>
</TabItem>
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
<UniformGrid Columns="2" Rows="7">
<CheckBox VerticalAlignment="Center" Content="Skip Type Detect"
IsChecked="{Binding Options.SkipMediaTypeDetection}"
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
IsChecked="{Binding Options.SkipSystemDetection}"
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
/>
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left" ItemsSource="{Binding Systems}" SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHeader}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
IsChecked="{Binding Options.IgnoreFixedDrives}"
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Show Separate Window"
IsChecked="{Binding Options.ToolsInSeparateWindow}"
ToolTip="Show program output in separate command window instead of in the log. Enable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Protection Scan"
IsChecked="{Binding Options.ScanForProtection}"
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
/>
<CheckBox VerticalAlignment="Center" Content="Eject After Dump"
IsChecked="{Binding Options.EjectAfterDump}"
ToolTip="Eject the disc from the drive after dumping has completed" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Show Disc Info"
IsChecked="{Binding Options.PromptForDiscInformation}"
ToolTip="Enable showing the disc information output after dumping" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Add Placeholders"
IsChecked="{Binding Options.AddPlaceholders}"
ToolTip="Enable adding placeholder text in the submissioninfo output for required and optional fields" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Output Submission JSON"
IsChecked="{Binding Options.OutputSubmissionJSON}"
ToolTip="Enable outputting a compressed JSON version of the submission info" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Artifacts"
IsChecked="{Binding Options.IncludeArtifacts}" IsEnabled="{Binding Options.OutputSubmissionJSON}"
ToolTip="Include log files in serialized JSON data" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Compress Log Files"
IsChecked="{Binding Options.CompressLogFiles}"
ToolTip="Compress output log files to reduce space" Margin="0,4"
/>
</UniformGrid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Default Speeds" Grid.ColumnSpan="2">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="2.0*"/>
<ColumnDefinition Width="40"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="CD-ROM" />
<Slider x:Name="DumpSpeedCDSlider" Grid.Row="0" Grid.Column="1" Minimum="1" Maximum="72" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static local:Constants.SpeedsForCDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedCD}" />
<TextBox x:Name="DumpSpeedCDTextBox" Grid.Row="0" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedCDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
<Label Grid.Row="1" Grid.Column="0" Content="DVD-ROM" />
<Slider x:Name="DumpSpeedDVDSlider" Grid.Row="1" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static local:Constants.SpeedsForDVDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedDVD}" />
<TextBox x:Name="DumpSpeedDVDTextBox" Grid.Row="1" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
<Label Grid.Row="2" Grid.Column="0" Content="BD-ROM" />
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static local:Constants.SpeedsForBDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedBD}" />
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedBDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
</Grid>
</GroupBox>
</StackPanel>
</TabItem>
<TabItem Header="Protection Scanning" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<UniformGrid Columns="2" Rows="2">
<CheckBox VerticalAlignment="Center" Content="Scan Archive Contents"
IsChecked="{Binding Options.ScanArchivesForProtection}"
ToolTip="Enable scanning archive contents during protection scanning (may drastically increase scanning time but is more accurate)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Executable Packers"
IsChecked="{Binding Options.ScanPackersForProtection}"
ToolTip="Include executable packers in outputted protections" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Force Scanning All Files"
IsChecked="{Binding Options.ForceScanningForProtection}"
ToolTip="Force scanning all files even if they're not executables" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
ToolTip="Include debug information during protection scans" Margin="0,4"
/>
</UniformGrid>
</StackPanel>
</TabItem>
<TabItem Header="Aaru" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="3">
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
IsChecked="{Binding Options.AaruEnableDebug}"
ToolTip="Enable debug output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
IsChecked="{Binding Options.AaruEnableVerbose}"
ToolTip="Enable verbose output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Force Dumping"
IsChecked="{Binding Options.AaruForceDumping}"
ToolTip="Enable forcing dump even if there are issues" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Strip Personal Data"
IsChecked="{Binding Options.AaruStripPersonalData}"
ToolTip="Enable stripping of personally identifiable information from metadata" Margin="0,4,0,0"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.AaruRereadCount}"
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
/>
</UniformGrid>
</TabItem>
<TabItem Header="DiscImageCreator" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="3">
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
IsChecked="{Binding Options.DICQuietMode}"
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Paranoid Mode"
IsChecked="{Binding Options.DICParanoidMode}"
ToolTip="Enable pedantic and super-safe flags" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Use CMI Flag"
IsChecked="{Binding Options.DICUseCMIFlag}"
ToolTip="Enable the CMI flag for supported disc types (DVD/HD-DVD only)" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Reset After Dump"
IsChecked="{Binding Options.DICResetDriveAfterDump}"
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.DICRereadCount}"
ToolTip="Specifies how many rereads are attempted on C2 error"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Logging" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2">
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
IsChecked="{Binding Options.VerboseLogging}"
ToolTip="Display all logging statements" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Auto-Open Log"
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
ToolTip="Open the log panel when the program launches" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Log Formatting"
IsChecked="{Binding Options.EnableLogFormatting}"
ToolTip="Format log lines written to the log, including overwriting previous lines on match. Disable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Progress Processing"
IsChecked="{Binding Options.EnableProgressProcessing}" IsEnabled="{Binding Options.EnableLogFormatting}"
ToolTip="Process lines written to the log to update the progress bar. Disable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Login Info" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump Credentials">
<UniformGrid Columns="5" Rows="1">
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Username" />
<TextBox x:Name="RedumpUsernameTextBox" Height="22" HorizontalAlignment="Stretch"
Text="{Binding Options.RedumpUsername}" />
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Password" />
<PasswordBox x:Name="RedumpPasswordBox" Height="22" HorizontalAlignment="Stretch" PasswordChar="*" />
<Button x:Name="RedumpLoginTestButton" Height="22" Width="80" Content="Test Login"
Click="OnRedumpTestClick" Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
</StackPanel>
</TabItem>
</TabControl>
<!-- Accept / Cancel -->
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
<Label/> <!-- Empty label for padding -->
<Button Name="AcceptButton" Height="25" Width="80" IsDefault="True" Content="Accept"
Click="OnAcceptClick" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
Click="OnCancelClick" Style="{DynamicResource CustomButtonStyle}" />
<Label/> <!-- Empty label for padding -->
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,232 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;
using MPF.Data;
using MPF.Redump;
using WPFCustomMessageBox;
using Button = System.Windows.Controls.Button;
using TextBox = System.Windows.Controls.TextBox;
namespace MPF.Windows
{
/// <summary>
/// Interaction logic for OptionsWindow.xaml
/// </summary>
public partial class OptionsWindow : Window
{
#region Fields
/// <summary>
/// Current set of options
/// </summary>
public Options Options { get; set; }
/// <summary>
/// List of available internal programs
/// </summary>
public List<Element<InternalProgram>> InternalPrograms { get; private set; } = PopulateInternalPrograms();
/// <summary>
/// Current list of supported system profiles
/// </summary>
public List<KnownSystemComboBoxItem> Systems { get; private set; } = KnownSystemComboBoxItem.GenerateElements().ToList();
/// <summary>
/// Flag for if settings were saved or not
/// </summary>
public bool SavedSettings { get; private set; } = false;
#endregion
public OptionsWindow(Options options)
{
InitializeComponent();
DataContext = this;
Options = options.Clone() as Options;
Load();
}
#region Helpers
private FolderBrowserDialog CreateFolderBrowserDialog()
{
FolderBrowserDialog dialog = new FolderBrowserDialog();
return dialog;
}
private OpenFileDialog CreateOpenFileDialog()
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.InitialDirectory = AppDomain.CurrentDomain.BaseDirectory;
dialog.Filter = "Executables (*.exe)|*.exe";
dialog.FilterIndex = 0;
dialog.RestoreDirectory = true;
return dialog;
}
/// <summary>
/// Load any options-related elements
/// </summary>
private void Load()
{
InternalProgramComboBox.SelectedIndex = InternalPrograms.FindIndex(r => r == Options.InternalProgram);
DefaultSystemComboBox.SelectedIndex = Systems.FindIndex(r => r == Options.DefaultSystem);
RedumpPasswordBox.Password = Options.RedumpPassword;
}
/// <summary>
/// Get a complete list of supported internal programs
/// </summary>
private static List<Element<InternalProgram>> PopulateInternalPrograms()
{
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.DD };
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
}
/// <summary>
/// Save any options-related elements
/// </summary>
private void Save()
{
var selectedInternalProgram = InternalProgramComboBox.SelectedItem as Element<InternalProgram>;
Options.InternalProgram = selectedInternalProgram?.Value ?? InternalProgram.DiscImageCreator;
var selectedDefaultSystem = DefaultSystemComboBox.SelectedItem as KnownSystemComboBoxItem;
Options.DefaultSystem = selectedDefaultSystem?.Value ?? KnownSystem.NONE;
Options.RedumpPassword = RedumpPasswordBox.Password;
SavedSettings = true;
}
/// <summary>
/// Find a TextBox by setting name
/// </summary>
/// <param name="name">Setting name to find</param>
/// <returns>TextBox for that setting</returns>
private TextBox TextBoxForPathSetting(string name)
{
return FindName(name + "TextBox") as TextBox;
}
#endregion
#region Event Handlers
/// <summary>
/// Handler for generic Click event
/// </summary>
private void BrowseForPathClick(object sender, EventArgs e)
{
Button button = sender as Button;
// strips button prefix to obtain the setting name
string pathSettingName = button.Name.Substring(0, button.Name.IndexOf("Button"));
// TODO: hack for now, then we'll see
bool shouldBrowseForPath = pathSettingName == "DefaultOutputPath";
CommonDialog dialog = shouldBrowseForPath ? (CommonDialog)CreateFolderBrowserDialog() : CreateOpenFileDialog();
using (dialog)
{
DialogResult result = dialog.ShowDialog();
if (result == System.Windows.Forms.DialogResult.OK)
{
string path;
bool exists;
if (shouldBrowseForPath)
{
path = (dialog as FolderBrowserDialog).SelectedPath;
exists = Directory.Exists(path);
}
else
{
path = (dialog as OpenFileDialog).FileName;
exists = File.Exists(path);
}
if (exists)
{
TextBoxForPathSetting(pathSettingName).Text = path;
}
else
{
CustomMessageBox.Show(
"Specified path doesn't exists!",
"Error",
MessageBoxButton.OK,
MessageBoxImage.Error
);
}
}
}
}
/// <summary>
/// Handler for CloseButton Click event
/// </summary>
private void CloseButtonClick(object sender, RoutedEventArgs e)
{
Close();
}
/// <summary>
/// Handler for MinimizeButton Click event
/// </summary>
private void MinimizeButtonClick(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
/// <summary>
/// Handler for AcceptButton Click event
/// </summary>
private void OnAcceptClick(object sender, EventArgs e)
{
Save();
Close();
}
/// <summary>
/// Handler for CancelButtom Click event
/// </summary>
private void OnCancelClick(object sender, EventArgs e)
{
Close();
}
/// <summary>
/// Test Redump credentials for validity
/// </summary>
private void OnRedumpTestClick(object sender, EventArgs e)
{
using (RedumpWebClient wc = new RedumpWebClient())
{
bool? loggedIn = wc.Login(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password);
if (loggedIn == true)
CustomMessageBox.Show(this, "Redump login credentials accepted!", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
else if (loggedIn == false)
CustomMessageBox.Show(this, "Redump login credentials denied!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
else
CustomMessageBox.Show(this, "Error validating credentials!", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
/// <summary>
/// Handler for Title MouseDown event
/// </summary>
private void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
#endregion
}
}

View File

@@ -0,0 +1,103 @@
<Window x:Class="MPF.Windows.RingCodeGuideWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MPF"
xmlns:controls="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
Title="Ring Code Guide" Width="500" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock TextAlignment="Center"><Bold>Ring Code Guide</Bold></TextBlock>
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="/Images/ring-code-guide.png"
HorizontalAlignment="Center" Stretch="Fill" Width="500" Height="500"
RenderOptions.BitmapScalingMode="HighQuality" />
<Label Grid.Row="1">
<Label.Content>
<TextBlock><Bold Foreground="Red">1. Mastering Ring:</Bold> Sony DADC&lt;tab&gt;A0100368905-0101&lt;tab&gt;15</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="2">
<Label.Content>
<TextBlock><Bold Foreground="Green">2. Mastering SID:</Bold> IFPI L553</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="3">
<Label.Content>
<TextBlock><Bold Foreground="Purple">3. Toolstamp/Mastering Code:</Bold> A2</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="4">
<Label.Content>
<TextBlock><Bold Foreground="LightBlue">4. Mould SID:</Bold> IFPI 94V1</TextBlock>
</Label.Content>
</Label>
</Grid>
</StackPanel>
</Grid>
</Window>

View File

@@ -0,0 +1,45 @@
using System.Windows;
using System.Windows.Input;
namespace MPF.Windows
{
/// <summary>
/// Interaction logic for RingCodeGuideWindow.xaml
/// </summary>
public partial class RingCodeGuideWindow : Window
{
public RingCodeGuideWindow()
{
InitializeComponent();
}
#region Event Handlers
/// <summary>
/// Handler for CloseButton Click event
/// </summary>
private void CloseButtonClick(object sender, RoutedEventArgs e)
{
Close();
}
/// <summary>
/// Handler for MinimizeButton Click event
/// </summary>
private void MinimizeButtonClick(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
}
/// <summary>
/// Handler for Title MouseDown event
/// </summary>
private void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
#endregion
}
}

View File

@@ -1,125 +0,0 @@
<Window x:Class="DICUI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:DICUI"
mc:Ignorable="d"
Title="Disc Image Creator GUI" Height="350" Width="500">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="3*"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Margin="5" HorizontalAlignment="Stretch" Header="Informations Input"/>
<GroupBox Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="5" HorizontalAlignment="Stretch" Header="Control"/>
<GroupBox Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="5" HorizontalAlignment="Stretch" Header="Status"/>
<Grid Grid.Row="0" Grid.Column="0" Margin="15,25,15,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center">Disc Type</Label>
<Label Grid.Row="1" Grid.Column="0" VerticalAlignment="Center">Output Filename</Label>
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center">Output Directory</Label>
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center">Drive Letter</Label>
<Label Grid.Row="4" Grid.Column="0" VerticalAlignment="Center">Drive Speed</Label>
<ComboBox x:Name="CB_DiscType" Grid.Row="0" Grid.Column="1" Height="22" Text="Bandai Playdia Quick Interactive System" SelectionChanged="CB_DiscType_SelectionChanged">
<ComboBoxItem Content="Unknown"/>
<ComboBoxItem Content="---------- Consoles ----------" IsEnabled="False"/>
<ComboBoxItem Content="Bandai Playdia Quick Interactive System"/>
<ComboBoxItem Content="Bandai / Apple Pippin"/>
<ComboBoxItem Content="Commodore Amiga CD / CD32 / CDTV"/>
<ComboBoxItem Content="Mattel HyperScan"/>
<ComboBoxItem Content="NEC PC-FX / PC-Engine / TurboGrafx CD"/>
<ComboBoxItem Content="Panasonic 3DO"/>
<ComboBoxItem Content="Philips CD-i"/>
<ComboBoxItem Content="Sega CD / Mega CD / Saturn"/>
<ComboBoxItem Content="SNK Neo Geo CD"/>
<ComboBoxItem Content="Sony PlayStation"/>
<ComboBoxItem Content="Sony PlayStation 2 (CD-Rom)"/>
<ComboBoxItem Content="Sony PlayStation 2 (DVD-Rom)"/>
<ComboBoxItem Content="Sony PlayStation 4"/>
<ComboBoxItem Content="VTech V.Flash - V.Smile Pro"/>
<ComboBoxItem Content="XBOX ONE"/>
<ComboBoxItem Content="---------- Computers ----------" IsEnabled="False"/>
<ComboBoxItem Content="Apple Macintosh (CD-Rom)"/>
<ComboBoxItem Content="Apple Macintosh (DVD-Rom)"/>
<ComboBoxItem Content="FM Towns series"/>
<ComboBoxItem Content="IBM PC Compatible (CD-Rom) SecuROM"/>
<ComboBoxItem Content="IBM PC Compatible (CD-Rom) Detectable Protection"/>
<ComboBoxItem Content="IBM PC Compatible (CD-Rom) C2 Error Protection"/>
<ComboBoxItem Content="IBM PC Compatible(CD - Rom) No Copy Protection"/>
<ComboBoxItem Content="IBM PC Compatible (DVD-Rom)"/>
<ComboBoxItem Content="NEC PC-88 / PC-98"/>
<ComboBoxItem Content="---------- Others ----------" IsEnabled="False"/>
<ComboBoxItem Content="Audio CD"/>
<ComboBoxItem Content="BD-Video"/>
<ComboBoxItem Content="PalmOS"/>
<ComboBoxItem Content="Photo CD"/>
<ComboBoxItem Content="PlayStation GameShark Updates"/>
<ComboBoxItem Content="Sega Lindbergh"/>
<ComboBoxItem Content="Tomy Kiss-Site"/>
<ComboBoxItem Content="Video CD"/>
</ComboBox>
<TextBox x:Name="TXT_OutputFilename" Grid.Row="1" Grid.Column="1" Height="22"></TextBox>
<TextBox x:Name="TXT_OutputDirectory" Grid.Row="2" Grid.Column="1" Height="22" Width="290" HorizontalAlignment="left" Text="ISO" ></TextBox>
<Button x:Name="BTN_OutputDirectoryBrowse" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse" Click="BTN_OutputDirectoryBrowse_Click" ></Button>
<ComboBox x:Name="CB_DriveLetter" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="left">
</ComboBox>
<ComboBox x:Name="CB_DriveSpeed" Grid.Row="4" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="left" SelectionChanged="CB_DriveSpeed_SelectionChanged">
<ComboBoxItem Content="4"/>
<ComboBoxItem Content="8"/>
<ComboBoxItem Content="16"/>
<ComboBoxItem Content="48"/>
<ComboBoxItem Content="Custom"/>
</ComboBox>
</Grid>
<Grid Grid.Row="1" Grid.Column="0" Margin="15,20,15,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Button x:Name="BTN_Start" Grid.Row="0" Grid.Column="0" Height="22" Width="150" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Start Dumping" Click="BTN_Start_Click" IsEnabled="False" />
<Button x:Name="BTN_Search" Grid.Row="0" Grid.Column="1" Height="22" Width="150" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for disks" Click="BTN_Search_Click" />
</Grid>
<Grid Grid.Row="2" Grid.Column="0" Margin="15,20,15,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="LBL_Status" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Waiting for CD or DVD..." />
</Grid>
</Grid>
</Window>

View File

@@ -1,287 +0,0 @@
using System;
using System.Linq;
using System.Windows;
using System.IO;
using WinForms = System.Windows.Forms;
using System.Diagnostics;
using System.Threading.Tasks;
namespace DICUI
{
public partial class MainWindow : Window
{
public void ScanForDisk()
{
BTN_Search.IsEnabled = false;
CB_DriveLetter.Items.Clear();
foreach (var d in DriveInfo.GetDrives().Where(d => d.DriveType == DriveType.CDRom))
{
if (d.IsReady == true)
{
TXT_OutputFilename.Text = d.VolumeLabel;
if (TXT_OutputFilename.Text == "")
{
TXT_OutputFilename.Text = "unknown";
}
CB_DriveLetter.Items.Add(d.Name.Replace(":\\", ""));
CB_DriveLetter.SelectedIndex = 0;
TXT_OutputDirectory.Text = "ISO" + "\\" + TXT_OutputFilename.Text + "\\";
LBL_Status.Content = "CD or DVD found ! Choose your Disc Type";
BTN_Start.IsEnabled = true;
CB_DriveSpeed.Text = "8";
}
else
{
LBL_Status.Content = "No CD or DVD found !";
}
BTN_Search.IsEnabled = true;
}
}
public void BrowseFolder()
{
WinForms.FolderBrowserDialog folderDialog = new WinForms.FolderBrowserDialog { ShowNewFolderButton = false, SelectedPath = System.AppDomain.CurrentDomain.BaseDirectory };
WinForms.DialogResult result = folderDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
String sPath = folderDialog.SelectedPath;
TXT_OutputDirectory.Text = sPath;
}
}
public async void StartDumping()
{
String VAR_Type = "";
String VAR_Switches = "";
String VAR_DriveLetter = CB_DriveLetter.Text;
String VAR_OutputDirectory = TXT_OutputDirectory.Text;
String VAR_OutputFilename = TXT_OutputFilename.Text;
String VAR_DriveSpeed = CB_DriveSpeed.Text;
Boolean VAR_IsPSX = false;
Boolean VAR_IsXBOXorPS4 = false;
BTN_Start.IsEnabled = false;
switch (Convert.ToString(CB_DiscType.Text))
{
case "Bandai Playdia Quick Interactive System":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Bandai / Apple Pippin":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Commodore Amiga CD / CD32 / CDTV":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Mattel HyperScan":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "NEC PC-FX / PC-Engine / TurboGrafx CD":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Panasonic 3DO":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Philips CD-i":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Sega CD / Mega CD / Saturn":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "SNK Neo Geo CD":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Sony PlayStation 2 (CD-Rom)":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "VTech V.Flash - V.Smile Pro":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Apple Macintosh (CD-Rom)":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "FM Towns series":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "NEC PC-88 / PC-98":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Audio CD":
VAR_Type = "audio";
VAR_Switches = "/c2";
break;
case "PalmOS":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Photo CD":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "PlayStation GameShark Updates":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Sega Lindbergh":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Tomy Kiss-Site":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Video CD":
VAR_Type = "cd";
VAR_Switches = "/c2";
break;
case "Sony PlayStation 2 (DVD-Rom)":
VAR_Type = "dvd";
VAR_Switches = "";
break;
case "Apple Macintosh (DVD-Rom)":
VAR_Type = "dvd";
VAR_Switches = "";
break;
case "IBM PC Compatible (DVD-Rom)":
VAR_Type = "dvd";
VAR_Switches = "";
break;
case "IBM PC Compatible(CD - Rom) No Copy Protection":
VAR_Type = "dvd";
VAR_Switches = "/c2";
break;
case "BD-Video":
VAR_Switches = "";
break;
case "Sony PlayStation":
VAR_Type = "cd";
VAR_Switches = "/c2";
VAR_IsPSX = true;
break;
case "Sony PlayStation 4":
VAR_Type = "bd";
VAR_Switches = "";
VAR_IsXBOXorPS4 = true;
break;
case "XBOX ONE":
VAR_Type = "bd";
VAR_Switches = "";
VAR_IsXBOXorPS4 = true;
break;
case "IBM PC Compatible (CD-Rom) SecuROM":
VAR_Type = "cd";
VAR_Switches = "/c2 /ns";
break;
case "IBM PC Compatible (CD-Rom) Detectable Protection":
VAR_Type = "cd";
VAR_Switches = "/c2 /sf";
break;
case "IBM PC Compatible (CD-Rom) C2 Error Protection":
VAR_Type = "cd";
VAR_Switches = "/c2 /ss";
break;
case "Unknown":
VAR_Type = "";
VAR_Switches = "";
break;
}
await Task.Run(
() =>
{
Process process = new Process();
process.StartInfo.FileName = "Release_ANSI\\DiscImageCreator.exe";
process.StartInfo.Arguments = VAR_Type + " " + VAR_DriveLetter + " " + VAR_OutputDirectory + "\\" + VAR_OutputFilename + " " + VAR_DriveSpeed + " " + VAR_Switches;
Console.WriteLine(process.StartInfo.Arguments);
process.Start();
process.WaitForExit();
});
if (VAR_IsXBOXorPS4 == true)
{
using (StreamWriter writetext = new StreamWriter("PS4orXBOXONE.bat"))
{
writetext.WriteLine("sg_raw.exe -v -r 4100 -R " + VAR_DriveLetter + ":" + "ad 01 00 00 00 00 00 00 10 04 00 00 -o \"PIC.bin\"");
}
Process processps4orxboxone = new Process();
processps4orxboxone.StartInfo.FileName = "PS4orXBOXONE.bat";
processps4orxboxone.Start();
processps4orxboxone.WaitForExit();
}
if (VAR_IsPSX == true)
{
using (StreamWriter writetext = new StreamWriter("PSX.bat"))
{
writetext.WriteLine("edccchk" + " " + "\"" + VAR_OutputDirectory + VAR_OutputFilename + ".bin" + "\" > " + "\"" + VAR_OutputDirectory + "edccchk1.txt");
writetext.WriteLine("edccchk" + " " + "\"" + VAR_OutputDirectory + VAR_OutputFilename + " (Track 1).bin" + "\" > " + "\"" + VAR_OutputDirectory + "edccchk1.txt");
writetext.WriteLine("edccchk" + " " + "\"" + VAR_OutputDirectory + VAR_OutputFilename + " (Track 01).bin" + "\" > " + "\"" + VAR_OutputDirectory + "edccchk1.txt");
writetext.WriteLine("psxt001z" + " " + "\"" + VAR_OutputDirectory + VAR_OutputFilename + ".bin" + "\" > " + "\"" + VAR_OutputDirectory + "psxt001z1.txt");
writetext.WriteLine("psxt001z" + " " + "\"" + VAR_OutputDirectory + VAR_OutputFilename + " (Track 1).bin" + "\" > " + "\"" + VAR_OutputDirectory + "psxt001z2.txt");
writetext.WriteLine("psxt001z" + " " + "\"" + VAR_OutputDirectory + VAR_OutputFilename + " (Track 01).bin" + "\" > " + "\"" + VAR_OutputDirectory + "psxt001z3.txt");
writetext.WriteLine("psxt001z" + " " + "--libcrypt " + "\"" + VAR_OutputDirectory + VAR_OutputFilename + ".sub\" > " + "\"" + VAR_OutputDirectory + "libcrypt.txt");
writetext.WriteLine("psxt001z" + " " + "--libcryptdrvfast " + VAR_DriveLetter + " > " + "\"" + VAR_OutputDirectory + "libcryptdrv.log");
}
Process processpsx = new Process();
processpsx.StartInfo.FileName = "PSX.bat";
processpsx.Start();
processpsx.WaitForExit();
}
BTN_Start.IsEnabled = true;
}
public MainWindow()
{
InitializeComponent();
ScanForDisk();
}
private void BTN_Start_Click(object sender, RoutedEventArgs e)
{
StartDumping();
}
private void BTN_OutputDirectoryBrowse_Click(object sender, RoutedEventArgs e)
{
BrowseFolder();
}
private void BTN_Search_Click(object sender, RoutedEventArgs e)
{
ScanForDisk();
}
private void CB_DiscType_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
LBL_Status.Content = "Ready to dump";
}
private void CB_DriveSpeed_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
if (CB_DriveSpeed.SelectedIndex == 4)
{
CB_DriveSpeed.Items.Clear();
CB_DriveSpeed.IsEditable = true;
}
}
}
}

View File

@@ -1,55 +0,0 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("DICUI")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DICUI")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -1,63 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace DICUI.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("DICUI.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More