Compare commits

..

185 Commits
2.1 ... 2.3

Author SHA1 Message Date
Matt Nadareski
87400793eb Bump version to 2.3 2022-02-05 13:45:06 -08:00
Matt Nadareski
45f79d95b1 Adjust paths for DIC just before dumping (fixes #358) 2022-02-04 20:46:56 -08:00
Matt Nadareski
c8a4a61028 Normalize PS1/PS2 executable names 2022-02-03 22:28:14 -08:00
Matt Nadareski
44091981b2 Check explicitly for no matches on Redump pull (fixes #357) 2022-02-03 15:43:17 -08:00
Matt Nadareski
d3352643fc Ensure drive is not null for volume labels (fixes #356) 2022-02-02 20:05:15 -08:00
Matt Nadareski
114c7fb38a Make error clearer if something is unsupported in Check 2022-02-02 19:59:53 -08:00
Matt Nadareski
dc7da708dc Add alternate pseudo-tag for Playable Demos 2022-02-02 12:54:08 -08:00
Matt Nadareski
72e56aa1c7 Ensure Games pseudo-tag is multi-line 2022-02-02 12:50:32 -08:00
Matt Nadareski
99ceab07ad Ensure version only pulled if one doesn't exist (fixes #355) 2022-02-01 20:54:26 -08:00
Matt Nadareski
c0f6c072ce Read longer string for Saturn internal serial 2022-02-01 20:51:33 -08:00
Matt Nadareski
e039124f6c Add verification reminders for pulled tags 2022-02-01 13:04:28 -08:00
Matt Nadareski
c96e4a4c7a Add another hand-formatted version of SS tag 2022-01-31 10:50:53 -08:00
Matt Nadareski
622a08acf3 Update changelog 2022-01-31 09:49:07 -08:00
Matt Nadareski
f44b6bf0d0 Slightly rename UK and USA regions for UI 2022-01-31 09:47:42 -08:00
Matt Nadareski
a6d75e15ea Check for $SystemUpdate folder for X360 discs 2022-01-30 20:51:47 -08:00
Matt Nadareski
a02f03c4cb Move internal serial before volume label 2022-01-30 15:57:12 -08:00
Matt Nadareski
d48f5132fb Add another hand-formatted version of SS tag 2022-01-28 23:12:25 -08:00
Matt Nadareski
ed4ac24efa Add Sierra ID to list of pseudo-tags 2022-01-28 09:24:37 -08:00
Matt Nadareski
9f3b8a7c2c Adjust long names for some languages 2022-01-27 21:40:23 -08:00
Matt Nadareski
612d4bb1f5 Fix incorrect region two-letter code 2022-01-27 21:25:58 -08:00
Matt Nadareski
b58a50d246 Disable unnecessary cuesheet parsing 2022-01-27 20:49:32 -08:00
Matt Nadareski
af83811d57 Fix parsing of non-tag tags again 2022-01-27 17:17:42 -08:00
Matt Nadareski
66835fe6ab Fix non-tag tag shortnames 2022-01-27 16:27:40 -08:00
Matt Nadareski
34cc1d33c6 Hook up additional Xbox field to disc info window 2022-01-27 16:04:14 -08:00
Matt Nadareski
a42d14e3b8 Fix incorrect language three-letter code 2022-01-27 15:39:09 -08:00
Matt Nadareski
87aa165edf Add more non-tag support; rearrange info window 2022-01-27 13:35:47 -08:00
Matt Nadareski
d217d62007 Start supporting ordered tags and non-tags 2022-01-27 12:13:17 -08:00
Matt Nadareski
27bcc0d40a Better helper method organization 2022-01-27 10:58:40 -08:00
Matt Nadareski
e1df075cde Make site code formatting helper method 2022-01-27 10:51:27 -08:00
Matt Nadareski
8358692e8d Ensure ordering in output site tags 2022-01-27 10:45:48 -08:00
Matt Nadareski
e1fae01dab Add support for all ISO region codes 2022-01-26 23:29:06 -08:00
Matt Nadareski
d206ab140a Add support for all ISO language codes 2022-01-26 21:38:11 -08:00
Matt Nadareski
9d8722ab17 Try to delete old log archive before writing (fixes #348) 2022-01-26 09:51:37 -08:00
Matt Nadareski
c4fa40c403 Sync with Redump region and language selection (fixes #349) 2022-01-26 09:41:34 -08:00
Matt Nadareski
1d0b06bfbe Use volume label in checks, not formatted version 2022-01-20 13:20:15 -08:00
Matt Nadareski
2cdf473dcb Be smarter about volume labels 2022-01-20 13:15:53 -08:00
Matt Nadareski
1af9e2c2da Conditionally pull region from Redump 2022-01-14 16:30:20 -08:00
Matt Nadareski
9a1815fa1e Differentiate XMID and XeMID 2022-01-14 13:06:11 -08:00
Matt Nadareski
f601961c49 Fix crash on invalid parameters 2022-01-13 10:25:57 -08:00
Matt Nadareski
406acd34c5 Fix Sega CD internal serial reading 2022-01-09 22:38:22 -08:00
Matt Nadareski
31cdcbbc25 Reformat Saturn internal date (fixes #346) 2022-01-09 14:44:55 -08:00
Matt Nadareski
2215ce71c9 Make protection read-only field multiline 2022-01-07 21:41:49 -08:00
Matt Nadareski
1872fbb1c8 Fix incorrect header check 2022-01-07 20:57:47 -08:00
Matt Nadareski
d99f912ac2 Add hidden debug option for "ShowDebugViewMenuItem" (fixes #334) 2022-01-07 13:25:46 -08:00
Matt Nadareski
00a76fb648 Only include booleans if the value is true 2022-01-07 12:49:10 -08:00
Matt Nadareski
187e951a47 Fix IsReadOnly 2022-01-07 09:04:12 -08:00
Matt Nadareski
c0b9b27aae Adjust width ratios for disc info window 2022-01-06 22:19:06 -08:00
Matt Nadareski
b76bb17396 Convert postgap and VCD fields to checkboxes 2022-01-06 21:51:20 -08:00
Matt Nadareski
2efa6d3623 Fix scrolling issues in disc info window 2022-01-06 21:31:46 -08:00
Matt Nadareski
3972ce633d Changed IsEnabled to IsReadOnly 2022-01-06 21:30:47 -08:00
Matt Nadareski
0dc7901393 Further disc info window tweaks 2022-01-06 17:08:45 -08:00
Matt Nadareski
a25ba6eaa5 Add tab setting (fixes #303) 2022-01-06 15:25:25 -08:00
Matt Nadareski
4ea48dfe57 Tweak minimalized layout a bit more 2022-01-06 14:57:38 -08:00
Matt Nadareski
8f7ad8b2ee Unban newly opened consoles 2022-01-06 10:23:07 -08:00
Matt Nadareski
3b9800df07 Add <tab> processing 2022-01-04 21:28:51 -08:00
Matt Nadareski
4c80d3234e Logically group more things in disc info window 2022-01-04 21:19:46 -08:00
Matt Nadareski
9e4af1d66b Remove Enter/Escape registration on disc info window 2022-01-04 11:13:17 -08:00
Matt Nadareski
73555df2ea Omit volume label for "Audio CD" (fixes #343) 2022-01-03 21:26:54 -08:00
Matt Nadareski
3ca78604fd Fix InfoTool tests 2022-01-02 22:01:21 -08:00
Matt Nadareski
0138046923 Add newlines for mutliline special fields 2022-01-02 21:54:49 -08:00
Matt Nadareski
2129184209 Add missing continue statement 2022-01-02 13:58:58 -08:00
Matt Nadareski
dd2116f8a6 Fix newline skipping 2022-01-01 22:24:03 -08:00
Matt Nadareski
814c2d9149 Force scroll visibility, tweak text sizes again 2022-01-01 21:42:05 -08:00
Matt Nadareski
b3f7276044 Skip unnecessary newlines in parsing 2022-01-01 21:36:39 -08:00
Matt Nadareski
ad88aa980b Tweak more Disc Info window formatting 2022-01-01 21:13:35 -08:00
Matt Nadareski
aca55e9203 Handle pulled linebreaks better, again 2022-01-01 20:46:44 -08:00
Matt Nadareski
cc3330bb27 Skip anti-modchip string in some cases 2022-01-01 20:42:52 -08:00
Matt Nadareski
1370909db7 Handle pulled linebreaks better (fixes #342) 2022-01-01 14:18:14 -08:00
Matt Nadareski
08cc0c394b Sanitize filename after check (fixes #341) 2022-01-01 14:16:58 -08:00
Matt Nadareski
cb6692aea3 Add even more safety to clone 2022-01-01 13:58:33 -08:00
Matt Nadareski
b3badb3a55 Add first attempt 2-layer ringcode guide 2021-12-31 14:21:14 -08:00
Matt Nadareski
cbf73901d3 Add model for 2-layer ringcode guide 2021-12-31 13:54:31 -08:00
Matt Nadareski
4822e45d58 Ensure all fields are read-only on read-only tab 2021-12-30 22:21:33 -08:00
Matt Nadareski
1d930d36bf Be smarter about showing update checks 2021-12-30 21:00:01 -08:00
Matt Nadareski
9effcc403d Allow internal serial and volume label to be hidden 2021-12-30 20:56:55 -08:00
Matt Nadareski
05dcc039bd Tweak new disc information fields and tabs 2021-12-30 20:48:32 -08:00
Matt Nadareski
cb08656abc Add horizontal scroll to user input 2021-12-30 20:45:15 -08:00
Matt Nadareski
69b22fc736 Show most read-only fields in new tab (fixes #301) 2021-12-30 17:27:08 -08:00
Matt Nadareski
bf857f6ce7 Fix CSSKey log handling (fixes #333) 2021-12-30 15:47:44 -08:00
Matt Nadareski
7ebf2378b3 Try to handle multi-line fields during parsing 2021-12-30 15:36:58 -08:00
Matt Nadareski
aec25dab37 Clean up default handling of fields 2021-12-30 15:23:17 -08:00
Matt Nadareski
e11969780d Add new tabs for special site information 2021-12-30 14:52:49 -08:00
Matt Nadareski
02c98b1547 Add internal structure for special site codes 2021-12-30 13:00:55 -08:00
Matt Nadareski
c864589478 Start overhauling Redump information pulling, again 2021-12-30 11:09:37 -08:00
Matt Nadareski
bdea1593be Bump version to 2.2 2021-12-30 09:47:44 -08:00
Matt Nadareski
f6b78c07ca Add safety around volume labels 2021-12-29 10:00:28 -08:00
Matt Nadareski
982a217d32 Temporarily disable pulling comments from Redump pages 2021-12-27 13:10:48 -08:00
Matt Nadareski
06588752ad Allow for better matching of multi track discs 2021-12-25 21:37:19 -08:00
Matt Nadareski
9b057d7141 Validate track count when matching Redump 2021-12-25 21:23:24 -08:00
Matt Nadareski
93fb3a85b5 Update changelist 2021-12-25 14:22:17 -08:00
Matt Nadareski
7320f9ba66 Fix ISN string 2021-12-25 14:22:05 -08:00
Matt Nadareski
a94f43ae0c Fix missing ISN usages 2021-12-25 13:54:06 -08:00
Matt Nadareski
e19a3f02e5 Move constants to proper place 2021-12-24 21:53:53 -08:00
Matt Nadareski
375c2c896c Invert condition for volume label 2021-12-24 21:32:45 -08:00
Matt Nadareski
b151e79aed Capture newlines in Redump fields 2021-12-24 21:06:07 -08:00
Matt Nadareski
b45901c133 Fix default value bug 2021-12-24 15:37:10 -08:00
Matt Nadareski
fab54ca0ae Use the Volume Label special site code (fixes #337) 2021-12-24 15:13:06 -08:00
Matt Nadareski
31dd32f19b Add internal support for all Redump site codes (fixes #336) 2021-12-24 14:30:50 -08:00
Matt Nadareski
f51c79d282 Allow default system if skipping system detection enabled 2021-12-21 21:11:18 -08:00
Matt Nadareski
743c943363 Fix saving path settings if set from dialog 2021-12-21 20:49:35 -08:00
Matt Nadareski
91f6e266d1 Fix output dialog issues in Options window 2021-12-21 12:50:19 -08:00
Matt Nadareski
8e843647bf Overhaul XeMID handling (fixes #334) 2021-12-21 10:54:57 -08:00
Matt Nadareski
151402dc50 Detect BD-Video (fixes #332) 2021-12-11 21:00:27 -08:00
Matt Nadareski
057f340c9c Refine the "missing disc" text (fixes #329) 2021-11-30 15:03:14 -08:00
Matt Nadareski
f3f9c63156 Add and overhaul some more tests 2021-11-26 14:07:07 -08:00
Matt Nadareski
7910a79917 Minor code cleanups 2021-11-26 14:06:57 -08:00
Matt Nadareski
cc7acfcd00 Minor usings cleanup 2021-11-25 22:45:13 -08:00
Matt Nadareski
5580e208f5 More test changes/additions, new helper method 2021-11-25 22:38:59 -08:00
Matt Nadareski
56ef06d651 Add enum converter tests, fix other related things 2021-11-25 22:05:35 -08:00
Matt Nadareski
204dfca126 Remove unused converter class 2021-11-25 21:21:27 -08:00
Matt Nadareski
eb337a8aaf Reorganize result tests 2021-11-25 21:06:19 -08:00
TonyLizard
aa6fd781e8 Adding a space to the -No Title Key- string in the DVD Protection section (#328) 2021-11-25 21:03:28 -08:00
Matt Nadareski
32bcfa1d42 Add SubmissionInfo serialization tests 2021-11-24 23:58:08 -08:00
Matt Nadareski
3b2e14d0de Finish filling out first round of Extension tests 2021-11-24 23:36:12 -08:00
Matt Nadareski
88a0ce38f9 Fill in another significant chunk of extension tests 2021-11-24 23:19:58 -08:00
Matt Nadareski
110c8337aa Overhaul cross-enumeration RedumpLib tests 2021-11-24 23:02:13 -08:00
Matt Nadareski
3dc79fea6b Cleanup some old test classes 2021-11-24 22:19:57 -08:00
Matt Nadareski
fb036385f9 Update changelog 2021-11-24 22:11:56 -08:00
Matt Nadareski
428bb2817b Strip ";1" from DVD protection results (fixes #327) 2021-11-24 22:10:10 -08:00
Matt Nadareski
791caff240 Minor cleanups and safety checks 2021-11-24 22:05:10 -08:00
Matt Nadareski
d92015d071 Add sanitization for StarForce 2021-11-19 11:03:24 -08:00
Matt Nadareski
de98bd9e0b Update to DIC 20211101 2021-11-19 10:33:13 -08:00
Matt Nadareski
67f5a7794a Fix protection sanitization; add tests 2021-11-19 10:09:34 -08:00
Matt Nadareski
fbef94f634 Only exclude Audio CDs from offset writing 2021-11-12 11:06:27 -08:00
Matt Nadareski
0e8363f06e Ensure parameters box is safer during options save 2021-11-08 20:45:37 -08:00
Matt Nadareski
a1bafe0426 Update changelist 2021-11-08 10:18:15 -08:00
Matt Nadareski
c51461592c Start adding regression tests for DIC 2021-11-05 14:44:58 -07:00
Matt Nadareski
ef5f996ec4 Remove offset from DIC output (fixes #326) 2021-11-05 11:25:45 -07:00
Matt Nadareski
aa5998a52e Be consistent with LBA values (fixes #325) 2021-11-04 22:20:52 -07:00
Matt Nadareski
37207c1cb0 Remove lower bound checking on LBA values 2021-11-04 21:59:26 -07:00
Matt Nadareski
d1813fdbc7 Make anti-modchip scans safer 2021-11-04 15:23:40 -07:00
Matt Nadareski
faf9d1d6f8 Add retry to Redump calls (fixes #309) 2021-11-02 11:35:31 -07:00
Matt Nadareski
0360df0604 Wrap directory checks in case of corruption 2021-11-02 10:54:08 -07:00
Matt Nadareski
fe6487eeb4 Fix enum converters (fixes #306) 2021-11-01 12:02:07 -07:00
Matt Nadareski
b9eedd1cf6 Add submission JSON option to check 2021-11-01 10:43:55 -07:00
Shiz
26281b8f0b Fix various arcade system metadata issues (#324)
* redumplib: correct media type for Sega Nu

* redumplib: merge Namco System 246 and System 256

* redumplib: drop Namco System 357 / 369

This was not a disc-based system, none of its released forms
have a disc reader.

* redumplib: add SEGA ALLS
2021-10-26 10:30:14 -07:00
sadikyo
f0508fdf93 Minor spelling fix. (#321)
Co-authored-by: sadik <sadik@TONY-HOME>
2021-10-22 16:51:40 -07:00
Matt Nadareski
166d69c7ec Fix NRE in InfoTool 2021-10-03 13:53:23 -07:00
Matt Nadareski
fabf08ba55 HTTP encode password for Redump login, again (fixes #314) 2021-10-01 09:01:41 -07:00
Matt Nadareski
0e5d8af0e9 Update to DIC 20211101 2021-09-30 22:09:09 -07:00
Matt Nadareski
3e1263ebff Update to Aaru v5.3.0 LTS 2021-09-30 20:59:13 -07:00
Matt Nadareski
c0ed830241 These should live in Drive 2021-09-29 16:37:28 -07:00
Matt Nadareski
4209158807 Extract anything that can be static 2021-09-29 16:16:54 -07:00
Matt Nadareski
6d79ebf449 Post-separation cleanup 2021-09-29 15:18:35 -07:00
Matt Nadareski
88ab691f07 Simplify remaining structure in MPF.Library 2021-09-29 15:05:17 -07:00
Matt Nadareski
37b1dc574c Separate out modules into own library; fix csproj's 2021-09-29 14:56:15 -07:00
Matt Nadareski
ad56e249ed Small cleanups for dead code 2021-09-29 12:24:03 -07:00
Matt Nadareski
f28cfe3d69 Segment out protection checks a bit more 2021-09-29 12:17:34 -07:00
Matt Nadareski
9643442281 Create MPF.Core and move a lot to there 2021-09-29 11:48:37 -07:00
Matt Nadareski
dd44ec7ac1 Clean up usings 2021-09-29 10:28:15 -07:00
Matt Nadareski
801c1126cf Remove now-unnecessary excludes from csproj 2021-09-29 09:40:01 -07:00
Matt Nadareski
4fd72efc46 Move CICMMetadata to top-level 2021-09-29 09:36:13 -07:00
Matt Nadareski
9ec045cb21 Move cuesheet code to separate DLL 2021-09-29 09:08:10 -07:00
Matt Nadareski
52801ee94c Null-safeguard RedumpLib conversions 2021-09-28 11:58:11 -07:00
Matt Nadareski
2077d53964 Don't print entire exception to popup 2021-09-28 11:46:37 -07:00
Matt Nadareski
14046e9217 Catch exception on update check 2021-09-28 11:29:23 -07:00
Matt Nadareski
2766debffc Fix double include 2021-09-27 22:12:32 -07:00
Matt Nadareski
4f4688899c Attempt to use Aaru.Devices and fail 2021-09-27 21:38:35 -07:00
Matt Nadareski
8151db9696 Remove non-4.8 from AppVeyor artifacts 2021-09-27 20:59:56 -07:00
Matt Nadareski
227c61b588 Add .NET 5.0 as build target 2021-09-27 20:59:37 -07:00
Matt Nadareski
61224fba7b Update to Aaru v5.3.0-rc2 2021-09-27 14:27:56 -07:00
Matt Nadareski
1dbf5516d8 Forgot ToLowerInvariant (fixes #304) 2021-09-22 14:38:24 -07:00
Matt Nadareski
10fe6ac32a Write out label-side data, if it exists (fixes #311) 2021-09-22 13:25:16 -07:00
Matt Nadareski
e39ee66726 Fix BOS-related things 2021-09-22 12:46:43 -07:00
Matt Nadareski
fff2db75d0 Add on-startup "check for updates" option (fixes #302) 2021-09-22 11:30:56 -07:00
Matt Nadareski
3a14db53c6 Update packages 2021-09-22 11:19:01 -07:00
Matt Nadareski
8b76b82e0f Update default Aaru dumping parameters 2021-09-17 22:23:11 -07:00
Matt Nadareski
e110c55e1a Create proper section for previous change 2021-09-16 14:08:07 -07:00
Matt Nadareski
85c30fbad3 Try to use another method of success/failure in AppVeyor 2021-09-16 14:07:19 -07:00
Matt Nadareski
14e5c74c65 Try to fix AppVeyor config 2021-09-16 13:37:24 -07:00
Matt Nadareski
b180e31b56 Update to Aaru v5.3.0-rc1 2021-09-16 12:02:19 -07:00
Matt Nadareski
d172a45891 Start adding special filtering to BOS outputs 2021-09-13 15:32:45 -07:00
Matt Nadareski
e74289116e Change how aux hashes are displayed (fixes #308) 2021-09-13 12:13:12 -07:00
Matt Nadareski
68b932bad0 Fix extension and conversion methods 2021-08-18 23:17:47 -07:00
Matt Nadareski
c4793e849c Move a bit more 2021-08-18 22:48:31 -07:00
Matt Nadareski
1c525a6d2e Update library, move more things there 2021-08-18 22:13:38 -07:00
Matt Nadareski
c8610ee16a Convert to using separate Redump library code 2021-08-18 15:32:45 -07:00
Matt Nadareski
0fb3bc1106 Clarify CUE or ISO in Check help text 2021-08-09 22:55:17 -07:00
Matt Nadareski
3cf133d750 Add HD-DVD-Video Redump support 2021-08-08 21:53:27 -07:00
Matt Nadareski
f0be6b0bc9 MVVM Overhaul (#300)
* Start migrating to MVVM

* Finish gutting LogOutput

* Remove now-useless regions

* Incremental initialization of the UI

* Add options directly to App-level

* Move options globally

* Accept -> OK

* Add future work notes to App.xaml.cs

* Add back cancel to DiscInformationWindow

* Enable/Disable instead of Add/Remove

* Add disc eject reminder option

* Add PS3VOLUME check
2021-08-04 14:17:53 -07:00
Matt Nadareski
e0ccb8b10d Add drive size check (fixes #299) 2021-08-02 20:40:37 -07:00
Matt Nadareski
940afae1fd Update PPC for Redump values 2021-08-01 13:24:39 -07:00
Matt Nadareski
94ba339ef2 Add Pocket PC support (fixes #298) 2021-07-30 13:04:21 -07:00
Matt Nadareski
bfead10d87 Use MCN check for Sega header, not CVD (fixes #297) 2021-07-27 22:06:26 -07:00
Matt Nadareski
97f9a73fc1 Fix Saturn header finding 2021-07-24 13:40:38 -07:00
121 changed files with 18815 additions and 13507 deletions

2
.gitmodules vendored
View File

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

View File

@@ -1,3 +1,138 @@
### 2.3 (2022-02-05)
- Start overhauling Redump information pulling, again
- Add internal structure for special site codes
- Add new tabs for special site information
- Clean up default handling of fields
- Try to handle multi-line fields during parsing
- Fix CSSKey log handling
- Show most read-only fields in new tab
- Add horizontal scroll to user input
- Tweak new disc information fields and tabs
- Allow internal serial and volume label to be hidden
- Be smarter about showing update checks
- Ensure all fields are read-only on read-only tab
- Add model for 2-layer ringcode guide
- Add first attempt 2-layer ringcode guide
- Add even more safety to clone
- Sanitize filename after check
- Handle pulled linebreaks better
- Skip anti-modchip string in some cases
- Handle pulled linebreaks better, again
- Tweak more Disc Info window formatting
- Skip unnecessary newlines in parsing
- Force scroll visibility, tweak text sizes again
- Fix newline skipping
- Add missing continue statement
- Add newlines for mutliline special fields
- Omit volume label for "Audio CD"
- Remove Enter/Escape registration on disc info window
- Logically group more things in disc info window
- Remove tab key from disc info window
- Add `<tab>` processing
- Unban newly opened consoles
- Tweak minimalized layout a bit more
- Add tab setting
- Further disc info window tweaks
- Changed IsEnabled to IsReadOnly
- Fix scrolling issues in disc info window
- Convert postgap and VCD fields to checkboxes
- Adjust width ratios for disc info window
- Fix IsReadOnly
- Only include booleans if the value is true
- Add hidden debug option for "ShowDebugViewMenuItem"
- Fix incorrect header check
- Make protection read-only field multiline
- Reformat Saturn internal date
- Fix Sega CD internal serial reading
- Fix crash on invalid parameters
- Differentiate XMID and XeMID
- Conditionally pull region from Redump
- Be smarter about volume labels
- Use volume label in checks, not formatted version
- Sync with Redump region and language selection
- Try to delete old log archive before writing
- Add support for all ISO language codes
- Add support for all ISO region codes
- Ensure ordering in output site tags
- Make site code formatting helper method
- Better helper method organization
- Start supporting ordered tags and non-tags
- Add more non-tag support; rearrange info window
- Fix incorrect language three-letter code
- Hook up additional Xbox field to disc info window
- Fix non-tag tag shortnames
- Fix parsing of non-tag tags again
- Disable unnecessary cuesheet parsing
- Fix incorrect region two-letter code
- Adjust long names for some languages
- Add Sierra ID to list of pseudo-tags
- Add another hand-formatted version of SS tag
- Move internal serial before volume label
- Check for $SystemUpdate folder for X360 discs
- Slightly rename UK and USA regions for UI
- Add another hand-formatted version of SS tag
- Add verification reminders for pulled tags
- Read longer string for Saturn internal serial
- Ensure version only pulled if one doesn't exist
- Ensure Games pseudo-tag is multi-line
- Add alternate pseudo-tag for Playable Demos
- Make error clearer if something is unsupported in Check
- Ensure drive is not null for volume labels
- Check explicitly for no matches on Redump pull
- Normalize PS1/PS2 executable names
- Adjust paths for DIC just before dumping
### 2.2 (2021-12-30)
- Fix Saturn header finding
- Add Pocket PC support
- Add HD-DVD-Video support
- Convert to using separate Redump library code
- Update to Aaru v5.3.0-rc1
- Update to BurnOutSharp 1.8.0
- Update support packages
- Add on-startup "check for updates" option
- Update to Aaru v5.3.0-rc2
- Add .NET 5.0 as build target
- Remove .NET Core 3.1 and .NET 5.0 from AppVeyor build artifacts
- Null-safeguard RedumpLib conversions
- Move cuesheet code to separate DLL
- Move CICMMetadata to top-level
- Separate out remaining functionality into individual DLLs
- Update to Aaru v5.3.0 LTS
- Update to DIC 20211001
- HTTP encode password for Redump login, again
- Fix NullReferenceException in anti-modchip scans
- Update arcade metadata (Shizmob)
- Add JSON output option for MPF.Check
- Fix JSON output serialization
- Ensure corrupt directories on media don't throw errors
- Add retry to Redump external calls
- Make anti-modchip scans even safer
- Remove lower bound checking on LBA values for DIC
- Remove offset for audio discs on DIC output
- Start adding regression tests for DIC
- Ensure parameters box is safer during options save
- Fix protection sanitization and add regression tests
- Update to DIC 20211101
- Add protection sanitization for StarForce
- Trim filenames for DVD protection from DIC
- Fill out internal tests around Redump library
- Refine the "missing disc" text
- Overhaul XeMID handling
- Fix output dialog issues in Options window
- Fix saving path settings if set from dialog
- Allow default system if skipping system detection enabled
- Add internal support for all Redump site codes
- Use the Volume Label special site code
- Capture newlines in Redump fields
- Invert condition for volume label
- Fix missing ISN usages
- Fix ISN string
- Validate track count when matching Redump
- Allow for better matching of multi track discs
- Temporarily disable pulling comments from Redump pages
- Add safety around volume labels
### 2.1 (2021-07-22)
- Enum, no more
- Sony works backward

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<OutputType>Exe</OutputType>
<Title>MPF Check</Title>
@@ -10,7 +10,7 @@
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.1</Version>
<Version>2.3</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -28,7 +28,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.7.0" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
@@ -46,6 +46,7 @@
<Project>{51ab0928-13f9-44bf-a407-b6957a43a056}</Project>
<Name>MPF.Library</Name>
</ProjectReference>
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,9 +1,13 @@
using System;
using System.IO;
using System.Linq;
using BurnOutSharp;
using MPF.Data;
using MPF.Redump;
using MPF.Utilities;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.Library;
using RedumpLib.Data;
using RedumpLib.Web;
namespace MPF.Check
{
@@ -33,7 +37,7 @@ namespace MPF.Check
}
else if (args[0] == "-ls" || args[0] == "--listsystems")
{
ListKnownSystems();
ListSystems();
Console.ReadLine();
return;
}
@@ -46,16 +50,16 @@ namespace MPF.Check
}
// Check the MediaType
var mediaType = Converters.ToMediaType(args[0].Trim('"'));
var mediaType = EnumConverter.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)
// Check the RedumpSystem
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
if (knownSystem == null)
{
DisplayHelp($"{args[1]} is not a recognized system");
return;
@@ -65,7 +69,7 @@ namespace MPF.Check
string username = null, password = null;
string internalProgram = "DiscImageCreator";
string path = string.Empty;
bool scan = false, compress = false;
bool scan = false, compress = false, json = false;
// Loop through and process options
int startIndex = 2;
@@ -108,13 +112,19 @@ namespace MPF.Check
}
// Scan for protection (requires device path)
else if (args[startIndex].StartsWith("-s") || args[startIndex].StartsWith("--scan"))
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
{
scan = true;
}
// Output submission JSON
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
{
json = true;
}
// Compress log and extraneous files
else if (args[startIndex].StartsWith("-z") || args[startIndex].StartsWith("--zip"))
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
{
compress = true;
}
@@ -163,9 +173,11 @@ namespace MPF.Check
// Now populate an environment
var options = new Options
{
InternalProgram = Converters.ToInternalProgram(internalProgram),
InternalProgram = EnumConverter.ToInternalProgram(internalProgram),
ScanForProtection = scan && !string.IsNullOrWhiteSpace(path),
PromptForDiscInformation = false,
ShowDiscEjectReminder = false,
OutputSubmissionJSON = json,
CompressLogFiles = compress,
RedumpUsername = username,
@@ -194,7 +206,7 @@ namespace MPF.Check
Console.WriteLine(error);
Console.WriteLine("Usage:");
Console.WriteLine("MPF.Check.exe <mediatype> <system> [options] </path/to/output.bin> ...");
Console.WriteLine("MPF.Check.exe <mediatype> <system> [options] </path/to/output.cue/iso> ...");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
@@ -207,6 +219,7 @@ namespace MPF.Check
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("-j, --json Enable submission JSON output");
Console.WriteLine("-z, --zip Enable log file compression");
Console.WriteLine();
}
@@ -242,17 +255,19 @@ namespace MPF.Check
}
/// <summary>
/// List all known systems with their short usable names
/// List all systems with their short usable names
/// </summary>
private static void ListKnownSystems()
private static void ListSystems()
{
Console.WriteLine("Supported Known Systems:");
foreach (var val in Enum.GetValues(typeof(KnownSystem)))
{
if (((KnownSystem)val) == KnownSystem.NONE)
continue;
var knownSystems = Enum.GetValues(typeof(RedumpSystem))
.OfType<RedumpSystem?>()
.Where(s => s != null && !s.IsMarker() && s.GetCategory() != SystemCategory.NONE)
.OrderBy(s => s.LongName() ?? string.Empty);
Console.WriteLine($"{((KnownSystem?)val).ShortName()} - {((KnownSystem?)val).LongName()}");
foreach (var val in knownSystems)
{
Console.WriteLine($"{val.ShortName()} - {val.LongName()}");
}
}

4
MPF.Core/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,381 @@
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Reflection;
#if NET_FRAMEWORK
using IMAPI2;
#endif
using MPF.Core.Data;
using RedumpLib.Data;
namespace MPF.Core.Converters
{
public static class EnumConverter
{
#region Cross-enumeration conversions
/// <summary>
/// Convert drive type to internal version, if possible
/// </summary>
/// <param name="driveType">DriveType value to check</param>
/// <returns>InternalDriveType, if possible, null on error</returns>
public static InternalDriveType? ToInternalDriveType(this DriveType driveType)
{
switch (driveType)
{
case DriveType.CDRom:
return InternalDriveType.Optical;
case DriveType.Fixed:
return InternalDriveType.HardDisk;
case DriveType.Removable:
return InternalDriveType.Removable;
default:
return null;
}
}
#if NET_FRAMEWORK
/// <summary>
/// Convert IMAPI physical media type to a MediaType
/// </summary>
/// <param name="type">IMAPI_MEDIA_PHYSICAL_TYPE value to check</param>
/// <returns>MediaType if possible, null on error</returns>
public static MediaType? IMAPIToMediaType(this IMAPI_MEDIA_PHYSICAL_TYPE type)
{
switch (type)
{
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_UNKNOWN:
return MediaType.NONE;
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDROM:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDR:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDRW:
return MediaType.CDROM;
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDROM:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDRAM:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR_DUALLAYER:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHRW:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR_DUALLAYER:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DISK:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW_DUALLAYER:
return MediaType.DVD;
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDROM:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDR:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDRAM:
return MediaType.HDDVD;
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDROM:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDR:
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDRE:
return MediaType.BluRay;
default:
return null;
}
}
#endif
#endregion
#region Convert to Long Name
/// <summary>
/// Long name method cache
/// </summary>
private static readonly ConcurrentDictionary<Type, MethodInfo> LongNameMethods = new ConcurrentDictionary<Type, MethodInfo>();
/// <summary>
/// Get the string representation of a generic enumerable value
/// </summary>
/// <param name="value">Enum value to convert</param>
/// <returns>String representation of that value if possible, empty string on error</returns>
public static string GetLongName(Enum value)
{
try
{
var sourceType = value?.GetType();
sourceType = Nullable.GetUnderlyingType(sourceType) ?? sourceType;
if (!LongNameMethods.TryGetValue(sourceType, out MethodInfo method))
{
method = typeof(RedumpLib.Data.Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
if (method == null)
method = typeof(EnumConverter).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
LongNameMethods.TryAdd(sourceType, method);
}
if (method != null)
return method.Invoke(null, new[] { value }) as string;
else
return string.Empty;
}
catch
{
// Converter is not implemented for the given type
return string.Empty;
}
}
/// <summary>
/// Get the string representation of the InternalProgram enum values
/// </summary>
/// <param name="prog">InternalProgram value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this InternalProgram? prog)
{
switch (prog)
{
#region Dumping support
case InternalProgram.Aaru:
return "Aaru";
case InternalProgram.DD:
return "dd";
case InternalProgram.DiscImageCreator:
return "DiscImageCreator";
#endregion
#region Verification support only
case InternalProgram.CleanRip:
return "CleanRip";
case InternalProgram.DCDumper:
return "DCDumper";
case InternalProgram.UmdImageCreator:
return "UmdImageCreator";
#endregion
case InternalProgram.NONE:
default:
return "Unknown";
}
}
#endregion
#region Convert From String
/// <summary>
/// Get the InternalProgram enum value for a given string
/// </summary>
/// <param name="internalProgram">String value to convert</param>
/// <returns>InternalProgram represented by the string, if possible</returns>
public static InternalProgram ToInternalProgram(string internalProgram)
{
switch (internalProgram.ToLowerInvariant())
{
// Dumping support
case "aaru":
case "chef":
case "dichef":
case "discimagechef":
return InternalProgram.Aaru;
case "creator":
case "dic":
case "dicreator":
case "discimagecreator":
return InternalProgram.DiscImageCreator;
case "dd":
return InternalProgram.DD;
// Verification support only
case "cleanrip":
case "cr":
return InternalProgram.CleanRip;
case "dc":
case "dcd":
case "dcdumper":
return InternalProgram.DCDumper;
case "uic":
case "umd":
case "umdcreator":
case "umdimagecreator":
return InternalProgram.UmdImageCreator;
default:
return InternalProgram.NONE;
}
}
/// <summary>
/// Get the MediaType enum value for a given string
/// </summary>
/// <param name="type">String value to convert</param>
/// <returns>MediaType represented by the string, if possible</returns>
public static MediaType ToMediaType(string type)
{
switch (type.ToLowerInvariant())
{
#region Punched Media
case "aperture":
case "aperturecard":
case "aperture card":
return MediaType.ApertureCard;
case "jacquardloom":
case "jacquardloomcard":
case "jacquard loom card":
return MediaType.JacquardLoomCard;
case "magneticstripe":
case "magneticstripecard":
case "magnetic stripe card":
return MediaType.MagneticStripeCard;
case "opticalphone":
case "opticalphonecard":
case "optical phonecard":
return MediaType.OpticalPhonecard;
case "punchcard":
case "punchedcard":
case "punched card":
return MediaType.PunchedCard;
case "punchtape":
case "punchedtape":
case "punched tape":
return MediaType.PunchedTape;
#endregion
#region Tape
case "openreel":
case "openreeltape":
case "open reel tape":
return MediaType.OpenReel;
case "datacart":
case "datacartridge":
case "datatapecartridge":
case "data tape cartridge":
return MediaType.DataCartridge;
case "cassette":
case "cassettetape":
case "cassette tape":
return MediaType.Cassette;
#endregion
#region Disc / Disc
case "bd":
case "bdrom":
case "bd-rom":
case "bluray":
return MediaType.BluRay;
case "cd":
case "cdrom":
case "cd-rom":
return MediaType.CDROM;
case "dvd":
case "dvd5":
case "dvd-5":
case "dvd9":
case "dvd-9":
case "dvdrom":
case "dvd-rom":
return MediaType.DVD;
case "fd":
case "floppy":
case "floppydisk":
case "floppy disk":
case "floppy diskette":
return MediaType.FloppyDisk;
case "floptical":
return MediaType.Floptical;
case "gd":
case "gdrom":
case "gd-rom":
return MediaType.GDROM;
case "hddvd":
case "hd-dvd":
case "hddvdrom":
case "hd-dvd-rom":
return MediaType.HDDVD;
case "hdd":
case "harddisk":
case "hard disk":
return MediaType.HardDisk;
case "bernoullidisk":
case "iomegabernoullidisk":
case "bernoulli disk":
case "iomega bernoulli disk":
return MediaType.IomegaBernoulliDisk;
case "jaz":
case "iomegajaz":
case "iomega jaz":
return MediaType.IomegaJaz;
case "zip":
case "zipdisk":
case "iomegazip":
case "iomega zip":
return MediaType.IomegaZip;
case "ldrom":
case "lvrom":
case "ld-rom":
case "lv-rom":
case "laserdisc":
case "laservision":
case "ld-rom / lv-rom":
return MediaType.LaserDisc;
case "64dd":
case "n64dd":
case "64dddisk":
case "n64dddisk":
case "64dd disk":
case "n64dd disk":
return MediaType.Nintendo64DD;
case "fds":
case "famicom":
case "nfds":
case "nintendofamicom":
case "famicomdisksystem":
case "famicom disk system":
case "famicom disk system disk":
return MediaType.NintendoFamicomDiskSystem;
case "gc":
case "gamecube":
case "nintendogamecube":
case "nintendo gamecube":
case "gamecube disc":
case "gamecube game disc":
return MediaType.NintendoGameCubeGameDisc;
case "wii":
case "nintendowii":
case "nintendo wii":
case "nintendo wii disc":
case "wii optical disc":
return MediaType.NintendoWiiOpticalDisc;
case "wiiu":
case "nintendowiiu":
case "nintendo wiiu":
case "nintendo wiiu disc":
case "wiiu optical disc":
case "wii u optical disc":
return MediaType.NintendoWiiUOpticalDisc;
case "umd":
return MediaType.UMD;
#endregion
// Unsorted Formats
case "cartridge":
return MediaType.Cartridge;
case "ced":
case "rcaced":
case "rca ced":
case "videodisc":
case "rca videodisc":
return MediaType.CED;
default:
return MediaType.NONE;
}
}
#endregion
}
}

View File

@@ -1,7 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using RedumpLib.Data;
namespace MPF.Data
namespace MPF.Core.Data
{
/// <summary>
/// Constant values for UI
@@ -16,10 +17,10 @@ namespace MPF.Data
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 };
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
@@ -32,16 +33,16 @@ namespace MPF.Data
{
case MediaType.CDROM:
case MediaType.GDROM:
return cd;
return CD;
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
return dvd;
return DVD;
case MediaType.BluRay:
return bd;
return BD;
default:
return unknown;
return Unknown;
}
}
}
@@ -99,11 +100,7 @@ namespace MPF.Data
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

484
MPF.Core/Data/Drive.cs Normal file
View File

@@ -0,0 +1,484 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;
using MPF.Core.Converters;
using MPF.Core.Utilities;
using RedumpLib.Data;
#if NET_FRAMEWORK
using IMAPI2;
#else
using Aaru.CommonTypes.Enums;
using AaruDevices = Aaru.Devices;
#endif
namespace MPF.Core.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 => driveInfo?.DriveFormat;
/// <summary>
/// Windows drive letter
/// </summary>
public char Letter => driveInfo?.Name[0] ?? '\0';
/// <summary>
/// Windows drive path
/// </summary>
public string Name => driveInfo?.Name;
/// <summary>
/// Represents if Windows has marked the drive as active
/// </summary>
public bool MarkedActive => driveInfo?.IsReady ?? false;
/// <summary>
/// Media label as read by Windows
/// </summary>
/// <remarks>The try/catch is needed because Windows will throw an exception if the drive is not marked as active</remarks>
public string VolumeLabel
{
get
{
try
{
return driveInfo?.VolumeLabel;
}
catch
{
return null;
}
}
}
/// <summary>
/// Media label as read by Windows, formatted to avoid odd outputs
/// </summary>
public string FormattedVolumeLabel
{
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>
/// Create a list of active drives matched to their volume labels
/// </summary>
/// <param name="ignoreFixedDrives">Ture to ignore fixed drives from population, false otherwise</param>
/// <returns>Active drives, matched to labels, if possible</returns>
/// <remarks>
/// https://stackoverflow.com/questions/3060796/how-to-distinguish-between-usb-and-floppy-devices?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
/// https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx
/// </remarks>
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
{
var desiredDriveTypes = new List<DriveType>() { DriveType.CDRom };
if (!ignoreFixedDrives)
{
desiredDriveTypes.Add(DriveType.Fixed);
desiredDriveTypes.Add(DriveType.Removable);
}
// Get all supported drive types
var drives = DriveInfo.GetDrives()
.Where(d => desiredDriveTypes.Contains(d.DriveType))
.Select(d => new Drive(EnumConverter.ToInternalDriveType(d.DriveType), d))
.ToList();
// TODO: Management searcher stuff is not supported on other platforms
// Get the floppy drives and set the flag from removable
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_LogicalDisk");
var collection = searcher.Get();
foreach (ManagementObject queryObj in collection)
{
uint? mediaType = (uint?)queryObj["MediaType"];
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
{
char devId = queryObj["DeviceID"].ToString()[0];
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
}
}
}
catch
{
// No-op
}
// Order the drives by drive letter
drives = drives.OrderBy(i => i.Letter).ToList();
return drives;
}
/// <summary>
/// Get the current media type from drive letter
/// </summary>
/// <param name="drive"></param>
/// <returns></returns>
/// <remarks>
/// This may eventually be replaced by Aaru.Devices being able to be about 10x more accurate.
/// This will also end up making it so that IMAPI2 is no longer necessary. Unfortunately, that
/// will only work for .NET Core 3.1 and beyond.
/// </remarks>
public (MediaType?, string) GetMediaType()
{
// Take care of the non-optical stuff first
// TODO: See if any of these can be more granular, like Optical is
if (this.InternalDriveType == Data.InternalDriveType.Floppy)
return (MediaType.FloppyDisk, null);
else if (this.InternalDriveType == Data.InternalDriveType.HardDisk)
return (MediaType.HardDisk, null);
else if (this.InternalDriveType == Data.InternalDriveType.Removable)
return (MediaType.FlashDrive, null);
#if NET_FRAMEWORK
// Get the current drive information
string deviceId = null;
bool loaded = false;
try
{
// Get the device ID first
var searcher = new ManagementObjectSearcher(
"root\\CIMV2",
$"SELECT * FROM Win32_CDROMDrive WHERE Id = '{this.Letter}:\'");
foreach (ManagementObject queryObj in searcher.Get())
{
deviceId = (string)queryObj["DeviceID"];
loaded = (bool)queryObj["MediaLoaded"];
}
// If we got no valid device, we don't care and just return
if (deviceId == null)
return (null, "Device could not be found");
else if (!loaded)
return (null, "Device is not reporting media loaded");
MsftDiscMaster2 discMaster = new MsftDiscMaster2();
deviceId = deviceId.ToLower().Replace('\\', '#').Replace('/', '#');
string id = null;
foreach (var disc in discMaster)
{
if (disc.ToString().Contains(deviceId))
id = disc.ToString();
}
// If we couldn't find the drive, we don't care and return
if (id == null)
return (null, "Device ID could not be found");
// Create the required objects for reading from the drive
MsftDiscRecorder2 recorder = new MsftDiscRecorder2();
recorder.InitializeDiscRecorder(id);
MsftDiscFormat2Data dataWriter = new MsftDiscFormat2Data();
// If the recorder is not supported, just return
if (!dataWriter.IsRecorderSupported(recorder))
return (null, "IMAPI2 recorder not supported");
// Otherwise, set the recorder to get information from
dataWriter.Recorder = recorder;
var media = dataWriter.CurrentPhysicalMediaType;
return (media.IMAPIToMediaType(), null);
}
catch (Exception ex)
{
return (null, ex.Message);
}
#else
try
{
var device = new AaruDevices.Device(this.Name);
if (device.Error)
return (null, "Could not open device");
else if (device.Type != DeviceType.ATAPI && device.Type != DeviceType.SCSI)
return (null, "Device does not support media type detection");
// TODO: In order to get the disc type, Aaru.Core will need to be included as a
// package. Unfortunately, it currently has a conflict with one of the required libraries:
// System.Text.Encoding.CodePages (BOS uses >= 5.0.0, DotNetZip uses >= 4.5.0 && < 5.0.0)
}
catch (Exception ex)
{
return (null, ex.Message);
}
return (null, "Media detection only supported on .NET Framework");
#endif
}
/// <summary>
/// Get the current system from drive
/// </summary>
/// <param name="defaultValue"></param>
/// <returns></returns>
public RedumpSystem? GetRedumpSystem(RedumpSystem? defaultValue)
{
string drivePath = $"{this.Letter}:\\";
// If we can't read the media in that drive, we can't do anything
if (!Directory.Exists(drivePath))
return defaultValue;
// We're going to assume for floppies, HDDs, and removable drives
// TODO: Try to be smarter about this
if (this.InternalDriveType != Data.InternalDriveType.Optical)
return RedumpSystem.IBMPCcompatible;
// Check volume labels first
RedumpSystem? systemFromLabel = GetRedumpSystemFromVolumeLabel();
if (systemFromLabel != null)
return systemFromLabel;
#region Consoles
// Microsoft Xbox 360
try
{
if (Directory.Exists(Path.Combine(drivePath, "$SystemUpdate"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any())
{
return RedumpSystem.MicrosoftXbox360;
}
}
catch { }
// Sega Dreamcast
if (File.Exists(Path.Combine(drivePath, "IP.BIN")))
{
return RedumpSystem.SegaDreamcast;
}
// Sega Mega-CD / Sega-CD
if (File.Exists(Path.Combine(drivePath, "_BOOT", "IP.BIN"))
|| File.Exists(Path.Combine(drivePath, "_BOOT", "SP.BIN"))
|| File.Exists(Path.Combine(drivePath, "_BOOT", "SP_AS.BIN"))
|| File.Exists(Path.Combine(drivePath, "FILESYSTEM.BIN")))
{
return RedumpSystem.SegaMegaCDSegaCD;
}
// Sega Saturn
try
{
byte[] sector = ReadSector(0);
if (sector != null)
{
if (sector.StartsWith(Interface.SaturnSectorZeroStart))
return RedumpSystem.SegaSaturn;
}
}
catch { }
// Sony PlayStation and Sony PlayStation 2
string psxExePath = Path.Combine(drivePath, "PSX.EXE");
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
if (File.Exists(systemCnfPath))
{
// Check for either BOOT or BOOT2
var systemCnf = new IniFile(systemCnfPath);
if (systemCnf.ContainsKey("BOOT"))
return RedumpSystem.SonyPlayStation;
else if (systemCnf.ContainsKey("BOOT2"))
return RedumpSystem.SonyPlayStation2;
}
else if (File.Exists(psxExePath))
{
return RedumpSystem.SonyPlayStation;
}
// V.Tech V.Flash / V.Smile Pro
if (File.Exists(Path.Combine(drivePath, "0SYSTEM")))
{
return RedumpSystem.VTechVFlashVSmilePro;
}
#endregion
#region Video Formats
// BD-Video
if (Directory.Exists(Path.Combine(drivePath, "BDMV")))
{
// Technically BD-Audio has this as well, but it's hard to split that out right now
return RedumpSystem.BDVideo;
}
// DVD-Audio and DVD-Video
try
{
if (Directory.Exists(Path.Combine(drivePath, "AUDIO_TS"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "AUDIO_TS")).Any())
{
return RedumpSystem.DVDAudio;
}
else if (Directory.Exists(Path.Combine(drivePath, "VIDEO_TS"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VIDEO_TS")).Any())
{
return RedumpSystem.DVDVideo;
}
}
catch { }
// HD-DVD-Video
try
{
if (Directory.Exists(Path.Combine(drivePath, "HVDVD_TS"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "HVDVD_TS")).Any())
{
return RedumpSystem.HDDVDVideo;
}
}
catch { }
// VCD
try
{
if (Directory.Exists(Path.Combine(drivePath, "VCD"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VCD")).Any())
{
return RedumpSystem.VideoCD;
}
}
catch { }
#endregion
// Default return
return defaultValue;
}
/// <summary>
/// Get the current system from the drive volume label
/// </summary>
/// <returns>The system based on volume label, null if none detected</returns>
public RedumpSystem? GetRedumpSystemFromVolumeLabel()
{
// If the volume label is empty, we can't do anything
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
return null;
// Audio CD
if (this.VolumeLabel.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.AudioCD;
// Microsoft Xbox
if (this.VolumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox;
else if (this.VolumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox;
// Microsoft Xbox 360
if (this.VolumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox360;
else if (this.VolumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox360;
// Microsoft Xbox 360 - Too overly broad even if a lot of discs use this
//if (this.VolumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase))
// return RedumpSystem.MicrosoftXbox360; // Also for Xbox One?
//if (this.VolumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
// return RedumpSystem.MicrosoftXbox360;
// Sony PlayStation 3
if (this.VolumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SonyPlayStation3;
// Sony PlayStation 4
if (this.VolumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SonyPlayStation4;
// Sony PlayStation 5
if (this.VolumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SonyPlayStation5;
return null;
}
/// <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();
}
}
}
}

View File

@@ -0,0 +1,31 @@
namespace MPF.Core.Data
{
/// <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,
}
}

View File

@@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace MPF.Data
namespace MPF.Core.Data
{
public class IniFile : IDictionary<string, string>
{

View File

@@ -1,14 +1,14 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using MPF.Utilities;
using MPF.Core.Converters;
using RedumpLib.Data;
namespace MPF.Data
namespace MPF.Core.Data
{
public class Options : IDictionary<string, string>, ICloneable
{
private Dictionary<string, string> _settings;
private readonly Dictionary<string, string> _settings;
#region Internal Program
@@ -47,7 +47,7 @@ namespace MPF.Data
get
{
string valueString = GetStringSetting(_settings, "InternalProgram", InternalProgram.DiscImageCreator.ToString());
var valueEnum = Converters.ToInternalProgram(valueString);
var valueEnum = EnumConverter.ToInternalProgram(valueString);
return valueEnum == InternalProgram.NONE ? InternalProgram.DiscImageCreator : valueEnum;
}
set
@@ -69,6 +69,15 @@ namespace MPF.Data
set { _settings["EnableDarkMode"] = value.ToString(); }
}
/// <summary>
/// Check for updates on startup
/// </summary>
public bool CheckForUpdatesOnStartup
{
get { return GetBooleanSetting(_settings, "CheckForUpdatesOnStartup", true); }
set { _settings["CheckForUpdatesOnStartup"] = value.ToString(); }
}
/// <summary>
/// Default output path for dumps
/// </summary>
@@ -81,20 +90,30 @@ namespace MPF.Data
/// <summary>
/// Default system if none can be detected
/// </summary>
public KnownSystem DefaultSystem
public RedumpSystem? DefaultSystem
{
get
{
string valueString = GetStringSetting(_settings, "DefaultSystem", KnownSystem.NONE.ToString());
var valueEnum = Converters.ToKnownSystem(valueString);
return valueEnum ?? KnownSystem.NONE;
string valueString = GetStringSetting(_settings, "DefaultSystem", null);
var valueEnum = Extensions.ToRedumpSystem(valueString);
return valueEnum;
}
set
{
_settings["DefaultSystem"] = Converters.GetLongName(value);
_settings["DefaultSystem"] = value.LongName();
}
}
/// <summary>
/// Default output path for dumps
/// </summary>
/// <remarks>This is a hidden setting</remarks>
public bool ShowDebugViewMenuItem
{
get { return GetBooleanSetting(_settings, "ShowDebugViewMenuItem", false); }
set { _settings["ShowDebugViewMenuItem"] = value.ToString(); }
}
#endregion
#region Dumping Speeds
@@ -260,6 +279,24 @@ namespace MPF.Data
set { _settings["PromptForDiscInformation"] = value.ToString(); }
}
/// <summary>
/// Enable tabs in all input fields
/// </summary>
public bool EnableTabsInInputFields
{
get { return GetBooleanSetting(_settings, "EnableTabsInInputFields", false); }
set { _settings["EnableTabsInInputFields"] = value.ToString(); }
}
/// <summary>
/// Show disc eject reminder before the disc information window is shown
/// </summary>
public bool ShowDiscEjectReminder
{
get { return GetBooleanSetting(_settings, "ShowDiscEjectReminder", true); }
set { _settings["ShowDiscEjectReminder"] = value.ToString(); }
}
/// <summary>
/// Eject the disc after dumping
/// </summary>

View File

@@ -3,7 +3,7 @@ using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace MPF.Data
namespace MPF.Core.Data
{
public class ProcessingQueue<T> : IDisposable
{
@@ -33,10 +33,7 @@ namespace MPF.Data
/// <summary>
/// Dispose the current instance
/// </summary>
public void Dispose()
{
this.TokenSource.Cancel();
}
public void Dispose() => this.TokenSource.Cancel();
/// <summary>
/// Enqueue a new item for processing

View File

@@ -1,4 +1,4 @@
namespace MPF.Data
namespace MPF.Core.Data
{
/// <summary>
/// Generic success/failure result object, with optional message

423
MPF.Core/Data/XgdInfo.cs Normal file
View File

@@ -0,0 +1,423 @@
using RedumpLib.Data;
namespace MPF.Core.Data
{
/// <summary>
/// Contains information specific to an XGD disc
/// </summary>
/// <remarks>
/// XGD1 XMID Format Information:
///
/// AABBBCCD
/// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details)
/// - BBB => Game ID
/// - CC => Version number
/// - D => Region identifier (see GetRegion for details)
///
/// XGD2/3 XeMID Format Information:
///
/// AABCCCDDEFFGHH(IIIIIIII)
/// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details)
/// - B => Platform identifier; 2 indicates Xbox 360.
/// - CCC => Game ID
/// - DD => SKU number (unique per SKU of a title)
/// - E => Region identifier (see GetRegion for details)
/// - FF => Base version; usually starts at 01 (can be 1 or 2 characters)
/// - G => Media type identifier (see GetMediaSubtype for details)
/// - HH => Disc number stored in [disc number][total discs] format
/// - IIIIIIII => 8-hex-digit certification submission identifier; usually on test discs only
/// </remarks>
public class XgdInfo
{
#region Fields
/// <summary>
/// Indicates whether the information in this object is fully instantiated or not
/// </summary>
public bool Initialized { get; private set; }
/// <summary>
/// Raw XMID/XeMID string that all other information is derived from
/// </summary>
public string XMID { get; private set; }
/// <summary>
/// 2-character publisher identifier
/// </summary>
public string PublisherIdentifier { get; private set; }
/// <summary>
/// Platform disc is made for, 2 indicates Xbox 360
/// </summary>
public char? PlatformIdentifier { get; private set; }
/// <summary>
/// Game ID
/// </summary>
public string GameID { get; private set; }
/// <summary>
/// For XGD1: Internal version number
/// For XGD2/3: Title-specific SKU
/// </summary>
public string SKU { get; private set; }
/// <summary>
/// Region identifier character
/// </summary>
public char RegionIdentifier { get; private set; }
/// <summary>
/// Base version of executables, usually starts at 01
/// </summary>
/// <remarks>
/// TODO: Check if this is always 2 characters for XGD2/3
/// </remarks>
public string BaseVersion { get; private set; }
/// <summary>
/// Media subtype identifier
/// </summary>
public char MediaSubtypeIdentifier { get; private set; }
/// <summary>
/// Disc number stored in [disc number][total discs] format
/// </summary>
public string DiscNumberIdentifier { get; private set; }
/// <summary>
/// 8-hex-digit certification submission identifier; usually on test discs only
/// </summary>
public string CertificationSubmissionIdentifier { get; private set; }
#endregion
#region Auto-Generated Information
/// <summary>
/// Human-readable name derived from the publisher identifier
/// </summary>
public string PublisherName => GetPublisher(this.PublisherIdentifier);
/// <summary>
/// Internally represented region
/// </summary>
public Region? InternalRegion => GetRegion(this.RegionIdentifier);
/// <summary>
/// Human-readable subtype derived from the media identifier
/// </summary>
public string MediaSubtype => GetMediaSubtype(this.MediaSubtypeIdentifier);
#endregion
/// <summary>
/// Populate a set of XGD information from a Master ID (XMID/XeMID) string
/// </summary>
/// <param name="xmid">XMID/XeMID string representing the DMI information</param>
/// <param name="validate">True if value validation should be performed, false otherwise</param>
public XgdInfo(string xmid, bool validate = false)
{
this.Initialized = false;
if (string.IsNullOrWhiteSpace(xmid))
return;
this.XMID = xmid.TrimEnd('\0');
if (string.IsNullOrWhiteSpace(this.XMID))
return;
// XGD1 information is 8 characters
if (this.XMID.Length == 8)
this.Initialized = ParseXGD1XMID(this.XMID, validate);
// XGD2/3 information is semi-variable length
else if (this.XMID.Length == 13 || this.XMID.Length == 14 || this.XMID.Length == 21 || this.XMID.Length == 22)
this.Initialized = ParseXGD23XeMID(this.XMID, validate);
}
/// <summary>
/// Get the human-readable serial string
/// </summary>
/// <returns>Formatted serial string, null on error</returns>
public string GetSerial()
{
if (!this.Initialized)
return null;
try
{
// XGD1 doesn't use PlatformIdentifier
if (this.PlatformIdentifier == null)
return $"{this.PublisherIdentifier}-{this.GameID}";
// XGD2/3 uses a specific identifier
else if (this.PlatformIdentifier == '2')
return $"{this.PublisherIdentifier}-{this.PlatformIdentifier}{this.GameID}";
return null;
}
catch
{
return null;
}
}
/// <summary>
/// Get the human-readable version string
/// </summary>
/// <returns>Formatted version string, null on error</returns>
/// <remarks>This may differ for XGD2/3 in the future</remarks>
public string GetVersion()
{
if (!this.Initialized)
return null;
try
{
// XGD1 doesn't use PlatformIdentifier
if (this.PlatformIdentifier == null)
return $"1.{this.SKU}";
// XGD2/3 uses a specific identifier
else if (this.PlatformIdentifier == '2')
return $"1.{this.SKU}";
return null;
}
catch
{
return null;
}
}
/// <summary>
/// Parse an XGD1 XMID string
/// </summary>
/// <param name="xmid">XMID string to attempt to parse</param>
/// <param name="validate">True if value validation should be performed, false otherwise</param>
/// <returns>True if the XMID could be parsed, false otherwise</returns>
private bool ParseXGD1XMID(string xmid, bool validate)
{
if (xmid == null || xmid.Length != 8)
return false;
this.PublisherIdentifier = xmid.Substring(0, 2);
if (validate && string.IsNullOrEmpty(this.PublisherName))
return false;
this.GameID = xmid.Substring(2, 3);
this.SKU = xmid.Substring(5, 2);
this.RegionIdentifier = xmid[7];
if (validate && this.InternalRegion == null)
return false;
return true;
}
/// <summary>
/// Parse an XGD2/3 XeMID string
/// </summary>
/// <param name="xemid">XeMID string to attempt to parse</param>
/// <param name="validate">True if value validation should be performed, false otherwise</param>
/// <returns>True if the XeMID could be parsed, false otherwise</returns>
private bool ParseXGD23XeMID(string xemid, bool validate)
{
if (xemid == null
|| (xemid.Length != 13 && xemid.Length != 14
&& xemid.Length != 21 && xemid.Length != 22))
return false;
this.PublisherIdentifier = xemid.Substring(0, 2);
if (validate && string.IsNullOrEmpty(this.PublisherName))
return false;
this.PlatformIdentifier = xemid[2];
if (validate && this.PlatformIdentifier != '2')
return false;
this.GameID = xemid.Substring(3, 3);
this.SKU = xemid.Substring(6, 2);
this.RegionIdentifier = xemid[8];
if (validate && this.InternalRegion == null)
return false;
if (xemid.Length == 13 || xemid.Length == 21)
{
this.BaseVersion = xemid.Substring(9, 1);
this.MediaSubtypeIdentifier = xemid[10];
if (validate && string.IsNullOrEmpty(this.MediaSubtype))
return false;
this.DiscNumberIdentifier = xemid.Substring(11, 2);
}
else if (xemid.Length == 14 || xemid.Length == 22)
{
this.BaseVersion = xemid.Substring(9, 2);
this.MediaSubtypeIdentifier = xemid[11];
if (validate && string.IsNullOrEmpty(this.MediaSubtype))
return false;
this.DiscNumberIdentifier = xemid.Substring(12, 2);
}
if (xemid.Length == 21)
this.CertificationSubmissionIdentifier = xemid.Substring(13);
else if (xemid.Length == 22)
this.CertificationSubmissionIdentifier = xemid.Substring(14);
return true;
}
#region Helpers
/// <summary>
/// Determine the XGD type based on the XGD2/3 media type identifier character
/// </summary>
/// <param name="mediaTypeIdentifier">Character denoting the media type</param>
/// <returns>Media subtype as a string, if possible</returns>
private static string GetMediaSubtype(char mediaTypeIdentifier)
{
switch (mediaTypeIdentifier)
{
case 'F': return "XGD3";
case 'X': return "XGD2";
case 'Z': return "Games on Demand / Marketplace Demo";
default: return null;
}
}
/// <summary>
/// Get the full name of the publisher from the 2-character identifier
/// </summary>
/// <param name="publisherIdentifier">Case-sensitive 2-character identifier</param>
/// <returns>Publisher name, if possible</returns>
/// <see cref="https://xboxdevwiki.net/Xbe#Title_ID"/>
private static string GetPublisher(string publisherIdentifier)
{
switch (publisherIdentifier)
{
case "AC": return "Acclaim Entertainment";
case "AH": return "ARUSH Entertainment";
case "AQ": return "Aqua System";
case "AS": return "ASK";
case "AT": return "Atlus";
case "AV": return "Activision";
case "AY": return "Aspyr Media";
case "BA": return "Bandai";
case "BL": return "Black Box";
case "BM": return "BAM! Entertainment";
case "BR": return "Broccoli Co.";
case "BS": return "Bethesda Softworks";
case "BU": return "Bunkasha Co.";
case "BV": return "Buena Vista Games";
case "BW": return "BBC Multimedia";
case "BZ": return "Blizzard";
case "CC": return "Capcom";
case "CK": return "Kemco Corporation"; // TODO: Confirm
case "CM": return "Codemasters";
case "CV": return "Crave Entertainment";
case "DC": return "DreamCatcher Interactive";
case "DX": return "Davilex";
case "EA": return "Electronic Arts (EA)";
case "EC": return "Encore inc";
case "EL": return "Enlight Software";
case "EM": return "Empire Interactive";
case "ES": return "Eidos Interactive";
case "FI": return "Fox Interactive";
case "FS": return "From Software";
case "GE": return "Genki Co.";
case "GV": return "Groove Games";
case "HE": return "Tru Blu (Entertainment division of Home Entertainment Suppliers)";
case "HP": return "Hip games";
case "HU": return "Hudson Soft";
case "HW": return "Highwaystar";
case "IA": return "Mad Catz Interactive";
case "IF": return "Idea Factory";
case "IG": return "Infogrames";
case "IL": return "Interlex Corporation";
case "IM": return "Imagine Media";
case "IO": return "Ignition Entertainment";
case "IP": return "Interplay Entertainment";
case "IX": return "InXile Entertainment"; // TODO: Confirm
case "JA": return "Jaleco";
case "JW": return "JoWooD";
case "KB": return "Kemco"; // TODO: Confirm
case "KI": return "Kids Station Inc."; // TODO: Confirm
case "KN": return "Konami";
case "KO": return "KOEI";
case "KU": return "Kobi and / or GAE (formerly Global A Entertainment)"; // TODO: Confirm
case "LA": return "LucasArts";
case "LS": return "Black Bean Games (publishing arm of Leader S.p.A.)";
case "MD": return "Metro3D";
case "ME": return "Medix";
case "MI": return "Microïds";
case "MJ": return "Majesco Entertainment";
case "MM": return "Myelin Media";
case "MP": return "MediaQuest"; // TODO: Confirm
case "MS": return "Microsoft Game Studios";
case "MW": return "Midway Games";
case "MX": return "Empire Interactive"; // TODO: Confirm
case "NK": return "NewKidCo";
case "NL": return "NovaLogic";
case "NM": return "Namco";
case "OX": return "Oxygen Interactive";
case "PC": return "Playlogic Entertainment";
case "PL": return "Phantagram Co., Ltd.";
case "RA": return "Rage";
case "SA": return "Sammy";
case "SC": return "SCi Games";
case "SE": return "SEGA";
case "SN": return "SNK";
case "SS": return "Simon & Schuster";
case "SU": return "Success Corporation";
case "SW": return "Swing! Deutschland";
case "TA": return "Takara";
case "TC": return "Tecmo";
case "TD": return "The 3DO Company (or just 3DO)";
case "TK": return "Takuyo";
case "TM": return "TDK Mediactive";
case "TQ": return "THQ";
case "TS": return "Titus Interactive";
case "TT": return "Take-Two Interactive Software";
case "US": return "Ubisoft";
case "VC": return "Victor Interactive Software";
case "VN": return "Vivendi Universal (just took Interplays publishing rights)"; // TODO: Confirm
case "VU": return "Vivendi Universal Games";
case "VV": return "Vivendi Universal Games"; // TODO: Confirm
case "WE": return "Wanadoo Edition";
case "WR": return "Warner Bros. Interactive Entertainment"; // TODO: Confirm
case "XI": return "XPEC Entertainment and Idea Factory";
case "XK": return "Xbox kiosk disk?"; // TODO: Confirm
case "XL": return "Xbox special bundled or live demo disk?"; // TODO: Confirm
case "XM": return "Evolved Games"; // TODO: Confirm
case "XP": return "XPEC Entertainment";
case "XR": return "Panorama";
case "YB": return "YBM Sisa (South-Korea)";
case "ZD": return "Zushi Games (formerly Zoo Digital Publishing)";
default: return null;
}
}
/// <summary>
/// Determine the region based on the XGD serial character
/// </summary>
/// <param name="region">Character denoting the region</param>
/// <returns>Region, if possible</returns>
private static Region? GetRegion(char region)
{
switch (region)
{
case 'W': return Region.World;
case 'A': return Region.UnitedStatesOfAmerica;
case 'J': return Region.JapanAsia;
case 'E': return Region.Europe;
case 'K': return Region.USAJapan;
case 'L': return Region.USAEurope;
case 'H': return Region.JapanEurope;
default: return null;
}
}
#endregion
}
}

View File

@@ -1,11 +1,27 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using MPF.Data;
using OptimizedCRC;
namespace MPF.Hashing
namespace MPF.Core.Hashing
{
/// <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>
/// Async hashing class wraper
/// </summary>
@@ -28,7 +44,7 @@ namespace MPF.Hashing
switch (HashType)
{
case Hash.CRC:
_hasher = new OptimizedCRC.OptimizedCRC();
_hasher = new OptimizedCRC();
break;
case Hash.MD5:
@@ -66,7 +82,7 @@ namespace MPF.Hashing
switch (HashType)
{
case Hash.CRC:
(_hasher as OptimizedCRC.OptimizedCRC).Update(buffer, 0, size);
(_hasher as OptimizedCRC).Update(buffer, 0, size);
break;
case Hash.MD5:
@@ -88,7 +104,7 @@ namespace MPF.Hashing
switch (HashType)
{
case Hash.CRC:
(_hasher as OptimizedCRC.OptimizedCRC).Update(emptyBuffer, 0, 0);
(_hasher as OptimizedCRC).Update(emptyBuffer, 0, 0);
break;
case Hash.MD5:
@@ -109,7 +125,7 @@ namespace MPF.Hashing
switch (HashType)
{
case Hash.CRC:
return BitConverter.GetBytes((_hasher as OptimizedCRC.OptimizedCRC).Value).Reverse().ToArray();
return BitConverter.GetBytes((_hasher as OptimizedCRC).Value).Reverse().ToArray();
case Hash.MD5:
case Hash.SHA1:

View File

@@ -24,7 +24,8 @@
using System;
namespace OptimizedCRC
//namespace OptimizedCRC
namespace MPF.Core.Hashing
{
internal class OptimizedCRC : IDisposable
{

View File

@@ -2,7 +2,8 @@
using System.IO;
using System.Threading;
namespace Compress.ThreadReaders
//namespace Compress.ThreadReaders
namespace MPF.Core.Hashing
{
public class ThreadLoadBuffer : IDisposable
{

65
MPF.Core/MPF.Core.csproj Normal file
View File

@@ -0,0 +1,65 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.3</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)'=='net48'">
<DefineConstants>NET_FRAMEWORK</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
<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>
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.Management" Version="6.0.0-rc.1.21451.13" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'!='net48'">
<PackageReference Include="Aaru.Devices" Version="5.3.0-rc2" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Management" />
</ItemGroup>
</Project>

View File

@@ -1,12 +1,12 @@
using System;
using System.IO;
namespace MPF.Utilities
namespace MPF.Core.Utilities
{
/// <summary>
/// Big endian reading overloads for BinaryReader
/// </summary>
internal static class BinaryReaderExtensions
public static class BinaryReaderExtensions
{
/// <summary>
/// Reads the specified number of bytes from the stream, starting from a specified point in the byte array.

View File

@@ -0,0 +1,144 @@
using RedumpLib.Data;
namespace MPF.Core.Utilities
{
public static class EnumExtensions
{
/// <summary>
/// Determine if a system is okay if it's not detected by Windows
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
/// <returns>True if Windows show see a disc when dumping, false otherwise</returns>
public static bool DetectedByWindows(this RedumpSystem? system)
{
switch (system)
{
case RedumpSystem.AmericanLaserGames3DO:
case RedumpSystem.AppleMacintosh:
case RedumpSystem.Atari3DO:
case RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem:
case RedumpSystem.NewJatreCDi:
case RedumpSystem.NintendoGameCube:
case RedumpSystem.NintendoWii:
case RedumpSystem.NintendoWiiU:
case RedumpSystem.PhilipsCDi:
case RedumpSystem.PhilipsCDiDigitalVideo:
case RedumpSystem.Panasonic3DOInteractiveMultiplayer:
case RedumpSystem.PanasonicM2:
case RedumpSystem.PioneerLaserActive:
case RedumpSystem.SuperAudioCD:
return false;
default:
return true;
}
}
/// <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 has reversed ringcodes
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
/// <returns>True if the system has reversed ringcodes, false otherwise</returns>
public static bool HasReversedRingcodes(this RedumpSystem? system)
{
switch (system)
{
case RedumpSystem.SonyPlayStation2:
case RedumpSystem.SonyPlayStation3:
case RedumpSystem.SonyPlayStation4:
//case RedumpSystem.SonyPlayStation5:
return true;
default:
return false;
}
}
/// <summary>
/// Determine if a system is considered audio-only
/// </summary>
/// <param name="system">RedumpSystem 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 RedumpSystem? system)
{
switch (system)
{
case RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem:
case RedumpSystem.AudioCD:
case RedumpSystem.DVDAudio:
case RedumpSystem.HasbroVideoNow:
case RedumpSystem.HasbroVideoNowColor:
case RedumpSystem.HasbroVideoNowJr:
case RedumpSystem.HasbroVideoNowXP:
case RedumpSystem.PhilipsCDi:
case RedumpSystem.SuperAudioCD:
return true;
default:
return false;
}
}
/// <summary>
/// Determine if a system is a marker value
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
/// <returns>True if the system is a marker value, false otherwise</returns>
public static bool IsMarker(this RedumpSystem? system)
{
switch (system)
{
case RedumpSystem.MarkerArcadeEnd:
case RedumpSystem.MarkerComputerEnd:
case RedumpSystem.MarkerDiscBasedConsoleEnd:
// case RedumpSystem.MarkerOtherConsoleEnd:
case RedumpSystem.MarkerOtherEnd:
return true;
default:
return false;
}
}
/// <summary>
/// Determine if a system is considered XGD
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
/// <returns>True if the system is XGD, false otherwise</returns>
public static bool IsXGD(this RedumpSystem? system)
{
switch (system)
{
case RedumpSystem.MicrosoftXbox:
case RedumpSystem.MicrosoftXbox360:
case RedumpSystem.MicrosoftXboxOne:
case RedumpSystem.MicrosoftXboxSeriesXS:
return true;
default:
return false;
}
}
}
}

View File

@@ -1,13 +1,12 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MPF.Utilities
namespace MPF.Core.Utilities
{
internal static class Logging
public static class Logging
{
/// <summary>
/// Process a chunk of text and send it to a handler
@@ -38,15 +37,15 @@ namespace MPF.Utilities
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'))
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"))
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'))
else if (line.Contains("\r"))
ProcessCarriageReturns(sb, line, baseClass, handler);
}
}
@@ -71,7 +70,7 @@ namespace MPF.Utilities
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'))
if (split[i].Contains("\r"))
{
ProcessCarriageReturns(sb, split[i], baseClass, handler);
continue;

View File

@@ -1,8 +1,11 @@
using System;
using System.Net;
using System.Reflection;
using MPF.Redump;
using MPF.Core.Data;
using Newtonsoft.Json.Linq;
using RedumpLib.Data;
namespace MPF.Utilities
namespace MPF.Core.Utilities
{
public static class Tools
{
@@ -70,6 +73,54 @@ namespace MPF.Utilities
#endregion
#region Support
/// <summary>
/// Verify that, given a system and a media type, they are correct
/// </summary>
public static Result GetSupportStatus(RedumpSystem? system, MediaType? type)
{
// No system chosen, update status
if (system == null)
return Result.Failure("Please select a valid system");
// If we're on an unsupported type, update the status accordingly
switch (type)
{
// Fully supported types
case MediaType.BluRay:
case MediaType.CDROM:
case MediaType.DVD:
case MediaType.FloppyDisk:
case MediaType.HardDisk:
case MediaType.CompactFlash:
case MediaType.SDCard:
case MediaType.FlashDrive:
case MediaType.HDDVD:
return Result.Success($"{type.LongName()} ready to dump");
// Partially supported types
case MediaType.GDROM:
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
return Result.Success($"{type.LongName()} partially supported for dumping");
// Special case for other supported tools
case MediaType.UMD:
return Result.Failure($"{type.LongName()} supported for submission info parsing");
// Specifically unknown type
case MediaType.NONE:
return Result.Failure($"Please select a valid media type");
// Undumpable but recognized types
default:
return Result.Failure($"{type.LongName()} media are not supported for dumping");
}
}
#endregion
#region Versioning
/// <summary>
@@ -82,14 +133,14 @@ namespace MPF.Utilities
/// </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())
try
{
(string tag, string url) = client.GetRemoteVersionAndUrl();
// 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
(string tag, string url) = GetRemoteVersionAndUrl();
bool different = version != tag;
string message = $"Local version: {version}"
@@ -100,6 +151,10 @@ namespace MPF.Utilities
return (different, message, url);
}
catch (Exception ex)
{
return (false, ex.ToString(), null);
}
}
/// <summary>
@@ -111,6 +166,26 @@ namespace MPF.Utilities
return assemblyVersion.InformationalVersion;
}
/// <summary>
/// Get the latest version of MPF from GitHub and the release URL
/// </summary>
private static (string tag, string url) GetRemoteVersionAndUrl()
{
using (WebClient wc = new WebClient())
{
wc.Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0";
// TODO: Figure out a better way than having this hardcoded...
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
string latestReleaseJsonString = wc.DownloadString(url);
var latestReleaseJson = JObject.Parse(latestReleaseJsonString);
string latestTag = latestReleaseJson["tag_name"].ToString();
string releaseUrl = latestReleaseJson["html_url"].ToString();
return (latestTag, releaseUrl);
}
}
#endregion
}
}

View File

@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.3</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>
<ItemGroup>
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -1,106 +0,0 @@
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

@@ -1,777 +0,0 @@
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
}

View File

@@ -0,0 +1,535 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;
using BurnOutSharp;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.Modules;
using RedumpLib.Data;
namespace MPF.Library
{
/// <summary>
/// Represents the state of all settings to be used during dumping
/// </summary>
public class DumpEnvironment
{
#region Output paths
/// <summary>
/// Base output directory to write files to
/// </summary>
public string OutputDirectory { get; private set; }
/// <summary>
/// Base output filename for output
/// </summary>
public string OutputFilename { get; private set; }
#endregion
#region UI information
/// <summary>
/// Drive object representing the current drive
/// </summary>
public Drive Drive { get; private set; }
/// <summary>
/// Currently selected system
/// </summary>
public RedumpSystem? System { get; private set; }
/// <summary>
/// Currently selected media type
/// </summary>
public MediaType? Type { get; private set; }
/// <summary>
/// Options object representing user-defined options
/// </summary>
public Options Options { get; private set; }
/// <summary>
/// Parameters object representing what to send to the internal program
/// </summary>
public BaseParameters Parameters { get; private set; }
#endregion
#region Event Handlers
/// <summary>
/// Generic way of reporting a message
/// </summary>
public EventHandler<string> ReportStatus;
/// <summary>
/// Queue of items that need to be logged
/// </summary>
private ProcessingQueue<string> outputQueue;
/// <summary>
/// Event handler for data returned from a process
/// </summary>
private void OutputToLog(object proc, string args) => outputQueue.Enqueue(args);
/// <summary>
/// Process the outputs in the queue
/// </summary>
private void ProcessOutputs(string nextOutput) => ReportStatus.Invoke(this, nextOutput);
#endregion
/// <summary>
/// Constructor for a full DumpEnvironment object from user information
/// </summary>
/// <param name="options"></param>
/// <param name="outputDirectory"></param>
/// <param name="outputFilename"></param>
/// <param name="drive"></param>
/// <param name="system"></param>
/// <param name="type"></param>
/// <param name="parameters"></param>
public DumpEnvironment(Options options,
string outputDirectory,
string outputFilename,
Drive drive,
RedumpSystem? system,
MediaType? type,
string parameters)
{
// Set options object
this.Options = options;
// Output paths
(this.OutputDirectory, this.OutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename);
// UI information
this.Drive = drive;
this.System = system ?? options.DefaultSystem;
this.Type = type ?? MediaType.NONE;
// Dumping program
SetParameters(parameters);
}
#region Public Functionality
/// <summary>
/// Adjust output paths if we're using DiscImageCreator
/// </summary>
public void AdjustPathsForDiscImageCreator()
{
// Only DiscImageCreator has issues with paths
if (this.Parameters.InternalProgram != InternalProgram.DiscImageCreator)
return;
// Replace all instances in the output directory
this.OutputDirectory = this.OutputDirectory.Replace('.', '_');
// Currently, only periods in directories matter
// Leave the following code commented in case filename handling breaks again
// Replace all instances in the output filename, except the extension
//string tempFilename = Path.GetFileNameWithoutExtension(this.OutputFilename)
// .Replace('.', '_');
//string tempExtension = Path.GetExtension(this.OutputFilename)?.TrimStart('.');
//this.OutputFilename = $"{tempFilename}.{tempExtension}";
}
/// <summary>
/// Set the parameters object based on the internal program and parameters string
/// </summary>
/// <param name="parameters">String representation of the parameters</param>
public void SetParameters(string parameters)
{
switch (Options.InternalProgram)
{
// Dumping support
case InternalProgram.Aaru:
this.Parameters = new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath };
break;
case InternalProgram.DD:
this.Parameters = new Modules.DD.Parameters(parameters) { ExecutablePath = Options.DDPath };
break;
case InternalProgram.DiscImageCreator:
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
break;
// Verification support only
case InternalProgram.CleanRip:
this.Parameters = new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null };
break;
case InternalProgram.DCDumper:
this.Parameters = null; // TODO: Create correct parameter type when supported
break;
case InternalProgram.UmdImageCreator:
this.Parameters = new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null };
break;
// This should never happen, but it needs a fallback
default:
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
break;
}
// Set system and type
this.Parameters.System = this.System;
this.Parameters.Type = this.Type;
}
/// <summary>
/// Get the full parameter string for either DiscImageCreator or Aaru
/// </summary>
/// <param name="driveSpeed">Nullable int representing the drive speed</param>
/// <returns>String representing the params, null on error</returns>
public string GetFullParameters(int? driveSpeed)
{
// Populate with the correct params for inputs (if we're not on the default option)
if (System != null && Type != MediaType.NONE)
{
// If drive letter is invalid, skip this
if (Drive == null)
return null;
// Set the proper parameters
string filename = OutputDirectory + Path.DirectorySeparatorChar + OutputFilename;
switch (Options.InternalProgram)
{
case InternalProgram.Aaru:
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
break;
case InternalProgram.DD:
Parameters = new Modules.DD.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
break;
case InternalProgram.DiscImageCreator:
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
break;
// This should never happen, but it needs a fallback
default:
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
break;
}
// Generate and return the param string
return Parameters.GenerateParameters();
}
return null;
}
#endregion
#region Dumping
/// <summary>
/// Cancel an in-progress dumping process
/// </summary>
public void CancelDumping() => Parameters.KillInternalProgram();
/// <summary>
/// Eject the disc using DiscImageCreator
/// </summary>
public async Task<string> EjectDisc() =>
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
/// <summary>
/// Reset the current drive using DiscImageCreator
/// </summary>
public async Task<string> ResetDrive() =>
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Reset);
/// <summary>
/// Execute the initial invocation of the dumping programs
/// </summary>
/// <param name="progress">Optional result progress callback</param>
public async Task<Result> Run(IProgress<Result> progress = null)
{
// Check that we have the basics for dumping
Result result = IsValidForDump();
if (!result)
return result;
// Invoke output processing, if needed
if (!Options.ToolsInSeparateWindow)
{
outputQueue = new ProcessingQueue<string>(ProcessOutputs);
Parameters.ReportStatus += OutputToLog;
}
// Execute internal tool
progress?.Report(Result.Success($"Executing {Options.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
Directory.CreateDirectory(OutputDirectory);
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
progress?.Report(Result.Success($"{Options.InternalProgram} has finished!"));
// Execute additional tools
progress?.Report(Result.Success("Running any additional tools... see log for output!"));
result = await Task.Run(() => ExecuteAdditionalTools());
progress?.Report(result);
// Remove event handler if needed
if (!Options.ToolsInSeparateWindow)
{
outputQueue.Dispose();
Parameters.ReportStatus -= OutputToLog;
}
return result;
}
/// <summary>
/// Verify that the current environment has a complete dump and create submission info is possible
/// </summary>
/// <param name="resultProgress">Optional result progress callback</param>
/// <param name="protectionProgress">Optional protection progress callback</param>
/// <param name="processUserInfo">Optional user prompt to deal with submission information</param>
/// <returns>Result instance with the outcome</returns>
public async Task<Result> VerifyAndSaveDumpOutput(
IProgress<Result> resultProgress = null,
IProgress<ProtectionProgress> protectionProgress = null,
Func<SubmissionInfo, (bool?, SubmissionInfo)> processUserInfo = null)
{
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
// Check to make sure that the output had all the correct files
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(this.OutputDirectory, this.OutputFilename, this.Parameters, false);
if (!foundFiles)
{
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
return Result.Failure("Error! Please check output directory as dump may be incomplete!");
}
// Extract the information from the output files
resultProgress?.Report(Result.Success("Extracting output information from output files..."));
SubmissionInfo submissionInfo = await InfoTool.ExtractOutputInformation(
this.OutputDirectory,
this.OutputFilename,
this.Drive,
this.System,
this.Type,
this.Options,
this.Parameters,
resultProgress,
protectionProgress);
resultProgress?.Report(Result.Success("Extracting information complete!"));
// Eject the disc automatically if configured to
if (Options.EjectAfterDump == true)
{
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive.Letter}"));
await EjectDisc();
}
// Reset the drive automatically if configured to
if (Options.InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
{
resultProgress?.Report(Result.Success($"Resetting drive {Drive.Letter}"));
await ResetDrive();
}
// Get user-modifiable information if confugured to
if (Options.PromptForDiscInformation && processUserInfo != null)
{
resultProgress?.Report(Result.Success("Waiting for additional disc information..."));
bool? filledInfo;
(filledInfo, submissionInfo) = processUserInfo(submissionInfo);
if (filledInfo == true)
resultProgress?.Report(Result.Success("Additional disc information added!"));
else
resultProgress?.Report(Result.Success("Disc information skipped!"));
}
// Process special fields for site codes
resultProgress?.Report(Result.Success("Processing site codes..."));
InfoTool.ProcessSpecialFields(submissionInfo);
resultProgress?.Report(Result.Success("Processing complete!"));
// Format the information for the text output
resultProgress?.Report(Result.Success("Formatting information..."));
List<string> formattedValues = InfoTool.FormatOutputData(submissionInfo);
resultProgress?.Report(Result.Success("Formatting complete!"));
// Write the text output
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
bool success = InfoTool.WriteOutputData(this.OutputDirectory, formattedValues);
if (success)
resultProgress?.Report(Result.Success("Writing complete!"));
else
resultProgress?.Report(Result.Failure("Writing could not complete!"));
// Write the JSON output, if required
if (Options.OutputSubmissionJSON)
{
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.json.gz..."));
success = InfoTool.WriteOutputData(this.OutputDirectory, submissionInfo);
if (success)
resultProgress?.Report(Result.Success("Writing complete!"));
else
resultProgress?.Report(Result.Failure("Writing could not complete!"));
}
// Conpress the logs, if required
if (Options.CompressLogFiles)
{
resultProgress?.Report(Result.Success("Compressing log files..."));
success = InfoTool.CompressLogFiles(this.OutputDirectory, this.OutputFilename, this.Parameters);
if (success)
resultProgress?.Report(Result.Success("Compression complete!"));
else
resultProgress?.Report(Result.Failure("Compression could not complete!"));
}
resultProgress?.Report(Result.Success("Submission information process complete!"));
return Result.Success();
}
/// <summary>
/// Checks if the parameters are valid
/// </summary>
/// <returns>True if the configuration is valid, false otherwise</returns>
internal bool ParametersValid()
{
bool parametersValid = Parameters.IsValid();
bool floppyValid = !(Drive.InternalDriveType == InternalDriveType.Floppy ^ Type == MediaType.FloppyDisk);
// TODO: HardDisk being in the Removable category is a hack, fix this later
bool removableDiskValid = !((Drive.InternalDriveType == InternalDriveType.Removable || Drive.InternalDriveType == InternalDriveType.HardDisk)
^ (Type == MediaType.CompactFlash || Type == MediaType.SDCard || Type == MediaType.FlashDrive || Type == MediaType.HardDisk));
return parametersValid && floppyValid && removableDiskValid;
}
/// <summary>
/// Run any additional tools given a DumpEnvironment
/// </summary>
/// <returns>Result instance with the outcome</returns>
private Result ExecuteAdditionalTools() => Result.Success("No external tools needed!");
/// <summary>
/// Run internal program async with an input set of parameters
/// </summary>
/// <param name="parameters"></param>
/// <returns>Standard output from commandline window</returns>
private async Task<string> ExecuteInternalProgram(BaseParameters parameters)
{
Process childProcess;
string output = await Task.Run(() =>
{
childProcess = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = parameters.ExecutablePath,
Arguments = parameters.GenerateParameters(),
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
},
};
childProcess.Start();
childProcess.WaitForExit(1000);
// Just in case, we want to push a button 5 times to clear any errors
for (int i = 0; i < 5; i++)
childProcess.StandardInput.WriteLine("Y");
string stdout = childProcess.StandardOutput.ReadToEnd();
childProcess.Dispose();
return stdout;
});
return output;
}
/// <summary>
/// Validate the current environment is ready for a dump
/// </summary>
/// <returns>Result instance with the outcome</returns>
private Result IsValidForDump()
{
// Validate that everything is good
if (!ParametersValid())
return Result.Failure("Error! Current configuration is not supported!");
// Fix the output paths, just in case
(OutputDirectory, OutputFilename) = InfoTool.NormalizeOutputPaths(OutputDirectory, OutputFilename);
// Validate that the output path isn't on the dumping drive
string fullOutputPath = Path.GetFullPath(Path.Combine(OutputDirectory, OutputFilename));
if (fullOutputPath[0] == Drive.Letter)
return Result.Failure($"Error! Cannot output to same drive that is being dumped!");
// Validate that the required program exists
if (!File.Exists(Parameters.ExecutablePath))
return Result.Failure($"Error! {Parameters.ExecutablePath} does not exist!");
// Validate that the dumping drive doesn't contain the executable
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath);
if (fullExecutablePath[0] == Drive.Letter)
return Result.Failure("$Error! Cannot dump same drive that executable resides on!");
// Validate that the current configuration is supported
return Tools.GetSupportStatus(System, Type);
}
/// <summary>
/// Validate that DIscImageCreator is able to be found
/// </summary>
/// <returns>True if DiscImageCreator is found properly, false otherwise</returns>
private bool RequiredProgramsExist()
{
// Validate that the path is configured
if (string.IsNullOrWhiteSpace(Options.DiscImageCreatorPath))
return false;
// Validate that the required program exists
if (!File.Exists(Options.DiscImageCreatorPath))
return false;
return true;
}
/// <summary>
/// Run a standalone DiscImageCreator command
/// </summary>
/// <param name="command">Command string to run</param>
/// <returns>The output of the command on success, null on error</returns>
private async Task<string> RunStandaloneDiscImageCreatorCommand(string command)
{
// Validate that DiscImageCreator is all set
if (!RequiredProgramsExist())
return null;
// Validate we're not trying to eject a non-optical
if (Drive.InternalDriveType != InternalDriveType.Optical)
return null;
CancelDumping();
var parameters = new Modules.DiscImageCreator.Parameters(string.Empty)
{
BaseCommand = command,
DriveLetter = Drive.Letter.ToString(),
ExecutablePath = Options.DiscImageCreatorPath,
};
return await ExecuteInternalProgram(parameters);
}
#endregion
}
}

2034
MPF.Library/InfoTool.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</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>
<Version>2.3</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -23,63 +21,22 @@
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'!='netcoreapp3.1'">
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
<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>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</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">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.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>
@@ -90,8 +47,4 @@
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Management" />
</ItemGroup>
</Project>

304
MPF.Library/Protection.cs Normal file
View File

@@ -0,0 +1,304 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BurnOutSharp;
using BurnOutSharp.External.psxt001z;
using BurnOutSharp.ProtectionType;
using MPF.Core.Data;
namespace MPF.Library
{
public static class Protection
{
/// <summary>
/// Run protection scan on a given dump environment
/// </summary>
/// <param name="path">Path to scan for protection</param>
/// <param name="options">Options object that determines what to scan</param>
/// <param name="progress">Optional progress callback</param>
/// <returns>TCopy protection detected in the envirionment, if any</returns>
public static async Task<(bool, string)> RunProtectionScanOnPath(string path, Options options, IProgress<ProtectionProgress> progress = null)
{
try
{
var found = await Task.Run(() =>
{
var scanner = new Scanner(progress)
{
IncludeDebug = options.IncludeDebugProtectionInformation,
ScanAllFiles = options.ForceScanningForProtection,
ScanArchives = options.ScanArchivesForProtection,
ScanPackers = options.ScanPackersForProtection,
};
return scanner.GetProtections(path);
});
if (found == null || found.Count() == 0)
return (true, "None found");
// Get an ordered list of distinct found protections
var orderedDistinctProtections = found
.Where(kvp => kvp.Value != null && kvp.Value.Any())
.SelectMany(kvp => kvp.Value)
.Distinct()
.OrderBy(p => p);
// Sanitize and join protections for writing
string protections = SanitizeFoundProtections(orderedDistinctProtections);
return (true, protections);
}
catch (Exception ex)
{
return (false, ex.ToString());
}
}
/// <summary>
/// Get the existence of an anti-modchip string from a PlayStation disc, if possible
/// </summary>
/// <param name="path">Path to scan for anti-modchip strings</param>
/// <returns>Anti-modchip existence if possible, false on error</returns>
public static async Task<bool> GetPlayStationAntiModchipDetected(string path)
{
return await Task.Run(() =>
{
try
{
var antiModchip = new PSXAntiModchip();
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
{
try
{
byte[] fileContent = File.ReadAllBytes(file);
string protection = antiModchip.CheckContents(file, fileContent, false, null, null);
if (!string.IsNullOrWhiteSpace(protection))
return true;
}
catch { }
}
}
catch { }
return false;
});
}
/// <summary>
/// Get if LibCrypt data is detected in the subchannel file, if possible
/// </summary>
/// <param name="sub">.sub file location</param>
/// <returns>Status of the LibCrypt data, if possible</returns>
public static bool? GetLibCryptDetected(string sub)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(sub))
return null;
return LibCrypt.CheckSubfile(sub);
}
/// <summary>
/// Sanitize unnecessary protection duplication from output
/// </summary>
/// <param name="foundProtections">Enumerable of found protections</param>
public static string SanitizeFoundProtections(IEnumerable<string> foundProtections)
{
// ActiveMARK
if (foundProtections.Any(p => p == "ActiveMARK 5") && foundProtections.Any(p => p == "ActiveMARK"))
foundProtections = foundProtections.Where(p => p != "ActiveMARK");
// Cactus Data Shield
if (foundProtections.Any(p => Regex.IsMatch(p, @"Cactus Data Shield [0-9]{3} .+")) && foundProtections.Any(p => p == "Cactus Data Shield 200"))
foundProtections = foundProtections.Where(p => p != "Cactus Data Shield 200");
// CD-Check
foundProtections = foundProtections.Where(p => p != "Executable-Based CD Check");
// CD-Cops
if (foundProtections.Any(p => p == "CD-Cops") && foundProtections.Any(p => p.StartsWith("CD-Cops") && p.Length > "CD-Cops".Length))
foundProtections = foundProtections.Where(p => p != "CD-Cops");
// CD-Key / Serial
foundProtections = foundProtections.Where(p => p != "CD-Key / Serial");
// Electronic Arts
if (foundProtections.Any(p => p == "EA CdKey Registration Module") && foundProtections.Any(p => p.StartsWith("EA CdKey Registration Module") && p.Length > "EA CdKey Registration Module".Length))
foundProtections = foundProtections.Where(p => p != "EA CdKey Registration Module");
if (foundProtections.Any(p => p == "EA DRM Protection") && foundProtections.Any(p => p.StartsWith("EA DRM Protection") && p.Length > "EA DRM Protection".Length))
foundProtections = foundProtections.Where(p => p != "EA DRM Protection");
// Games for Windows LIVE
if (foundProtections.Any(p => p == "Games for Windows LIVE") && foundProtections.Any(p => p.StartsWith("Games for Windows LIVE") && !p.Contains("Zero Day Piracy Protection") && p.Length > "Games for Windows LIVE".Length))
foundProtections = foundProtections.Where(p => p != "Games for Windows LIVE");
// Impulse Reactor
if (foundProtections.Any(p => p.StartsWith("Impulse Reactor Core Module")) && foundProtections.Any(p => p == "Impulse Reactor"))
foundProtections = foundProtections.Where(p => p != "Impulse Reactor");
// JoWood X-Prot
if (foundProtections.Any(p => p.StartsWith("JoWood X-Prot")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"JoWood X-Prot [0-9]\.[0-9]\.[0-9]\.[0-9]{2}")))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
.Where(p => p != "JoWood X-Prot v1.0-v1.3")
.Where(p => p != "JoWood X-Prot v1.4+")
.Where(p => p != "JoWood X-Prot v2");
}
else if (foundProtections.Any(p => p == "JoWood X-Prot v2"))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
.Where(p => p != "JoWood X-Prot v1.0-v1.3")
.Where(p => p != "JoWood X-Prot v1.4+");
}
else if (foundProtections.Any(p => p == "JoWood X-Prot v1.4+"))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
.Where(p => p != "JoWood X-Prot v1.0-v1.3");
}
else if (foundProtections.Any(p => p == "JoWood X-Prot v1.0-v1.3"))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot");
}
}
// LaserLok
// TODO: Figure this one out
// Online Registration
foundProtections = foundProtections.Where(p => !p.StartsWith("Executable-Based Online Registration"));
// ProtectDISC / VOB ProtectCD/DVD
// TODO: Figure this one out
// SafeCast
// TODO: Figure this one out
// SafeDisc
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}")))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc 2")
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
.Where(p => !p.StartsWith("SafeDisc (dplayerx.dll)"))
.Where(p => !p.StartsWith("SafeDisc (drvmgt.dll)"))
.Where(p => !p.StartsWith("SafeDisc (secdrv.sys)"))
.Where(p => p != "SafeDisc Lite");
}
else if (foundProtections.Any(p => p.StartsWith("SafeDisc (drvmgt.dll)")))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc 2")
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
.Where(p => !p.StartsWith("SafeDisc (dplayerx.dll)"))
.Where(p => !p.StartsWith("SafeDisc (secdrv.sys)"))
.Where(p => p != "SafeDisc Lite");
}
else if (foundProtections.Any(p => p.StartsWith("SafeDisc (secdrv.sys)")))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc 2")
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
.Where(p => !p.StartsWith("SafeDisc (dplayerx.dll)"))
.Where(p => p != "SafeDisc Lite");
}
else if (foundProtections.Any(p => p.StartsWith("SafeDisc (dplayerx.dll)")))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc 2")
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
.Where(p => p != "SafeDisc Lite");
}
else if (foundProtections.Any(p => p == "SafeDisc 3.20-4.xx (version removed)"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc 2")
.Where(p => p != "SafeDisc Lite");
}
else if (foundProtections.Any(p => p == "SafeDisc 2"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc Lite");
}
else if (foundProtections.Any(p => p == "SafeDisc 1/Lite"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc Lite");
}
else if (foundProtections.Any(p => p == "SafeDisc Lite"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1-3");
}
else if (foundProtections.Any(p => p == "SafeDisc 1-3"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc");
}
}
// SecuROM
// TODO: Figure this one out
// SolidShield
// TODO: Figure this one out
// StarForce
if (foundProtections.Any(p => p.StartsWith("StarForce")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"StarForce [0-9]+\..+")))
{
foundProtections = foundProtections.Where(p => p != "StarForce")
.Where(p => p != "StarForce 3-5")
.Where(p => p != "StarForce 5")
.Where(p => p != "StarForce 5 [Protected Module]");
}
else if (foundProtections.Any(p => p == "StarForce 5 [Protected Module]"))
{
foundProtections = foundProtections.Where(p => p != "StarForce")
.Where(p => p != "StarForce 3-5")
.Where(p => p != "StarForce 5");
}
else if (foundProtections.Any(p => p == "StarForce 5"))
{
foundProtections = foundProtections.Where(p => p != "StarForce")
.Where(p => p != "StarForce 3-5");
}
else if (foundProtections.Any(p => p == "StarForce 3-5"))
{
foundProtections = foundProtections.Where(p => p != "StarForce");
}
}
// Sysiphus
if (foundProtections.Any(p => p == "Sysiphus") && foundProtections.Any(p => p.StartsWith("Sysiphus") && p.Length > "Sysiphus".Length))
foundProtections = foundProtections.Where(p => p != "Sysiphus");
// TAGES
// TODO: Figure this one out
// XCP
if (foundProtections.Any(p => p == "XCP") && foundProtections.Any(p => p.StartsWith("XCP") && p.Length > "XCP".Length))
foundProtections = foundProtections.Where(p => p != "XCP");
return string.Join(", ", foundProtections);
}
}
}

View File

@@ -1,229 +0,0 @@
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

File diff suppressed because it is too large Load Diff

View File

@@ -1,119 +0,0 @@
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;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
namespace MPF.Aaru
namespace MPF.Modules.Aaru
{
/// <summary>
/// Top-level commands for Aaru
@@ -7,6 +7,11 @@ namespace MPF.Aaru
{
public const string NONE = "";
// Archive Family
public const string ArchivePrefixShort = "arc";
public const string ArchivePrefixLong = "archive";
public const string ArchiveInfo = "info";
// Database Family
public const string DatabasePrefixShort = "db";
public const string DatabasePrefixLong = "database";
@@ -25,6 +30,7 @@ namespace MPF.Aaru
public const string FilesystemPrefixShortAlt = "fs";
public const string FilesystemPrefixLong = "filesystem";
public const string FilesystemExtract = "extract";
public const string FilesystemInfo = "info";
public const string FilesystemListShort = "ls";
public const string FilesystemListLong = "list";
public const string FilesystemOptions = "options";
@@ -32,7 +38,6 @@ namespace MPF.Aaru
// 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";
@@ -149,6 +154,7 @@ namespace MPF.Aaru
public const string MetadataLong = "--metadata";
public const string PartitionsShort = "-p";
public const string PartitionsLong = "--partitions";
public const string PauseLong = "--pause";
public const string PersistentLong = "--persistent";
public const string PrivateLong = "--private";
public const string ResumeShort = "-r";
@@ -168,9 +174,14 @@ namespace MPF.Aaru
public const string SpamSumLong = "--spamsum";
public const string StopOnErrorShort = "-s";
public const string StopOnErrorLong = "--stop-on-error";
public const string StoreEncryptedLong = "--store-encrypted";
public const string TapeShort = "-t";
public const string TapeLong = "--tape";
public const string TitleKeysLong = "--title-keys";
public const string TrapDiscShort = "-t";
public const string TrapDiscLong = "--trap-disc";
public const string TrimLong = "--trim";
public const string UseBufferedReadsLong = "--use-buffered-reads";
public const string VerboseShort = "-v";
public const string VerboseLong = "--verbose";
public const string VerifyDiscShort = "-w";
@@ -195,6 +206,7 @@ namespace MPF.Aaru
public const string BlockSizeLong = "--block-size";
public const string CountShort = "-c";
public const string CountLong = "--count";
public const string MaxBlocksLong = "--max-blocks";
public const string MediaLastSequenceLong = "--media-lastsequence";
public const string MediaSequenceLong = "--media-sequence";
public const string SkipShort = "-k";
@@ -219,6 +231,8 @@ namespace MPF.Aaru
public const string FormatConvertLong = "--format";
public const string FormatDumpShort = "-t";
public const string FormatDumpLong = "--format";
public const string GeometryShort = "-g";
public const string GeometryLong = "--geometry";
public const string ImgBurnLogShort = "-b";
public const string ImgBurnLogLong = "--ibg-log";
public const string MediaBarcodeLong = "--media-barcode";

View File

@@ -1,6 +1,6 @@
using MPF.Data;
using RedumpLib.Data;
namespace MPF.Aaru
namespace MPF.Modules.Aaru
{
public static class Converters
{

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

@@ -6,12 +6,12 @@ using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BurnOutSharp.ProtectionType;
using Compress.ThreadReaders;
using MPF.Hashing;
using MPF.Utilities;
using MPF.Core.Data;
using MPF.Core.Hashing;
using MPF.Core.Utilities;
using RedumpLib.Data;
namespace MPF.Data
namespace MPF.Modules
{
public abstract class BaseParameters
{
@@ -103,7 +103,7 @@ namespace MPF.Data
/// <summary>
/// Currently represented system
/// </summary>
public KnownSystem? System { get; set; }
public RedumpSystem? System { get; set; }
/// <summary>
/// Currently represented media type
@@ -126,13 +126,13 @@ namespace MPF.Data
/// <summary>
/// Generate parameters based on a set of known inputs
/// </summary>
/// <param name="system">KnownSystem value to use</param>
/// <param name="system">RedumpSystem value to use</param>
/// <param name="type">MediaType value to use</param>
/// <param name="driveLetter">Drive letter to use</param>
/// <param name="filename">Filename to use</param>
/// <param name="driveSpeed">Drive speed to use</param>
/// <param name="options">Options object containing all settings that may be used for setting parameters</param>
public BaseParameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
public BaseParameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
{
this.System = system;
this.Type = type;
@@ -308,12 +308,7 @@ namespace MPF.Data
/// <param name="index">Current index</param>
/// <returns>True if the next item exists, false otherwise</returns>
protected static bool DoesExist(List<string> parameters, int index)
{
if (index >= parameters.Count)
return false;
return true;
}
=> index < parameters.Count;
/// <summary>
/// Get the Base64 representation of a string
@@ -355,14 +350,9 @@ namespace MPF.Data
/// Returns whether a string is a valid drive letter
/// </summary>
/// <param name="parameter">String value to check</param>
/// <returns>True if it's a valid drive letter, false otherwise</returns>
/// <returns>True if it's a valid drive letter, false otherwise</W>
protected static bool IsValidDriveLetter(string parameter)
{
if (!Regex.IsMatch(parameter, @"^[A-Z]:?\\?$"))
return false;
return true;
}
=> Regex.IsMatch(parameter, @"^[A-Z]:?\\?$");
/// <summary>
/// Returns whether a string is a valid bool
@@ -370,9 +360,7 @@ namespace MPF.Data
/// <param name="parameter">String value to check</param>
/// <returns>True if it's a valid bool, false otherwise</returns>
protected static bool IsValidBool(string parameter)
{
return bool.TryParse(parameter, out bool _);
}
=> bool.TryParse(parameter, out bool _);
/// <summary>
/// Returns whether a string is a valid byte
@@ -462,9 +450,7 @@ namespace MPF.Data
/// <param name="i">Reference to the position in the parts</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
protected bool ProcessFlagParameter(List<string> parts, string flagString, ref int i)
{
return ProcessFlagParameter(parts, null, flagString, ref i);
}
=> ProcessFlagParameter(parts, null, flagString, ref i);
/// <summary>
/// Process a flag parameter
@@ -499,9 +485,7 @@ namespace MPF.Data
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
protected bool ProcessBooleanParameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessBooleanParameter(parts, null, flagString, ref i, missingAllowed);
}
=> ProcessBooleanParameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process a boolean parameter
@@ -576,9 +560,7 @@ namespace MPF.Data
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>SByte value if success, SByte.MinValue if skipped, null on error/returns>
protected sbyte? ProcessInt8Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessInt8Parameter(parts, null, flagString, ref i, missingAllowed);
}
=> ProcessInt8Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an sbyte parameter
@@ -655,9 +637,7 @@ namespace MPF.Data
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int16 value if success, Int16.MinValue if skipped, null on error/returns>
protected short? ProcessInt16Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessInt16Parameter(parts, null, flagString, ref i, missingAllowed);
}
=> ProcessInt16Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an Int16 parameter
@@ -733,9 +713,7 @@ namespace MPF.Data
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int32 value if success, Int32.MinValue if skipped, null on error/returns>
protected int? ProcessInt32Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessInt32Parameter(parts, null, flagString, ref i, missingAllowed);
}
=> ProcessInt32Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an Int32 parameter
@@ -811,9 +789,7 @@ namespace MPF.Data
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int64 value if success, Int64.MinValue if skipped, null on error/returns>
protected long? ProcessInt64Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessInt64Parameter(parts, null, flagString, ref i, missingAllowed);
}
=> ProcessInt64Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an Int64 parameter
@@ -881,7 +857,7 @@ namespace MPF.Data
}
/// <summary>
/// Process an Int64 parameter
/// Process an string parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
@@ -889,9 +865,7 @@ namespace MPF.Data
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
protected string ProcessStringParameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
{
return ProcessStringParameter(parts, null, flagString, ref i, missingAllowed);
}
=> ProcessStringParameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process a string parameter
@@ -1158,42 +1132,6 @@ namespace MPF.Data
}
}
/// <summary>
/// Get the existance of an anti-modchip string from a PlayStation disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Anti-modchip existance if possible, false on error</returns>
protected static bool GetPlayStationAntiModchipDetected(char? driveLetter)
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return false;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return false;
// Scan through each file to check for the anti-modchip strings
var antiModchip = new PSXAntiModchip();
foreach (string path in Directory.EnumerateFiles(drivePath, "*", SearchOption.AllDirectories))
{
try
{
byte[] fileContent = File.ReadAllBytes(path);
string protection = antiModchip.CheckContents(path, fileContent, includePosition: false);
if (!string.IsNullOrWhiteSpace(protection))
return true;
}
catch
{
// No-op, we don't care what the error was
}
}
return false;
}
/// <summary>
/// Get the EXE date from a PlayStation disc, if possible
/// </summary>
@@ -1202,7 +1140,7 @@ namespace MPF.Data
/// <param name="region">Output region, if possible</param>
/// <param name="date">Output EXE date in "yyyy-mm-dd" format if possible, null on error</param>
/// <returns></returns>
protected static bool GetPlayStationExecutableInfo(char? driveLetter, out string serial, out RedumpRegion? region, out string date)
protected static bool GetPlayStationExecutableInfo(char? driveLetter, out string serial, out Region? region, out string date)
{
serial = null; region = null; date = null;
@@ -1238,11 +1176,18 @@ namespace MPF.Data
if (!string.IsNullOrEmpty(bootValue))
{
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)");
if (match != null && match.Groups.Count > 1)
if (match.Groups.Count > 1)
{
exeName = match.Groups[1].Value;
exeName = exeName.Split(';')[0];
serial = exeName.Replace('_', '-').Replace(".", string.Empty);
// EXE name may have a trailing `;` after
// EXE name should always be in all caps
exeName = match.Groups[1].Value
.Split(';')[0]
.ToUpperInvariant();
// Serial is most of the EXE name normalized
serial = exeName
.Replace('_', '-')
.Replace(".", string.Empty);
}
}
@@ -1382,18 +1327,14 @@ namespace MPF.Data
/// </summary>
/// <param name="region">String representing the category</param>
/// <returns>Category, if possible</returns>
protected static RedumpDiscCategory? GetUMDCategory(string category)
protected static DiscCategory? GetUMDCategory(string category)
{
switch (category)
{
case "GAME":
return RedumpDiscCategory.Games;
case "VIDEO":
return RedumpDiscCategory.Video;
case "AUDIO":
return RedumpDiscCategory.Audio;
default:
return null;
case "GAME": return DiscCategory.Games;
case "VIDEO": return DiscCategory.Video;
case "AUDIO": return DiscCategory.Audio;
default: return null;
}
}
@@ -1406,7 +1347,7 @@ namespace MPF.Data
/// </summary>
/// <param name="serial">PlayStation serial code</param>
/// <returns>Region mapped from name, if possible</returns>
protected static RedumpRegion? GetPlayStationRegion(string serial)
protected static Region? GetPlayStationRegion(string serial)
{
// Standardized "S" serials
if (serial.StartsWith("S"))
@@ -1415,26 +1356,19 @@ namespace MPF.Data
// char secondRegion = serial[3];
switch (serial[2])
{
case 'A':
return RedumpRegion.Asia;
case 'C':
return RedumpRegion.China;
case 'E':
return RedumpRegion.Europe;
case 'J':
return RedumpRegion.JapanKorea;
case 'K':
return RedumpRegion.Korea;
case 'P':
return RedumpRegion.Japan;
case 'U':
return RedumpRegion.USA;
case 'A': return Region.Asia;
case 'C': return Region.China;
case 'E': return Region.Europe;
case 'J': return Region.JapanKorea;
case 'K': return Region.SouthKorea;
case 'P': return Region.Japan;
case 'U': return Region.UnitedStatesOfAmerica;
}
}
// Japan-only special serial
else if (serial.StartsWith("PAPX"))
return RedumpRegion.Japan;
return Region.Japan;
// Region appears entirely random
else if (serial.StartsWith("PABX"))
@@ -1442,47 +1376,19 @@ namespace MPF.Data
// Japan-only special serial
else if (serial.StartsWith("PCBX"))
return RedumpRegion.Japan;
return Region.Japan;
// Single disc known, Japan
else if (serial.StartsWith("PDBX"))
return RedumpRegion.Japan;
return Region.Japan;
// Single disc known, Europe
else if (serial.StartsWith("PEBX"))
return RedumpRegion.Europe;
return Region.Europe;
return null;
}
/// <summary>
/// Determine the region based on the XGD serial character
/// </summary>
/// <param name="region">Character denoting the region</param>
/// <returns>Region, if possible</returns>
protected static RedumpRegion? GetXgdRegion(char region)
{
switch (region)
{
case 'W':
return RedumpRegion.World;
case 'A':
return RedumpRegion.USA;
case 'J':
return RedumpRegion.JapanAsia;
case 'E':
return RedumpRegion.Europe;
case 'K':
return RedumpRegion.USAJapan;
case 'L':
return RedumpRegion.USAEurope;
case 'H':
return RedumpRegion.JapanEurope;
default:
return null;
}
}
#endregion
}
}

View File

@@ -2,9 +2,10 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Data;
using MPF.Core.Data;
using RedumpLib.Data;
namespace MPF.CleanRip
namespace MPF.Modules.CleanRip
{
/// <summary>
/// Represents a generic set of CleanRip parameters
@@ -22,7 +23,7 @@ namespace MPF.CleanRip
public Parameters(string parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
{
}
@@ -49,7 +50,8 @@ namespace MPF.CleanRip
break;
default:
return (false, missingFiles);
missingFiles.Add("Media and system combination not supported for CleanRip");
break;
}
return (!missingFiles.Any(), missingFiles);
@@ -82,7 +84,7 @@ namespace MPF.CleanRip
if (File.Exists(basePath + ".bca"))
info.Extras.BCA = GetBCA(basePath + ".bca");
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out RedumpRegion? gcRegion, out string gcVersion))
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out string gcVersion))
{
info.CommonDiscInfo.Region = gcRegion ?? info.CommonDiscInfo.Region;
info.VersionAndEditions.Version = gcVersion ?? info.VersionAndEditions.Version;
@@ -203,7 +205,7 @@ namespace MPF.CleanRip
/// <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)
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string version)
{
region = null; version = null;
@@ -238,49 +240,49 @@ namespace MPF.CleanRip
switch (serial[3])
{
case 'A':
region = RedumpRegion.World;
region = Region.World;
break;
case 'D':
region = RedumpRegion.Germany;
region = Region.Germany;
break;
case 'E':
region = RedumpRegion.USA;
region = Region.UnitedStatesOfAmerica;
break;
case 'F':
region = RedumpRegion.France;
region = Region.France;
break;
case 'I':
region = RedumpRegion.Italy;
region = Region.Italy;
break;
case 'J':
region = RedumpRegion.Japan;
region = Region.Japan;
break;
case 'K':
region = RedumpRegion.Korea;
region = Region.SouthKorea;
break;
case 'L':
region = RedumpRegion.Europe; // Japanese import to Europe
region = Region.Europe; // Japanese import to Europe
break;
case 'M':
region = RedumpRegion.Europe; // American import to Europe
region = Region.Europe; // American import to Europe
break;
case 'N':
region = RedumpRegion.USA; // Japanese import to USA
region = Region.UnitedStatesOfAmerica; // Japanese import to USA
break;
case 'P':
region = RedumpRegion.Europe;
region = Region.Europe;
break;
case 'R':
region = RedumpRegion.Russia;
region = Region.RussianFederation;
break;
case 'S':
region = RedumpRegion.Spain;
region = Region.Spain;
break;
case 'Q':
region = RedumpRegion.Korea; // Korea with Japanese language
region = Region.SouthKorea; // Korea with Japanese language
break;
case 'T':
region = RedumpRegion.Korea; // Korea with English language
region = Region.SouthKorea; // Korea with English language
break;
case 'X':
region = null; // Not a real region code

View File

@@ -1,4 +1,4 @@
namespace MPF.DD
namespace MPF.Modules.DD
{
/// <summary>
/// Top-level commands for DD

View File

@@ -1,6 +1,6 @@
using MPF.Data;
using RedumpLib.Data;
namespace MPF.DD
namespace MPF.Modules.DD
{
public static class Converters
{

View File

@@ -2,9 +2,10 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Data;
using MPF.Core.Data;
using RedumpLib.Data;
namespace MPF.DD
namespace MPF.Modules.DD
{
/// <summary>
/// Represents a generic set of DD parameters
@@ -59,7 +60,7 @@ namespace MPF.DD
public Parameters(string parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
{
}
@@ -86,10 +87,11 @@ namespace MPF.DD
switch (this.System)
{
case KnownSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out RedumpRegion? pythonTwoRegion, out string pythonTwoDate))
case RedumpSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate))
{
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {pythonTwoSerial}\n";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
@@ -97,21 +99,22 @@ namespace MPF.DD
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out RedumpRegion? playstationRegion, out string playstationDate))
case RedumpSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate))
{
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {playstationSerial}\n";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
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))
case RedumpSystem.SonyPlayStation2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate))
{
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {playstationTwoSerial}\n";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
}
@@ -119,11 +122,11 @@ namespace MPF.DD
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation4:
case RedumpSystem.SonyPlayStation4:
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation5:
case RedumpSystem.SonyPlayStation5:
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
break;
}

View File

@@ -1,4 +1,4 @@
namespace MPF.DiscImageCreator
namespace MPF.Modules.DiscImageCreator
{
/// <summary>
/// Top-level commands for DiscImageCreator

View File

@@ -1,6 +1,6 @@
using MPF.Data;
using RedumpLib.Data;
namespace MPF.DiscImageCreator
namespace MPF.Modules.DiscImageCreator
{
public static class Converters
{
@@ -10,33 +10,33 @@ namespace MPF.DiscImageCreator
/// 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)
/// <returns>RedumpSystem if possible, null on error</returns>
public static RedumpSystem? ToRedumpSystem(string baseCommand)
{
switch (baseCommand)
{
case CommandStrings.Audio:
return KnownSystem.AudioCD;
return RedumpSystem.AudioCD;
case CommandStrings.CompactDisc:
case CommandStrings.Data:
case CommandStrings.DigitalVideoDisc:
case CommandStrings.Disk:
case CommandStrings.Floppy:
case CommandStrings.Tape:
return KnownSystem.IBMPCCompatible;
return RedumpSystem.IBMPCcompatible;
case CommandStrings.GDROM:
case CommandStrings.Swap:
return KnownSystem.SegaDreamcast;
return RedumpSystem.SegaDreamcast;
case CommandStrings.BluRay:
return KnownSystem.SonyPlayStation3;
return RedumpSystem.SonyPlayStation3;
case CommandStrings.SACD:
return KnownSystem.SuperAudioCD;
return RedumpSystem.SuperAudioCD;
case CommandStrings.XBOX:
case CommandStrings.XBOXSwap:
return KnownSystem.MicrosoftXBOX;
return RedumpSystem.MicrosoftXbox;
case CommandStrings.XGD2Swap:
case CommandStrings.XGD3Swap:
return KnownSystem.MicrosoftXBOX360;
return RedumpSystem.MicrosoftXbox360;
default:
return null;
}

View File

@@ -3,12 +3,12 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using BurnOutSharp.External.psxt001z;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.CueSheets;
using MPF.Data;
using MPF.Utilities;
using RedumpLib.Data;
namespace MPF.DiscImageCreator
namespace MPF.Modules.DiscImageCreator
{
/// <summary>
/// Represents a generic set of DiscImageCreator parameters
@@ -159,7 +159,7 @@ namespace MPF.DiscImageCreator
public Parameters(string parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
{
}
@@ -346,7 +346,8 @@ namespace MPF.DiscImageCreator
break;
default:
return (false, missingFiles);
missingFiles.Add("Media and system combination not supported for DiscImageCreator");
break;
}
return (!missingFiles.Any(), missingFiles);
@@ -384,11 +385,15 @@ namespace MPF.DiscImageCreator
}
info.TracksAndWriteOffsets.Cuesheet = GetFullFile(basePath + ".cue") ?? "";
var cueSheet = new CueSheet(basePath + ".cue"); // TODO: Do something with this
//var cueSheet = new CueSheet(basePath + ".cue"); // TODO: Do something with this
string cdWriteOffset = GetWriteOffset(basePath + "_disc.txt") ?? "";
info.CommonDiscInfo.RingWriteOffset = cdWriteOffset;
info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset;
// Audio CDs "all have an offset of 0" and should not be included
if (System != RedumpSystem.AudioCD)
{
string cdWriteOffset = GetWriteOffset(basePath + "_disc.txt") ?? "";
info.CommonDiscInfo.RingWriteOffset = cdWriteOffset;
info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset;
}
break;
@@ -435,13 +440,13 @@ namespace MPF.DiscImageCreator
break;
}
// Extract info based specifically on KnownSystem
// Extract info based specifically on RedumpSystem
switch (this.System)
{
case KnownSystem.AppleMacintosh:
case KnownSystem.EnhancedCD:
case KnownSystem.IBMPCCompatible:
case KnownSystem.RainbowDisc:
case RedumpSystem.AppleMacintosh:
case RedumpSystem.EnhancedCD:
case RedumpSystem.IBMPCcompatible:
case RedumpSystem.RainbowDisc:
if (File.Exists(basePath + "_subIntention.txt"))
{
FileInfo fi = new FileInfo(basePath + "_subIntention.txt");
@@ -451,15 +456,16 @@ namespace MPF.DiscImageCreator
break;
case KnownSystem.DVDAudio:
case KnownSystem.DVDVideo:
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
info.CopyProtection.Protection = GetDVDProtection(basePath + "_CSSKey.txt", basePath + "_disc.txt") ?? "";
break;
case KnownSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out RedumpRegion? pythonTwoRegion, out string pythonTwoDate))
case RedumpSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate))
{
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {pythonTwoSerial}\n";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
@@ -467,44 +473,51 @@ namespace MPF.DiscImageCreator
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
break;
case KnownSystem.MicrosoftXBOX:
if (GetXgdAuxInfo(basePath + "_disc.txt", out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver))
case RedumpSystem.MicrosoftXbox:
string xgd1XMID = GetXGD1XMID(Path.Combine(outputDirectory, "DMI.bin"));
XgdInfo xgd1Info = new XgdInfo(xgd1XMID);
if (xgd1Info?.Initialized == true)
{
info.CommonDiscInfo.Comments += $"{Template.XBOXDMIHash}: {dmihash ?? ""}\n" +
$"{Template.XBOXPFIHash}: {pfihash ?? ""}\n" +
$"{Template.XBOXSSHash}: {sshash ?? ""}\n" +
$"{Template.XBOXSSVersion}: {ssver ?? ""}\n";
info.Extras.SecuritySectorRanges = ss ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = xgd1Info.XMID;
info.CommonDiscInfo.Serial = xgd1Info.GetSerial() ?? "";
info.VersionAndEditions.Version = xgd1Info.GetVersion() ?? "";
info.CommonDiscInfo.Region = xgd1Info.InternalRegion;
}
if (GetXboxDMIInfo(Path.Combine(outputDirectory, "DMI.bin"), out string serial, out string version, out RedumpRegion? region))
if (GetXGDAuxInfo(basePath + "_disc.txt", out string xgd1DMIHash, out string xgd1PFIHash, out string xgd1SSHash, out string xgd1SS, out string xgd1SSVer))
{
info.CommonDiscInfo.Serial = serial ?? "";
info.VersionAndEditions.Version = version ?? "";
info.CommonDiscInfo.Region = region;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd1DMIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer;
info.Extras.SecuritySectorRanges = xgd1SS ?? "";
}
break;
case KnownSystem.MicrosoftXBOX360:
if (GetXgdAuxInfo(basePath + "_disc.txt", out string dmi360hash, out string pfi360hash, out string ss360hash, out string ss360, out string ssver360))
case RedumpSystem.MicrosoftXbox360:
string xgd23XeMID = GetXGD23XeMID(Path.Combine(outputDirectory, "DMI.bin"));
XgdInfo xgd23Info = new XgdInfo(xgd23XeMID);
if (xgd23Info?.Initialized == true)
{
info.CommonDiscInfo.Comments += $"{Template.XBOXDMIHash}: {dmi360hash ?? ""}\n" +
$"{Template.XBOXPFIHash}: {pfi360hash ?? ""}\n" +
$"{Template.XBOXSSHash}: {ss360hash ?? ""}\n" +
$"{Template.XBOXSSVersion}: {ssver360 ?? ""}\n";
info.Extras.SecuritySectorRanges = ss360 ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = xgd23Info.XMID;
info.CommonDiscInfo.Serial = xgd23Info.GetSerial() ?? "";
info.VersionAndEditions.Version = xgd23Info.GetVersion() ?? "";
info.CommonDiscInfo.Region = xgd23Info.InternalRegion;
}
if (GetXbox360DMIInfo(Path.Combine(outputDirectory, "DMI.bin"), out string serial360, out string version360, out RedumpRegion? region360))
if (GetXGDAuxInfo(basePath + "_disc.txt", out string xgd23DMIHash, out string xgd23PFIHash, out string xgd23SSHash, out string xgd23SS, out string xgd23SSVer))
{
info.CommonDiscInfo.Serial = serial360 ?? "";
info.VersionAndEditions.Version = version360 ?? "";
info.CommonDiscInfo.Region = region360;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd23DMIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer;
info.Extras.SecuritySectorRanges = xgd23SS ?? "";
}
break;
case KnownSystem.NamcoSegaNintendoTriforce:
case RedumpSystem.NamcoSegaNintendoTriforce:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
@@ -515,7 +528,8 @@ namespace MPF.DiscImageCreator
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty;
info.VersionAndEditions.Version = gdVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
}
@@ -523,7 +537,7 @@ namespace MPF.DiscImageCreator
break;
case KnownSystem.SegaCDMegaCD:
case RedumpSystem.SegaMegaCDSegaCD:
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
// Take only the last 16 lines for Sega CD
@@ -532,13 +546,14 @@ namespace MPF.DiscImageCreator
if (GetSegaCDBuildInfo(info.Extras.Header, out string scdSerial, out string fixedDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {scdSerial ?? ""}";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = scdSerial ?? string.Empty;
info.CommonDiscInfo.EXEDateBuildDate = fixedDate ?? "";
}
break;
case KnownSystem.SegaChihiro:
case RedumpSystem.SegaChihiro:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
@@ -549,7 +564,8 @@ namespace MPF.DiscImageCreator
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty;
info.VersionAndEditions.Version = gdVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
}
@@ -557,7 +573,7 @@ namespace MPF.DiscImageCreator
break;
case KnownSystem.SegaDreamcast:
case RedumpSystem.SegaDreamcast:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
@@ -568,7 +584,8 @@ namespace MPF.DiscImageCreator
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty;
info.VersionAndEditions.Version = gdVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
}
@@ -576,7 +593,7 @@ namespace MPF.DiscImageCreator
break;
case KnownSystem.SegaNaomi:
case RedumpSystem.SegaNaomi:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
@@ -587,7 +604,8 @@ namespace MPF.DiscImageCreator
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty;
info.VersionAndEditions.Version = gdVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
}
@@ -595,7 +613,7 @@ namespace MPF.DiscImageCreator
break;
case KnownSystem.SegaNaomi2:
case RedumpSystem.SegaNaomi2:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
@@ -606,7 +624,8 @@ namespace MPF.DiscImageCreator
if (GetGDROMBuildInfo(info.Extras.Header, out string gdSerial, out string gdVersion, out string gdDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {gdSerial ?? ""}";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = gdSerial ?? string.Empty;
info.VersionAndEditions.Version = gdVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = gdDate ?? "";
}
@@ -614,7 +633,7 @@ namespace MPF.DiscImageCreator
break;
case KnownSystem.SegaSaturn:
case RedumpSystem.SegaSaturn:
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
// Take only the first 16 lines for Saturn
@@ -623,17 +642,19 @@ namespace MPF.DiscImageCreator
if (GetSaturnBuildInfo(info.Extras.Header, out string saturnSerial, out string saturnVersion, out string buildDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {saturnSerial ?? ""}";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = saturnSerial ?? string.Empty;
info.VersionAndEditions.Version = saturnVersion ?? "";
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? "";
}
break;
case KnownSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out RedumpRegion? playstationRegion, out string playstationDate))
case RedumpSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate))
{
info.CommonDiscInfo.Comments += $"Internal Serial: {playstationSerial ?? ""}\n";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
}
@@ -652,45 +673,13 @@ namespace MPF.DiscImageCreator
info.EDC.EDC = YesNo.NULL;
info.CopyProtection.AntiModchip = GetPlayStationAntiModchipDetected(basePath + "_disc.txt") ? YesNo.Yes : YesNo.No;
bool? psLibCryptStatus = GetLibCryptDetected(basePath + ".sub");
if (psLibCryptStatus == true)
{
// Guard against false positives
if (File.Exists(basePath + "_subIntention.txt"))
{
string libCryptData = GetFullFile(basePath + "_subIntention.txt") ?? "";
if (string.IsNullOrEmpty(libCryptData))
{
info.CopyProtection.LibCrypt = YesNo.No;
}
else
{
info.CopyProtection.LibCrypt = YesNo.Yes;
info.CopyProtection.LibCryptData = libCryptData;
}
}
else
{
info.CopyProtection.LibCrypt = YesNo.No;
}
}
else if (psLibCryptStatus == false)
{
info.CopyProtection.LibCrypt = YesNo.No;
}
else
{
info.CopyProtection.LibCrypt = YesNo.NULL;
info.CopyProtection.LibCryptData = "LibCrypt could not be detected because subchannel file is missing";
}
break;
case KnownSystem.SonyPlayStation2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out RedumpRegion? playstationTwoRegion, out string playstationTwoDate))
case RedumpSystem.SonyPlayStation2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate))
{
info.CommonDiscInfo.Comments += $"Internal Disc Serial: {playstationTwoSerial}\n";
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
}
@@ -698,11 +687,11 @@ namespace MPF.DiscImageCreator
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation4:
case RedumpSystem.SonyPlayStation4:
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
break;
case KnownSystem.SonyPlayStation5:
case RedumpSystem.SonyPlayStation5:
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
break;
}
@@ -718,6 +707,8 @@ namespace MPF.DiscImageCreator
info.Artifacts["ccd"] = GetBase64(GetFullFile(basePath + ".ccd"));
if (File.Exists(basePath + "_cmd.txt")) // TODO: Figure out how to read in the timestamp-named file
info.Artifacts["cmd"] = GetBase64(GetFullFile(basePath + "_cmd.txt"));
if (File.Exists(basePath + "_CSSKey.txt"))
info.Artifacts["csskey"] = GetBase64(GetFullFile(basePath + "_CSSKey.txt"));
if (File.Exists(basePath + ".cue"))
info.Artifacts["cue"] = GetBase64(GetFullFile(basePath + ".cue"));
if (File.Exists(basePath + ".dat"))
@@ -859,8 +850,7 @@ namespace MPF.DiscImageCreator
if (BaseCommand == CommandStrings.Audio
|| BaseCommand == CommandStrings.Data)
{
if (StartLBAValue != null && StartLBAValue > 0
&& EndLBAValue != null && EndLBAValue > 0)
if (StartLBAValue != null && EndLBAValue != null)
{
parameters.Add(StartLBAValue.ToString());
parameters.Add(EndLBAValue.ToString());
@@ -1311,6 +1301,7 @@ namespace MPF.DiscImageCreator
FlagStrings.DisableBeep,
FlagStrings.Fix,
FlagStrings.ForceUnitAccess,
FlagStrings.NoSkipSS,
FlagStrings.PadSector,
FlagStrings.Raw,
FlagStrings.Resume,
@@ -1462,8 +1453,6 @@ namespace MPF.DiscImageCreator
logFiles.Add(cmdPath);
if (File.Exists($"{basePath}_cmd.txt"))
logFiles.Add($"{basePath}_cmd.txt");
if (File.Exists($"{basePath}_CSSKey.txt"))
logFiles.Add($"{basePath}_CSSKey.txt");
if (File.Exists($"{basePath}.dat"))
logFiles.Add($"{basePath}.dat");
if (File.Exists($"{basePath}.sub"))
@@ -1508,6 +1497,8 @@ namespace MPF.DiscImageCreator
logFiles.Add(cmdPath);
if (File.Exists($"{basePath}_cmd.txt"))
logFiles.Add($"{basePath}_cmd.txt");
if (File.Exists($"{basePath}_CSSKey.txt"))
logFiles.Add($"{basePath}_CSSKey.txt");
if (File.Exists($"{basePath}.dat"))
logFiles.Add($"{basePath}.dat");
if (File.Exists($"{basePath}_disc.txt"))
@@ -1614,7 +1605,7 @@ namespace MPF.DiscImageCreator
Filename = filename;
// First check to see if the combination of system and MediaType is valid
var validTypes = Validators.GetValidMediaTypes(this.System);
var validTypes = this.System.MediaTypes();
if (!validTypes.Contains(this.Type))
return;
@@ -1644,8 +1635,8 @@ namespace MPF.DiscImageCreator
switch (this.System)
{
case KnownSystem.AppleMacintosh:
case KnownSystem.IBMPCCompatible:
case RedumpSystem.AppleMacintosh:
case RedumpSystem.IBMPCcompatible:
this[FlagStrings.NoFixSubQSecuROM] = true;
this[FlagStrings.ScanFileProtect] = true;
this[FlagStrings.ScanSectorProtect] = options.DICParanoidMode;
@@ -1654,17 +1645,17 @@ namespace MPF.DiscImageCreator
SubchannelReadLevelValue = 2;
break;
case KnownSystem.AtariJaguarCD:
case RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem:
this[FlagStrings.AtariJaguar] = true;
break;
case KnownSystem.HasbroVideoNow:
case KnownSystem.HasbroVideoNowColor:
case KnownSystem.HasbroVideoNowJr:
case KnownSystem.HasbroVideoNowXP:
case RedumpSystem.HasbroVideoNow:
case RedumpSystem.HasbroVideoNowColor:
case RedumpSystem.HasbroVideoNowJr:
case RedumpSystem.HasbroVideoNowXP:
this[FlagStrings.AddOffset] = true;
this.AddOffsetValue = 0; // Value needed for first run and placeholder after
break;
case KnownSystem.SonyPlayStation:
case RedumpSystem.SonyPlayStation:
this[FlagStrings.ScanAntiMod] = true;
this[FlagStrings.NoFixSubQLibCrypt] = true;
break;
@@ -1742,12 +1733,12 @@ namespace MPF.DiscImageCreator
else
DriveSpeed = Int32.Parse(parts[3]);
if (!IsValidInt32(parts[4], lowerBound: 0))
if (!IsValidInt32(parts[4]))
return false;
else
StartLBAValue = Int32.Parse(parts[4]);
if (!IsValidInt32(parts[5], lowerBound: 0))
if (!IsValidInt32(parts[5]))
return false;
else
EndLBAValue = Int32.Parse(parts[5]);
@@ -1829,12 +1820,12 @@ namespace MPF.DiscImageCreator
else
DriveSpeed = Int32.Parse(parts[3]);
if (!IsValidInt32(parts[4], lowerBound: 0))
if (!IsValidInt32(parts[4]))
return false;
else
StartLBAValue = Int32.Parse(parts[4]);
if (!IsValidInt32(parts[5], lowerBound: 0))
if (!IsValidInt32(parts[5]))
return false;
else
EndLBAValue = Int32.Parse(parts[5]);
@@ -2120,7 +2111,7 @@ namespace MPF.DiscImageCreator
return false;
}
// Loop through all auxilary flags, if necessary
// Loop through all auxiliary flags, if necessary
if (index > 0)
{
for (int i = index; i < parts.Count; i++)
@@ -2331,12 +2322,12 @@ namespace MPF.DiscImageCreator
/// <summary>
/// Set the DIC command to be used for a given system and media type
/// </summary>
/// <param name="system">KnownSystem value to check</param>
/// <param name="system">RedumpSystem value to check</param>
/// <param name="type">MediaType value to check</param>
private void SetBaseCommand(KnownSystem? system, MediaType? type)
private void SetBaseCommand(RedumpSystem? system, MediaType? type)
{
// If we have an invalid combination, we should BaseCommand = null
if (!Validators.GetValidMediaTypes(system).Contains(type))
if (!system.MediaTypes().Contains(type))
{
BaseCommand = null;
return;
@@ -2345,14 +2336,14 @@ namespace MPF.DiscImageCreator
switch (type)
{
case MediaType.CDROM:
if (system == KnownSystem.SuperAudioCD)
if (system == RedumpSystem.SuperAudioCD)
BaseCommand = CommandStrings.SACD;
else
BaseCommand = CommandStrings.CompactDisc;
return;
case MediaType.DVD:
if (system == KnownSystem.MicrosoftXBOX
|| system == KnownSystem.MicrosoftXBOX360)
if (system == RedumpSystem.MicrosoftXbox
|| system == RedumpSystem.MicrosoftXbox360)
{
BaseCommand = CommandStrings.XBOX;
return;
@@ -2502,12 +2493,20 @@ namespace MPF.DiscImageCreator
if (line.Contains("No TitleKey"))
{
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), No TitleKey$");
vobKeys += $"{match.Groups[1].Value} Title Key: No TitleKey\n";
string matchedFilename = match.Groups[1].Value;
if (matchedFilename.EndsWith(";1"))
matchedFilename = matchedFilename.Substring(0, matchedFilename.Length - 2);
vobKeys += $"{matchedFilename} Title Key: No Title Key\n";
}
else
{
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), EncryptedTitleKey: .*?, DecryptedTitleKey: (.*?)$");
vobKeys += $"{match.Groups[1].Value} Title Key: {match.Groups[2].Value}\n";
string matchedFilename = match.Groups[1].Value;
if (matchedFilename.EndsWith(";1"))
matchedFilename = matchedFilename.Substring(0, matchedFilename.Length - 2);
vobKeys += $"{matchedFilename} Title Key: {match.Groups[2].Value}\n";
}
}
}
@@ -2736,20 +2735,6 @@ namespace MPF.DiscImageCreator
}
}
/// <summary>
/// Get if LibCrypt data is detected in the subchannel file, if possible
/// </summary>
/// <param name="sub">.sub file location</param>
/// <returns>Status of the LibCrypt data, if possible</returns>
private static bool? GetLibCryptDetected(string sub)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(sub))
return null;
return LibCrypt.CheckSubfile(sub);
}
/// <summary>
/// Get the hex contents of the PIC file
/// </summary>
@@ -2943,9 +2928,10 @@ namespace MPF.DiscImageCreator
string[] header = segaHeader.Split('\n');
string serialVersionLine = header[2].Substring(58);
string dateLine = header[3].Substring(58);
serial = serialVersionLine.Substring(0, 8);
serial = serialVersionLine.Substring(0, 10).Trim();
version = serialVersionLine.Substring(10, 6).TrimStart('V', 'v');
date = dateLine.Substring(0, 8);
date = $"{date[0]}{date[1]}{date[2]}{date[3]}-{date[4]}{date[5]}-{date[6]}{date[7]}";
return true;
}
catch
@@ -2975,7 +2961,7 @@ namespace MPF.DiscImageCreator
string[] header = segaHeader.Split('\n');
string serialVersionLine = header[8].Substring(58);
string dateLine = header[1].Substring(58);
serial = serialVersionLine.Substring(3, 7);
serial = serialVersionLine.Substring(3, 8).TrimEnd('-', ' ');
date = dateLine.Substring(8).Trim();
// Properly format the date string, if possible
@@ -3056,10 +3042,12 @@ namespace MPF.DiscImageCreator
{
// If we're in a new mainInfo, the location of the header changed
string line = sr.ReadLine();
if (line.StartsWith("========== OpCode") || line.StartsWith("========== TOC (Binary)"))
if (line.StartsWith("========== OpCode")
|| line.StartsWith("========== TOC (Binary)")
|| line.StartsWith("========== FULL TOC (Binary)"))
{
// Seek to unscrambled data
while (!(line = sr.ReadLine()).StartsWith("========== Check Volume Descriptor ==========")) ;
while (!(line = sr.ReadLine()).Contains("Check MCN and/or ISRC")) ;
// Read the next line so the search goes properly
line = sr.ReadLine();
@@ -3127,8 +3115,13 @@ namespace MPF.DiscImageCreator
/// Get the XGD auxiliary info from the outputted files, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <param name="dmihash">Extracted DMI.bin CRC32 hash (upper-cased)</param>
/// <param name="pfihash">Extracted PFI.bin CRC32 hash (upper-cased)</param>
/// <param name="sshash">Extracted SS.bin CRC32 hash (upper-cased)</param>
/// <param name="ss">Extracted security sector data</param>
/// <param name="ssver">Extracted security sector version</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
private static bool GetXgdAuxInfo(string disc, out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver)
private static bool GetXGDAuxInfo(string disc, out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver)
{
dmihash = null; pfihash = null; sshash = null; ss = null; ssver = null;
@@ -3179,12 +3172,15 @@ namespace MPF.DiscImageCreator
// Special File Hashes
else if (line.StartsWith("<rom"))
{
if (line.Contains("SS.bin"))
sshash = line;
else if (line.Contains("PFI.bin"))
pfihash = line;
else if (line.Contains("DMI.bin"))
dmihash = line;
if (GetISOHashValues(line, out long _, out string crc32, out string _, out string _))
{
if (line.Contains("SS.bin"))
sshash = crc32.ToUpperInvariant();
else if (line.Contains("PFI.bin"))
pfihash = crc32.ToUpperInvariant();
else if (line.Contains("DMI.bin"))
dmihash = crc32.ToUpperInvariant();
}
}
}
@@ -3199,65 +3195,49 @@ namespace MPF.DiscImageCreator
}
/// <summary>
/// Get the Xbox serial info from the DMI.bin file, if possible
/// Get the XGD1 Master ID (XMID) information
/// </summary>
/// <param name="dmi">DMI.bin file location</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
private static bool GetXboxDMIInfo(string dmi, out string serial, out string version, out RedumpRegion? region)
/// <returns>String representation of the XGD1 DMI information, empty string on error</returns>
private static string GetXGD1XMID(string dmi)
{
serial = null; version = null; region = RedumpRegion.World;
if (!File.Exists(dmi))
return false;
return string.Empty;
using (BinaryReader br = new BinaryReader(File.OpenRead(dmi)))
{
try
{
br.BaseStream.Seek(8, SeekOrigin.Begin);
char[] str = br.ReadChars(8);
serial = $"{str[0]}{str[1]}-{str[2]}{str[3]}{str[4]}";
version = $"1.{str[5]}{str[6]}";
region = GetXgdRegion(str[7]);
return true;
return new string(br.ReadChars(8));
}
catch
{
return false;
return string.Empty;
}
}
}
/// <summary>
/// Get the Xbox 360 serial info from the DMI.bin file, if possible
/// Get the XGD2/3 Master ID (XeMID) information
/// </summary>
/// <param name="dmi">DMI.bin file location</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
private static bool GetXbox360DMIInfo(string dmi, out string serial, out string version, out RedumpRegion? region)
/// <returns>String representation of the XGD2/3 DMI information, empty string on error</returns>
private static string GetXGD23XeMID(string dmi)
{
serial = null; version = null; region = RedumpRegion.World;
if (!File.Exists(dmi))
return false;
return string.Empty;
using (BinaryReader br = new BinaryReader(File.OpenRead(dmi)))
{
try
{
br.BaseStream.Seek(64, SeekOrigin.Begin);
char[] str = br.ReadChars(14);
serial = $"{str[0]}{str[1]}-{str[2]}{str[3]}{str[4]}{str[5]}";
version = $"1.{str[6]}{str[7]}";
region = GetXgdRegion(str[8]);
// str[9], str[10], str[11] - unknown purpose
// str[12], str[13] - disc <12> of <13>
return true;
return new string(br.ReadChars(14));
}
catch
{
return false;
return string.Empty;
}
}
}

View File

@@ -0,0 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.3</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)'=='net48'">
<DefineConstants>NET_FRAMEWORK</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.CueSheets\MPF.CueSheets.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
<ProjectReference Include="..\CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -2,9 +2,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Data;
using MPF.Core.Data;
using RedumpLib.Data;
namespace MPF.UmdImageCreator
namespace MPF.Modules.UmdImageCreator
{
/// <summary>
/// Represents a generic set of UmdImageCreator parameters
@@ -22,7 +23,7 @@ namespace MPF.UmdImageCreator
public Parameters(string parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(KnownSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
{
}
@@ -51,7 +52,8 @@ namespace MPF.UmdImageCreator
break;
default:
return (false, missingFiles);
missingFiles.Add("Media and system combination not supported for UmdImageCreator");
break;
}
return (!missingFiles.Any(), missingFiles);
@@ -74,10 +76,10 @@ namespace MPF.UmdImageCreator
info.SizeAndChecksums.SHA1 = sha1;
}
if (GetUMDAuxInfo(basePath + "_disc.txt", out string title, out RedumpDiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize))
if (GetUMDAuxInfo(basePath + "_disc.txt", out string title, out DiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize))
{
info.CommonDiscInfo.Title = title ?? "";
info.CommonDiscInfo.Category = umdcat ?? RedumpDiscCategory.Games;
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
info.VersionAndEditions.Version = umdversion ?? "";
info.SizeAndChecksums.Size = umdsize;
@@ -169,7 +171,7 @@ namespace MPF.UmdImageCreator
/// </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)
private static bool GetUMDAuxInfo(string disc, out string title, out DiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize)
{
title = null; umdcat = null; umdversion = null; umdlayer = null; umdsize = -1;

View File

@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if NET_FRAMEWORK
using IMAPI2;
#endif
using MPF.Core.Converters;
using MPF.Core.Data;
using Xunit;
namespace MPF.Test.Core.Converters
{
public class EnumConverterTests
{
#region Cross-enumeration conversions
/// <summary>
/// DiscType values that map to InternalDriveType
/// </summary>
private static readonly DriveType[] _mappableDriveTypes = new DriveType[]
{
DriveType.CDRom,
DriveType.Fixed,
DriveType.Removable,
};
#if NET_FRAMEWORK
/// <summary>
/// IMAPI_MEDIA_PHYSICAL_TYPE values that map to MediaType
/// </summary>
private static readonly IMAPI_MEDIA_PHYSICAL_TYPE[] _mappableImapiTypes = new IMAPI_MEDIA_PHYSICAL_TYPE[]
{
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_UNKNOWN,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDROM,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDR,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDRW,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDROM,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDRAM,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR_DUALLAYER,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHRW,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR_DUALLAYER,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DISK,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW_DUALLAYER,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDROM,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDR,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDRAM,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDROM,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDR,
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDRE,
};
#endif
/// <summary>
/// Check that every supported DriveType maps to an InternalDriveType
/// </summary>
/// <param name="driveType">DriveType value to check</param>
/// <param name="expectNull">True to expect a null mapping, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDriveTypeMappingTestData))]
public void ToInternalDriveTypeTest(DriveType driveType, bool expectNull)
{
var actual = driveType.ToInternalDriveType();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
#if NET_FRAMEWORK
/// <summary>
/// Check that every supported IMAPI_MEDIA_PHYSICAL_TYPE maps to an MediaType
/// </summary>
/// <param name="imapiType">IMAPI_MEDIA_PHYSICAL_TYPE value to check</param>
/// <param name="expectNull">True to expect a null mapping, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateImapiTypeMappingTestData))]
public void IMAPIToMediaTypeTest(IMAPI_MEDIA_PHYSICAL_TYPE imapiType, bool expectNull)
{
var actual = imapiType.IMAPIToMediaType();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
#endif
/// <summary>
/// Generate a test set of DriveType values
/// </summary>
/// <returns>MemberData-compatible list of DriveType values</returns>
public static List<object[]> GenerateDriveTypeMappingTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (DriveType driveType in Enum.GetValues(typeof(DriveType)))
{
if (_mappableDriveTypes.Contains(driveType))
testData.Add(new object[] { driveType, false });
else
testData.Add(new object[] { driveType, true });
}
return testData;
}
#if NET_FRAMEWORK
/// <summary>
/// Generate a test set of IMAPI_MEDIA_PHYSICAL_TYPE values
/// </summary>
/// <returns>MemberData-compatible list of IMAPI_MEDIA_PHYSICAL_TYPE values</returns>
public static List<object[]> GenerateImapiTypeMappingTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
foreach (IMAPI_MEDIA_PHYSICAL_TYPE imapiType in Enum.GetValues(typeof(IMAPI_MEDIA_PHYSICAL_TYPE)))
{
if (_mappableImapiTypes.Contains(imapiType))
testData.Add(new object[] { imapiType, false });
else
testData.Add(new object[] { imapiType, true });
}
return testData;
}
#endif
#endregion
#region Convert to Long Name
// TODO: Maybe add a test for the generic "GetLongName" method
/// <summary>
/// Check that every InternalProgram has a long name provided
/// </summary>
/// <param name="internalProgram">InternalProgram value to check</param>
[Theory]
[MemberData(nameof(GenerateInternalProgramTestData))]
public void InternalProgramLongNameTest(InternalProgram? internalProgram)
{
string actual = internalProgram.LongName();
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of InternalProgram values
/// </summary>
/// <returns>MemberData-compatible list of InternalProgram values</returns>
public static List<object[]> GenerateInternalProgramTestData()
{
var testData = new List<object[]>() { new object[] { null } };
foreach (InternalProgram? internalProgram in Enum.GetValues(typeof(InternalProgram)))
{
testData.Add(new object[] { internalProgram });
}
return testData;
}
#endregion
// TODO: Add from-string tests
}
}

View File

@@ -0,0 +1,42 @@
using MPF.Core.Data;
using Xunit;
namespace MPF.Test.Core.Data
{
public class ResultTests
{
[Fact]
public void EmptySuccessTest()
{
var actual = Result.Success();
Assert.True(actual);
Assert.Empty(actual.Message);
}
[Fact]
public void CustomMessageSuccessTest()
{
string message = "Success!";
var actual = Result.Success(message);
Assert.True(actual);
Assert.Equal(message, actual.Message);
}
[Fact]
public void EmptyFailureTest()
{
var actual = Result.Failure();
Assert.False(actual);
Assert.Empty(actual.Message);
}
[Fact]
public void CustomMessageFailureTest()
{
string message = "Failure!";
var actual = Result.Failure(message);
Assert.False(actual);
Assert.Equal(message, actual.Message);
}
}
}

View File

@@ -0,0 +1,115 @@
using MPF.Core.Data;
using Xunit;
namespace MPF.Test.Core.Data
{
public class XgdInfoTests
{
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("1234567")]
[InlineData("1234567\0")]
[InlineData("123456789")]
public void UnmatchedStringTests(string invalidString)
{
XgdInfo xgdInfo = new XgdInfo(invalidString);
Assert.False(xgdInfo.Initialized);
}
[Theory]
[InlineData("AV00100W", "AV", "001", "00", 'W')]
[InlineData("AV00100W\0", "AV", "001", "00", 'W')]
public void XGD1ValidTests(string validString, string publisher, string gameId, string version, char regionIdentifier)
{
XgdInfo xgdInfo = new XgdInfo(validString, validate: true);
Assert.True(xgdInfo.Initialized);
Assert.Equal(publisher, xgdInfo.PublisherIdentifier);
Assert.Equal(gameId, xgdInfo.GameID);
Assert.Equal(version, xgdInfo.SKU);
Assert.Equal(regionIdentifier, xgdInfo.RegionIdentifier);
}
[Theory]
// Invalid publisher identifier
[InlineData("ZZ000000")]
[InlineData("ZZ000000\0")]
// Invalid region identifier
[InlineData("AV00000Z")]
[InlineData("AV00000Z\0")]
public void XGD1InvalidTests(string invalidString)
{
XgdInfo xgdInfo = new XgdInfo(invalidString, validate: true);
Assert.False(xgdInfo.Initialized);
}
[Theory]
[InlineData("AV200100W0F11", "AV", "001", "00", 'W', "0", 'F', "11", null)]
[InlineData("AV200100W0F11\0", "AV", "001", "00", 'W', "0", 'F', "11", null)]
[InlineData("AV200100W01F11", "AV", "001", "00", 'W', "01", 'F', "11", null)]
[InlineData("AV200100W01F11\0", "AV", "001", "00", 'W', "01", 'F', "11", null)]
[InlineData("AV200100W0F11DEADBEEF", "AV", "001", "00", 'W', "0", 'F', "11", "DEADBEEF")]
[InlineData("AV200100W0F11DEADBEEF\0", "AV", "001", "00", 'W', "0", 'F', "11", "DEADBEEF")]
[InlineData("AV200100W01F11DEADBEEF", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
[InlineData("AV200100W01F11DEADBEEF\0", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
public void XGD23ValidTests(string validString, string publisher, string gameId, string sku, char regionIdentifier, string baseVersion, char mediaSubtype, string discNumber, string cert)
{
XgdInfo xgdInfo = new XgdInfo(validString, validate: true);
Assert.True(xgdInfo.Initialized);
Assert.Equal(publisher, xgdInfo.PublisherIdentifier);
Assert.Equal('2', xgdInfo.PlatformIdentifier);
Assert.Equal(gameId, xgdInfo.GameID);
Assert.Equal(sku, xgdInfo.SKU);
Assert.Equal(regionIdentifier, xgdInfo.RegionIdentifier);
Assert.Equal(baseVersion, xgdInfo.BaseVersion);
Assert.Equal(mediaSubtype, xgdInfo.MediaSubtypeIdentifier);
Assert.Equal(discNumber, xgdInfo.DiscNumberIdentifier);
Assert.Equal(cert, xgdInfo.CertificationSubmissionIdentifier);
}
[Theory]
// Invalid publisher identifier
[InlineData("ZZ00000000000")]
[InlineData("ZZ00000000000\0")]
[InlineData("ZZ000000000000")]
[InlineData("ZZ000000000000\0")]
[InlineData("ZZ0000000000000000000")]
[InlineData("ZZ0000000000000000000\0")]
[InlineData("ZZ00000000000000000000")]
[InlineData("ZZ00000000000000000000\0")]
// Invalid platform identifier
[InlineData("AV90000000000")]
[InlineData("AV90000000000\0")]
[InlineData("AV900000000000")]
[InlineData("AV900000000000\0")]
[InlineData("AV9000000000000000000")]
[InlineData("AV9000000000000000000\0")]
[InlineData("AV90000000000000000000")]
[InlineData("AV90000000000000000000\0")]
// Invalid region identifier
[InlineData("AV200000Z0000")]
[InlineData("AV200000Z0000\0")]
[InlineData("AV200000Z00000")]
[InlineData("AV200000Z00000\0")]
[InlineData("AV200000Z000000000000")]
[InlineData("AV200000Z000000000000\0")]
[InlineData("AV200000Z0000000000000")]
[InlineData("AV200000Z0000000000000\0")]
// Invalid media subtype identifier
[InlineData("AV200000W0A00")]
[InlineData("AV200000W0A00\0")]
[InlineData("AV200000W00A00")]
[InlineData("AV200000W00A00\0")]
[InlineData("AV200000W00A000000000")]
[InlineData("AV200000W00A000000000\0")]
[InlineData("AV200000W00A0000000000")]
[InlineData("AV200000W00A0000000000\0")]
public void XGD23InvalidTests(string invalidString)
{
XgdInfo xgdInfo = new XgdInfo(invalidString, validate: true);
Assert.False(xgdInfo.Initialized);
}
}
}

View File

@@ -0,0 +1,229 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Core.Utilities;
using RedumpLib.Data;
using Xunit;
namespace MPF.Test.Core.Utilities
{
public class EnumExtensionsTests
{
/// <summary>
/// MediaType values that support drive speeds
/// </summary>
private static readonly MediaType?[] _supportDriveSpeeds = new MediaType?[]
{
MediaType.CDROM,
MediaType.DVD,
MediaType.GDROM,
MediaType.HDDVD,
MediaType.BluRay,
MediaType.NintendoGameCubeGameDisc,
MediaType.NintendoWiiOpticalDisc,
};
/// <summary>
/// RedumpSystem values that are considered Audio
/// </summary>
private static readonly RedumpSystem?[] _audioSystems = new RedumpSystem?[]
{
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem,
RedumpSystem.AudioCD,
RedumpSystem.DVDAudio,
RedumpSystem.HasbroVideoNow,
RedumpSystem.HasbroVideoNowColor,
RedumpSystem.HasbroVideoNowJr,
RedumpSystem.HasbroVideoNowXP,
RedumpSystem.PhilipsCDi,
RedumpSystem.SuperAudioCD,
};
/// <summary>
/// RedumpSystem values that are considered markers
/// </summary>
private static readonly RedumpSystem?[] _markerSystems = new RedumpSystem?[]
{
RedumpSystem.MarkerArcadeEnd,
RedumpSystem.MarkerComputerEnd,
RedumpSystem.MarkerDiscBasedConsoleEnd,
RedumpSystem.MarkerOtherEnd,
};
/// <summary>
/// RedumpSystem values that are have reversed ringcodes
/// </summary>
private static readonly RedumpSystem?[] _reverseRingcodeSystems = new RedumpSystem?[]
{
RedumpSystem.SonyPlayStation2,
RedumpSystem.SonyPlayStation3,
RedumpSystem.SonyPlayStation4,
};
/// <summary>
/// RedumpSystem values that are considered XGD
/// </summary>
private static readonly RedumpSystem?[] _xgdSystems = new RedumpSystem?[]
{
RedumpSystem.MicrosoftXbox,
RedumpSystem.MicrosoftXbox360,
RedumpSystem.MicrosoftXboxOne,
RedumpSystem.MicrosoftXboxSeriesXS,
};
/// <summary>
/// Check that all optical media support drive speeds
/// </summary>
/// <param name="mediaType">DriveType value to check</param>
/// <param name="expected">The expected value to come from the check</param>
[Theory]
[MemberData(nameof(GenerateSupportDriveSpeedsTestData))]
public void DoesSupportDriveSpeedTest(MediaType? mediaType, bool expected)
{
bool actual = mediaType.DoesSupportDriveSpeed();
Assert.Equal(expected, actual);
}
/// <summary>
/// Check that all systems with reversed ringcodes are marked properly
/// </summary>
/// <param name="redumpSystem">RedumpSystem value to check</param>
/// <param name="expected">The expected value to come from the check</param>
[Theory]
[MemberData(nameof(GenerateReversedRingcodeSystemsTestData))]
public void HasReversedRingcodesTest(RedumpSystem? redumpSystem, bool expected)
{
bool actual = redumpSystem.HasReversedRingcodes();
Assert.Equal(expected, actual);
}
/// <summary>
/// Check that all audio systems are marked properly
/// </summary>
/// <param name="redumpSystem">RedumpSystem value to check</param>
/// <param name="expected">The expected value to come from the check</param>
[Theory]
[MemberData(nameof(GenerateAudioSystemsTestData))]
public void IsAudioTest(RedumpSystem? redumpSystem, bool expected)
{
bool actual = redumpSystem.IsAudio();
Assert.Equal(expected, actual);
}
/// <summary>
/// Check that all marker systems are marked properly
/// </summary>
/// <param name="redumpSystem">RedumpSystem value to check</param>
/// <param name="expected">The expected value to come from the check</param>
[Theory]
[MemberData(nameof(GenerateMarkerSystemsTestData))]
public void IsMarkerTest(RedumpSystem? redumpSystem, bool expected)
{
bool actual = redumpSystem.IsMarker();
Assert.Equal(expected, actual);
}
/// <summary>
/// Check that all XGD systems are marked properly
/// </summary>
/// <param name="redumpSystem">RedumpSystem value to check</param>
/// <param name="expected">The expected value to come from the check</param>
[Theory]
[MemberData(nameof(GenerateXGDSystemsTestData))]
public void IsXGDTest(RedumpSystem? redumpSystem, bool expected)
{
bool actual = redumpSystem.IsXGD();
Assert.Equal(expected, actual);
}
/// <summary>
/// Generate a test set of MediaType values that support drive speeds
/// </summary>
/// <returns>MemberData-compatible list of MediaType values</returns>
public static List<object[]> GenerateSupportDriveSpeedsTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
foreach (MediaType mediaType in Enum.GetValues(typeof(MediaType)))
{
if (_supportDriveSpeeds.Contains(mediaType))
testData.Add(new object[] { mediaType, true });
else
testData.Add(new object[] { mediaType, false });
}
return testData;
}
/// <summary>
/// Generate a test set of RedumpSystem values that are considered Audio
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
public static List<object[]> GenerateAudioSystemsTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_audioSystems.Contains(redumpSystem))
testData.Add(new object[] { redumpSystem, true });
else
testData.Add(new object[] { redumpSystem, false });
}
return testData;
}
/// <summary>
/// Generate a test set of RedumpSystem values that are considered markers
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
public static List<object[]> GenerateMarkerSystemsTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_markerSystems.Contains(redumpSystem))
testData.Add(new object[] { redumpSystem, true });
else
testData.Add(new object[] { redumpSystem, false });
}
return testData;
}
/// <summary>
/// Generate a test set of RedumpSystem values that are considered markers
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
public static List<object[]> GenerateReversedRingcodeSystemsTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_reverseRingcodeSystems.Contains(redumpSystem))
testData.Add(new object[] { redumpSystem, true });
else
testData.Add(new object[] { redumpSystem, false });
}
return testData;
}
/// <summary>
/// Generate a test set of RedumpSystem values that are considered XGD
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
public static List<object[]> GenerateXGDSystemsTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_xgdSystems.Contains(redumpSystem))
testData.Add(new object[] { redumpSystem, true });
else
testData.Add(new object[] { redumpSystem, false });
}
return testData;
}
}
}

View File

@@ -0,0 +1,31 @@
using System.IO;
using MPF.Core.Data;
using MPF.Library;
using RedumpLib.Data;
using Xunit;
namespace MPF.Test.Library
{
public class DumpEnvironmentTests
{
[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, RedumpSystem.IBMPCcompatible, mediaType, parameters);
bool actual = env.ParametersValid();
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,196 @@
using System.Collections.Generic;
using MPF.Library;
using RedumpLib.Data;
using Xunit;
namespace MPF.Test.Library
{
public class InfoToolTests
{
[Theory]
[InlineData(null, 0, 0, 0, 0, null)]
[InlineData(null, 12345, 0, 0, 0, null)]
[InlineData(null, 12345, 1, 0, 0, null)]
[InlineData(null, 12345, 1, 2, 0, null)]
[InlineData(null, 12345, 1, 2, 3, null)]
[InlineData(MediaType.CDROM, 0, 0, 0, 0, "CD-ROM")]
[InlineData(MediaType.CDROM, 12345, 0, 0, 0, "CD-ROM")]
[InlineData(MediaType.CDROM, 12345, 1, 0, 0, "CD-ROM")]
[InlineData(MediaType.CDROM, 12345, 1, 2, 0, "CD-ROM")]
[InlineData(MediaType.CDROM, 12345, 1, 2, 3, "CD-ROM")]
[InlineData(MediaType.DVD, 0, 0, 0, 0, "DVD-ROM-5")]
[InlineData(MediaType.DVD, 12345, 0, 0, 0, "DVD-ROM-5")]
[InlineData(MediaType.DVD, 12345, 1, 0, 0, "DVD-ROM-9")]
[InlineData(MediaType.DVD, 12345, 1, 2, 0, "DVD-ROM-9")]
[InlineData(MediaType.DVD, 12345, 1, 2, 3, "DVD-ROM-9")]
[InlineData(MediaType.BluRay, 0, 0, 0, 0, "BD-ROM-25")]
[InlineData(MediaType.BluRay, 12345, 0, 0, 0, "BD-ROM-25")]
[InlineData(MediaType.BluRay, 26_843_531_857, 0, 0, 0, "BD-ROM-33")]
[InlineData(MediaType.BluRay, 12345, 1, 0, 0, "BD-ROM-50")]
[InlineData(MediaType.BluRay, 53_687_063_713, 1, 0, 0, "BD-ROM-66")]
[InlineData(MediaType.BluRay, 12345, 1, 2, 0, "BD-ROM-100")]
[InlineData(MediaType.BluRay, 12345, 1, 2, 3, "BD-ROM-128")]
[InlineData(MediaType.UMD, 0, 0, 0, 0, "UMD-SL")]
[InlineData(MediaType.UMD, 12345, 0, 0, 0, "UMD-SL")]
[InlineData(MediaType.UMD, 12345, 1, 0, 0, "UMD-DL")]
[InlineData(MediaType.UMD, 12345, 1, 2, 0, "UMD-DL")]
[InlineData(MediaType.UMD, 12345, 1, 2, 3, "UMD-DL")]
public void GetFixedMediaTypeTest(
MediaType? mediaType,
long size,
long layerbreak,
long layerbreak2,
long layerbreak3,
string expected)
{
string actual = InfoTool.GetFixedMediaType(mediaType, size, layerbreak, layerbreak2, layerbreak3);
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 NormalizeOutputPathsTest(string outputDirectory, string outputFilename, string expectedOutputDirectory, string expectedOutputFilename)
{
(string actualOutputDirectory, string actualOutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename);
Assert.Equal(expectedOutputDirectory, actualOutputDirectory);
Assert.Equal(expectedOutputFilename, actualOutputFilename);
}
[Fact]
public void ProcessSpecialFieldsCompleteTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = "This is a comments line\n[T:ISBN] ISBN Value",
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
{
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
},
Contents = "This is a contents line\n[T:GF] Game Footage",
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
{
[SiteCode.Patches] = "1.04 patch",
},
}
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
Assert.Null(info.CommonDiscInfo.CommentsSpecialFields);
Assert.NotNull(info.CommonDiscInfo.Contents);
Assert.Null(info.CommonDiscInfo.ContentsSpecialFields);
// Split the values
string[] splitComments = info.CommonDiscInfo.Comments.Split('\n');
string[] splitContents = info.CommonDiscInfo.Contents.Split('\n');
// Validate the lines
Assert.Equal(3, splitComments.Length);
Assert.Equal(5, splitContents.Length);
}
[Fact]
public void ProcessSpecialFieldsNullObjectTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
{
CommonDiscInfo = null,
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
// Validate
Assert.Null(info.CommonDiscInfo);
}
[Fact]
public void ProcessSpecialFieldsNullCommentsContentsTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = null,
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
{
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
},
Contents = null,
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
{
[SiteCode.Patches] = "1.04 patch",
},
}
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
Assert.Null(info.CommonDiscInfo.CommentsSpecialFields);
Assert.NotNull(info.CommonDiscInfo.Contents);
Assert.Null(info.CommonDiscInfo.ContentsSpecialFields);
// Split the values
string[] splitComments = info.CommonDiscInfo.Comments.Split('\n');
string[] splitContents = info.CommonDiscInfo.Contents.Split('\n');
// Validate the lines
Assert.Single(splitComments);
Assert.Equal(2, splitContents.Length);
}
[Fact]
public void ProcessSpecialFieldsNullDictionariesTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = "This is a comments line\n[T:ISBN] ISBN Value",
CommentsSpecialFields = null,
Contents = "This is a contents line\n[T:GF] Game Footage",
ContentsSpecialFields = null,
}
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
Assert.Null(info.CommonDiscInfo.CommentsSpecialFields);
Assert.NotNull(info.CommonDiscInfo.Contents);
Assert.Null(info.CommonDiscInfo.ContentsSpecialFields);
// Split the values
string[] splitComments = info.CommonDiscInfo.Comments.Split('\n');
string[] splitContents = info.CommonDiscInfo.Contents.Split('\n');
// Validate the lines
Assert.Equal(2, splitComments.Length);
Assert.Equal(2, splitContents.Length);
}
}
}

View File

@@ -0,0 +1,271 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Library;
using Xunit;
namespace MPF.Test.Library
{
public class ProtectionTests
{
[Fact]
public void SanitizeFoundProtectionsActiveMARKTest()
{
List<string> protections = new List<string>()
{
"ActiveMARK",
"ActiveMARK 5",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("ActiveMARK 5", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsCactusDataShieldTest()
{
List<string> protections = new List<string>()
{
"Cactus Data Shield 200",
"Cactus Data Shield 200 (Build 3.0.100a)",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Cactus Data Shield 200 (Build 3.0.100a)", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsCDCheckTest()
{
List<string> protections = new List<string>()
{
"Anything Else Protection",
"Executable-Based CD Check",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsCDCopsTest()
{
List<string> protections = new List<string>()
{
"CD-Cops",
"CD-Cops v1.2.0",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("CD-Cops v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsCDKeyTest()
{
List<string> protections = new List<string>()
{
"Anything Else Protection",
"CD-Key / Serial",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsEACdKeyTest()
{
List<string> protections = new List<string>()
{
"EA CdKey Registration Module",
"EA CdKey Registration Module v1.2.0",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("EA CdKey Registration Module v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsEADRMTest()
{
List<string> protections = new List<string>()
{
"EA DRM Protection",
"EA DRM Protection v1.2.0",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("EA DRM Protection v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsGFWLTest()
{
List<string> protections = new List<string>()
{
"Games for Windows LIVE",
"Games for Windows LIVE v1.2.0",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Games for Windows LIVE v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsGFWLZDPPTest()
{
List<string> protections = new List<string>()
{
"Games for Windows LIVE",
"Games for Windows LIVE Zero Day Piracy Protection",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Games for Windows LIVE, Games for Windows LIVE Zero Day Piracy Protection", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsImpulseReactorTest()
{
List<string> protections = new List<string>()
{
"Impulse Reactor",
"Impulse Reactor Core Module v1.2.0",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Impulse Reactor Core Module v1.2.0", sanitized);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public void SanitizeFoundProtectionsJoWoodXProtTest(int skip)
{
List<string> protections = new List<string>()
{
"JoWood X-Prot 1.2.0.00",
"JoWood X-Prot v2",
"JoWood X-Prot v1.4+",
"JoWood X-Prot v1.0-v1.3",
"JoWood X-Prot",
};
// Safeguard for the future
if (skip >= protections.Count)
throw new ArgumentException("Invalid skip value", nameof(skip));
// The list is in order of preference
protections = protections.Skip(skip).ToList();
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal(protections[0], sanitized);
}
[Fact]
public void SanitizeFoundProtectionsOnlineRegistrationTest()
{
List<string> protections = new List<string>()
{
"Anything Else Protection",
"Executable-Based Online Registration",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
[InlineData(4)]
[InlineData(5)]
[InlineData(6)]
[InlineData(7)]
[InlineData(8)]
public void SanitizeFoundProtectionsSafeDiscTest(int skip)
{
List<string> protections = new List<string>()
{
"SafeDisc 1.20.000",
"SafeDisc (drvmgt.dll) 1.2.0",
"SafeDisc (secdrv.sys) 1.2.0",
"SafeDisc (dplayerx.dll) 1.2.0",
"SafeDisc 3.20-4.xx (version removed)",
"SafeDisc 2",
"SafeDisc 1/Lite",
"SafeDisc Lite",
"SafeDisc 1-3",
"SafeDisc",
};
// Safeguard for the future
if (skip >= protections.Count)
throw new ArgumentException("Invalid skip value", nameof(skip));
// The list is in order of preference
protections = protections.Skip(skip).ToList();
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal(protections[0], sanitized);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public void SanitizeFoundProtectionStarForceTest(int skip)
{
List<string> protections = new List<string>()
{
"StarForce 1.20.000.000",
"StarForce 5 [Protected Module]",
"StarForce 5",
"StarForce 3-5",
"StarForce",
};
// Safeguard for the future
if (skip >= protections.Count)
throw new ArgumentException("Invalid skip value", nameof(skip));
// The list is in order of preference
protections = protections.Skip(skip).ToList();
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal(protections[0], sanitized);
}
[Fact]
public void SanitizeFoundProtectionsSysiphusTest()
{
List<string> protections = new List<string>()
{
"Sysiphus",
"Sysiphus v1.2.0",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Sysiphus v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsXCPTest()
{
List<string> protections = new List<string>()
{
"XCP",
"XCP v1.2.0",
};
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("XCP v1.2.0", sanitized);
}
}
}

View File

@@ -1,25 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>net48;netcoreapp3.1;net5.0-windows</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>
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
<DefineConstants>NET_FRAMEWORK</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
<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>
<PackageReference Include="Microsoft.CodeCoverage" Version="16.11.0-release-20210626-04" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0-release-20210626-04" />
<ProjectReference Include="..\MPF\MPF.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" />

View File

@@ -0,0 +1,165 @@
using System.Collections.Generic;
using MPF.Core.Data;
using MPF.Modules.DiscImageCreator;
using RedumpLib.Data;
using Xunit;
namespace MPF.Test.Modules
{
public class DiscImageCreatorTests
{
#region Old Tests
[Theory]
[InlineData(RedumpSystem.MicrosoftXbox, MediaType.CDROM, CommandStrings.CompactDisc)]
[InlineData(RedumpSystem.MicrosoftXbox, MediaType.DVD, CommandStrings.XBOX)]
[InlineData(RedumpSystem.MicrosoftXbox, MediaType.LaserDisc, null)]
[InlineData(RedumpSystem.SonyPlayStation3, MediaType.BluRay, CommandStrings.BluRay)]
[InlineData(RedumpSystem.AppleMacintosh, MediaType.FloppyDisk, CommandStrings.Floppy)]
[InlineData(RedumpSystem.RawThrillsVarious, MediaType.GDROM, null)]
public void ParametersFromSystemAndTypeTest(RedumpSystem? 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(RedumpSystem.AppleMacintosh, MediaType.LaserDisc, true, 20, null, null)]
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, false, 20, null, new string[] { FlagStrings.Raw })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, 20, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
/* paranoid mode tests */
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, 1000, 2, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
[InlineData(RedumpSystem.AppleMacintosh, MediaType.CDROM, false, 20, null, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
/* reread c2 */
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, false, 1000, null, new string[] { FlagStrings.C2Opcode })]
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, false, -1, null, new string[] { FlagStrings.C2Opcode })]
public void ParametersFromOptionsTest(RedumpSystem? 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 ?? new string[0]);
Assert.Equal(expectedSet, actualSet);
if (rereadC2 == -1 || !knownSystem.MediaTypes().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());
}
[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 = MPF.Modules.DiscImageCreator.Converters.Extension(mediaType);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(CommandStrings.Audio, MediaType.CDROM)]
[InlineData(CommandStrings.BluRay, MediaType.BluRay)]
[InlineData(CommandStrings.Close, null)]
[InlineData(CommandStrings.CompactDisc, MediaType.CDROM)]
[InlineData(CommandStrings.Data, MediaType.CDROM)]
[InlineData(CommandStrings.DigitalVideoDisc, MediaType.DVD)]
[InlineData(CommandStrings.Eject, null)]
[InlineData(CommandStrings.Floppy, MediaType.FloppyDisk)]
[InlineData(CommandStrings.GDROM, MediaType.GDROM)]
[InlineData(CommandStrings.MDS, null)]
[InlineData(CommandStrings.Reset, null)]
[InlineData(CommandStrings.SACD, MediaType.CDROM)]
[InlineData(CommandStrings.Start, null)]
[InlineData(CommandStrings.Stop, null)]
[InlineData(CommandStrings.Sub, null)]
[InlineData(CommandStrings.Swap, MediaType.GDROM)]
[InlineData(CommandStrings.XBOX, MediaType.DVD)]
public void BaseCommandToMediaTypeTest(string command, MediaType? expected)
{
MediaType? actual = MPF.Modules.DiscImageCreator.Converters.ToMediaType(command);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(CommandStrings.Audio, RedumpSystem.AudioCD)]
[InlineData(CommandStrings.BluRay, RedumpSystem.SonyPlayStation3)]
[InlineData(CommandStrings.Close, null)]
[InlineData(CommandStrings.CompactDisc, RedumpSystem.IBMPCcompatible)]
[InlineData(CommandStrings.Data, RedumpSystem.IBMPCcompatible)]
[InlineData(CommandStrings.DigitalVideoDisc, RedumpSystem.IBMPCcompatible)]
[InlineData(CommandStrings.Eject, null)]
[InlineData(CommandStrings.Floppy, RedumpSystem.IBMPCcompatible)]
[InlineData(CommandStrings.GDROM, RedumpSystem.SegaDreamcast)]
[InlineData(CommandStrings.MDS, null)]
[InlineData(CommandStrings.Reset, null)]
[InlineData(CommandStrings.SACD, RedumpSystem.SuperAudioCD)]
[InlineData(CommandStrings.Start, null)]
[InlineData(CommandStrings.Stop, null)]
[InlineData(CommandStrings.Sub, null)]
[InlineData(CommandStrings.Swap, RedumpSystem.SegaDreamcast)]
[InlineData(CommandStrings.XBOX, RedumpSystem.MicrosoftXbox)]
public void BaseCommandToRedumpSystemTest(string command, RedumpSystem? expected)
{
RedumpSystem? actual = MPF.Modules.DiscImageCreator.Converters.ToRedumpSystem(command);
Assert.Equal(expected, actual);
}
#endregion
[Fact]
public void DiscImageCreatorAudioParametersTest()
{
string originalParameters = "audio F \"ISO\\Audio CD\\Audio CD.bin\" 72 -5 0";
// Validate that a common audio commandline is parsed
var parametersObject = new Parameters(originalParameters);
Assert.NotNull(parametersObject);
// Validate that the same set of parameters are generated on the output
string newParameters = parametersObject.GenerateParameters();
Assert.NotNull(newParameters);
Assert.Equal(originalParameters, newParameters);
}
[Fact]
public void DiscImageCreatorDataParametersTest()
{
string originalParameters = "data F \"ISO\\Data CD\\Data CD.bin\" 72 -5 0";
// Validate that a common audio commandline is parsed
var parametersObject = new Parameters(originalParameters);
Assert.NotNull(parametersObject);
// Validate that the same set of parameters are generated on the output
string newParameters = parametersObject.GenerateParameters();
Assert.NotNull(newParameters);
Assert.Equal(originalParameters, newParameters);
}
}
}

View File

@@ -0,0 +1,723 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using RedumpLib.Data;
using Xunit;
namespace MPF.Test.RedumpLib
{
// TODO: Add tests for string-to-enum conversion
public class ExtensionsTests
{
#region Cross-Enumeration
/// <summary>
/// DiscType values that map to MediaType
/// </summary>
private static readonly DiscType?[] _mappableDiscTypes = new DiscType?[]
{
DiscType.BD25,
DiscType.BD50,
DiscType.CD,
DiscType.DVD5,
DiscType.DVD9,
DiscType.GDROM,
DiscType.HDDVDSL,
DiscType.NintendoGameCubeGameDisc,
DiscType.NintendoWiiOpticalDiscSL,
DiscType.NintendoWiiOpticalDiscDL,
DiscType.NintendoWiiUOpticalDiscSL,
DiscType.UMDSL,
DiscType.UMDDL,
};
/// <summary>
/// MediaType values that map to DiscType
/// </summary>
private static readonly MediaType?[] _mappableMediaTypes = new MediaType?[]
{
MediaType.BluRay,
MediaType.CDROM,
MediaType.DVD,
MediaType.GDROM,
MediaType.HDDVD,
MediaType.NintendoGameCubeGameDisc,
MediaType.NintendoWiiOpticalDisc,
MediaType.NintendoWiiUOpticalDisc,
MediaType.UMD,
};
/// <summary>
/// Check that every supported system has some set of MediaTypes supported
/// </summary>
/// <param name="redumpSystem">RedumpSystem value to check</param>
[Theory]
[MemberData(nameof(GenerateRedumpSystemMappingTestData))]
public void MediaTypesTest(RedumpSystem? redumpSystem)
{
var actual = redumpSystem.MediaTypes();
Assert.NotEmpty(actual);
}
/// <summary>
/// Check that both mappable and unmappable media types output correctly
/// </summary>
/// <param name="mediaType">MediaType value to check</param>
/// <param name="expectNull">True to expect a null mapping, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateMediaTypeMappingTestData))]
public void ToDiscTypeTest(MediaType? mediaType, bool expectNull)
{
DiscType? actual = mediaType.ToDiscType();
Assert.Equal(expectNull, actual == null);
}
/// <summary>
/// Check that DiscType values all map to something appropriate
/// </summary>
/// <param name="discType">DiscType value to check</param>
/// <param name="expectNull">True to expect a null mapping, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscTypeMappingTestData))]
public void ToMediaTypeTest(DiscType? discType, bool expectNull)
{
MediaType? actual = discType.ToMediaType();
Assert.Equal(expectNull, actual == null);
}
/// <summary>
/// Generate a test set of DiscType values
/// </summary>
/// <returns>MemberData-compatible list of DiscType values</returns>
public static List<object[]> GenerateDiscTypeMappingTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (DiscType? discType in Enum.GetValues(typeof(DiscType)))
{
if (_mappableDiscTypes.Contains(discType))
testData.Add(new object[] { discType, false });
else
testData.Add(new object[] { discType, true });
}
return testData;
}
/// <summary>
/// Generate a test set of RedumpSystem values
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
public static List<object[]> GenerateRedumpSystemMappingTestData()
{
var testData = new List<object[]>() { new object[] { null } };
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
testData.Add(new object[] { redumpSystem });
}
return testData;
}
/// <summary>
/// Generate a test set of mappable media types
/// </summary>
/// <returns>MemberData-compatible list of MediaTypes</returns>
public static List<object[]> GenerateMediaTypeMappingTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
{
if (_mappableMediaTypes.Contains(mediaType))
testData.Add(new object[] { mediaType, false });
else
testData.Add(new object[] { mediaType, true });
}
return testData;
}
#endregion
#region Disc Category
/// <summary>
/// Check that every DiscCategory has a long name provided
/// </summary>
/// <param name="discCategory">DiscCategory value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscCategoryTestData))]
public void DiscCategoryLongNameTest(DiscCategory? discCategory, bool expectNull)
{
string actual = discCategory.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of DiscCategory values
/// </summary>
/// <returns>MemberData-compatible list of DiscCategory values</returns>
public static List<object[]> GenerateDiscCategoryTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (DiscCategory? discCategory in Enum.GetValues(typeof(DiscCategory)))
{
testData.Add(new object[] { discCategory, false });
}
return testData;
}
#endregion
#region Disc Type
/// <summary>
/// Check that every DiscType has a long name provided
/// </summary>
/// <param name="discType">DiscType value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDiscTypeTestData))]
public void DiscTypeLongNameTest(DiscType? discType, bool expectNull)
{
string actual = discType.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of DiscType values
/// </summary>
/// <returns>MemberData-compatible list of DiscType values</returns>
public static List<object[]> GenerateDiscTypeTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (DiscType? discType in Enum.GetValues(typeof(DiscType)))
{
if (discType == DiscType.NONE)
testData.Add(new object[] { discType, true });
else
testData.Add(new object[] { discType, false });
}
return testData;
}
#endregion
#region Language
/// <summary>
/// Check that every Language has a long name provided
/// </summary>
/// <param name="language">Language value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateLanguageTestData))]
public void LanguageLongNameTest(Language? language, bool expectNull)
{
string actual = language.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Check that every Language has a short name provided
/// </summary>
/// <param name="language">Language value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateLanguageTestData))]
public void LanguageShortNameTest(Language? language, bool expectNull)
{
string actual = language.ShortName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Ensure that every Language that has an ISO 639-1 code is unique
/// </summary>
[Fact]
public void LanguageNoDuplicateTwoLetterCodeTest()
{
var fullLanguages = Enum.GetValues(typeof(Language)).Cast<Language?>().ToList();
var filteredLanguages = new Dictionary<string, Language?>();
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
string code = language.TwoLetterCode();
if (string.IsNullOrWhiteSpace(code))
continue;
// Throw if the code already exists
if (filteredLanguages.ContainsKey(code))
throw new DuplicateNameException($"Code {code} already in dictionary");
filteredLanguages[code] = language;
totalCount++;
}
Assert.Equal(totalCount, filteredLanguages.Count);
}
/// <summary>
/// Ensure that every Language that has a standard/bibliographic ISO 639-2 code is unique
/// </summary>
[Fact]
public void LanguageNoDuplicateThreeLetterCodeTest()
{
var fullLanguages = Enum.GetValues(typeof(Language)).Cast<Language?>().ToList();
var filteredLanguages = new Dictionary<string, Language?>();
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
string code = language.ThreeLetterCode();
if (string.IsNullOrWhiteSpace(code))
continue;
// Throw if the code already exists
if (filteredLanguages.ContainsKey(code))
throw new DuplicateNameException($"Code {code} already in dictionary");
filteredLanguages[code] = language;
totalCount++;
}
Assert.Equal(totalCount, filteredLanguages.Count);
}
/// <summary>
/// Ensure that every Language that has a terminology ISO 639-2 code is unique
/// </summary>
[Fact]
public void LanguageNoDuplicateThreeLetterCodeAltTest()
{
var fullLanguages = Enum.GetValues(typeof(Language)).Cast<Language?>().ToList();
var filteredLanguages = new Dictionary<string, Language?>();
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
string code = language.ThreeLetterCodeAlt();
if (string.IsNullOrWhiteSpace(code))
continue;
// Throw if the code already exists
if (filteredLanguages.ContainsKey(code))
throw new DuplicateNameException($"Code {code} already in dictionary");
filteredLanguages[code] = language;
totalCount++;
}
Assert.Equal(totalCount, filteredLanguages.Count);
}
/// <summary>
/// Generate a test set of Language values
/// </summary>
/// <returns>MemberData-compatible list of Language values</returns>
public static List<object[]> GenerateLanguageTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (Language? language in Enum.GetValues(typeof(Language)))
{
testData.Add(new object[] { language, false });
}
return testData;
}
#endregion
#region Language Selection
/// <summary>
/// Check that every LanguageSelection has a long name provided
/// </summary>
/// <param name="languageSelection">LanguageSelection value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateLanguageSelectionTestData))]
public void LanguageSelectionLongNameTest(LanguageSelection? languageSelection, bool expectNull)
{
string actual = languageSelection.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of LanguageSelection values
/// </summary>
/// <returns>MemberData-compatible list of LanguageSelection values</returns>
public static List<object[]> GenerateLanguageSelectionTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (LanguageSelection? languageSelection in Enum.GetValues(typeof(LanguageSelection)))
{
testData.Add(new object[] { languageSelection, false });
}
return testData;
}
#endregion
#region Media Type
/// <summary>
/// Check that every MediaType has a long name provided
/// </summary>
/// <param name="mediaType">MediaType value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateMediaTypeTestData))]
public void MediaTypeLongNameTest(MediaType? mediaType, bool expectNull)
{
string actual = mediaType.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Check that every MediaType has a short name provided
/// </summary>
/// <param name="mediaType">MediaType value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateMediaTypeTestData))]
public void MediaTypeShortNameTest(MediaType? mediaType, bool expectNull)
{
string actual = mediaType.ShortName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of MediaType values
/// </summary>
/// <returns>MemberData-compatible list of MediaType values</returns>
public static List<object[]> GenerateMediaTypeTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
{
testData.Add(new object[] { mediaType, false });
}
return testData;
}
#endregion
#region Region
/// <summary>
/// Check that every Region has a long name provided
/// </summary>
/// <param name="region">Region value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateRegionTestData))]
public void RegionLongNameTest(Region? region, bool expectNull)
{
string actual = region.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Check that every Region has a short name provided
/// </summary>
/// <param name="region">Region value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateRegionTestData))]
public void RegionShortNameTest(Region? region, bool expectNull)
{
string actual = region.ShortName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Ensure that every Language that has an ISO 639-1 code is unique
/// </summary>
[Fact]
public void RegionNoDuplicateShortNameTest()
{
var fullRegions = Enum.GetValues(typeof(Region)).Cast<Region?>().ToList();
var filteredRegions = new Dictionary<string, Region?>();
int totalCount = 0;
foreach (Region? region in fullRegions)
{
string code = region.ShortName();
if (string.IsNullOrWhiteSpace(code))
continue;
// Throw if the code already exists
if (filteredRegions.ContainsKey(code))
throw new DuplicateNameException($"Code {code} already in dictionary");
filteredRegions[code] = region;
totalCount++;
}
Assert.Equal(totalCount, filteredRegions.Count);
}
/// <summary>
/// Generate a test set of Region values
/// </summary>
/// <returns>MemberData-compatible list of Region values</returns>
public static List<object[]> GenerateRegionTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (Region? region in Enum.GetValues(typeof(Region)))
{
testData.Add(new object[] { region, false });
}
return testData;
}
#endregion
#region Site Code
/// <summary>
/// Check that every SiteCode has a long name provided
/// </summary>
/// <param name="siteCode">SiteCode value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateSiteCodeTestData))]
public void SiteCodeLongNameTest(SiteCode? siteCode, bool expectNull)
{
string actual = siteCode.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Check that every SiteCode has a short name provided
/// </summary>
/// <param name="siteCode">SiteCode value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateSiteCodeTestData))]
public void SiteCodeShortNameTest(SiteCode? siteCode, bool expectNull)
{
string actual = siteCode.ShortName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of SiteCode values
/// </summary>
/// <returns>MemberData-compatible list of SiteCode values</returns>
public static List<object[]> GenerateSiteCodeTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (SiteCode? siteCode in Enum.GetValues(typeof(SiteCode)))
{
testData.Add(new object[] { siteCode, false });
}
return testData;
}
#endregion
#region System
/// <summary>
/// RedumpSystem values that are considered markers and not real systems
/// </summary>
private static readonly RedumpSystem?[] _markerSystemTypes = new RedumpSystem?[]
{
RedumpSystem.MarkerArcadeEnd,
RedumpSystem.MarkerComputerEnd,
RedumpSystem.MarkerDiscBasedConsoleEnd,
RedumpSystem.MarkerOtherEnd,
};
/// <summary>
/// Check that every RedumpSystem has a long name provided
/// </summary>
/// <param name="redumpSystem">RedumpSystem value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateRedumpSystemTestData))]
public void RedumpSystemLongNameTest(RedumpSystem? redumpSystem, bool expectNull)
{
string actual = redumpSystem.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
// TODO: Re-enable the following test once non-Redump systems are accounted for
/// <summary>
/// Check that every RedumpSystem has a short name provided
/// </summary>
/// <param name="redumpSystem">RedumpSystem value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
//[Theory]
//[MemberData(nameof(GenerateRedumpSystemTestData))]
//public void RedumpSystemShortNameTest(RedumpSystem? redumpSystem, bool expectNull)
//{
// string actual = redumpSystem.ShortName();
// if (expectNull)
// Assert.Null(actual);
// else
// Assert.NotNull(actual);
//}
// TODO: Test the other attributes as well
// Most are bool checks so they're not as interesting to have unit tests around
// SystemCategory always returns something as well, so is it worth testing?
/// <summary>
/// Generate a test set of RedumpSystem values
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
public static List<object[]> GenerateRedumpSystemTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
// We want to skip all markers for this
if (_markerSystemTypes.Contains(redumpSystem))
continue;
testData.Add(new object[] { redumpSystem, false });
}
return testData;
}
#endregion
#region System Category
/// <summary>
/// Check that every SystemCategory has a long name provided
/// </summary>
/// <param name="systemCategory">SystemCategory value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateSystemCategoryTestData))]
public void SystemCategoryLongNameTest(SystemCategory? systemCategory, bool expectNull)
{
string actual = systemCategory.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of SystemCategory values
/// </summary>
/// <returns>MemberData-compatible list of SystemCategory values</returns>
public static List<object[]> GenerateSystemCategoryTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
foreach (SystemCategory? systemCategory in Enum.GetValues(typeof(SystemCategory)))
{
if (systemCategory == SystemCategory.NONE)
testData.Add(new object[] { systemCategory, true });
else
testData.Add(new object[] { systemCategory, false });
}
return testData;
}
#endregion
#region Yes/No
/// <summary>
/// Check that every YesNo has a long name provided
/// </summary>
/// <param name="yesNo">YesNo value to check</param>
/// <param name="expectNull">True to expect a null value, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateYesNoTestData))]
public void YesNoLongNameTest(YesNo? yesNo, bool expectNull)
{
string actual = yesNo.LongName();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of YesNo values
/// </summary>
/// <returns>MemberData-compatible list of YesNo values</returns>
public static List<object[]> GenerateYesNoTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
foreach (YesNo? yesNo in Enum.GetValues(typeof(YesNo)))
{
testData.Add(new object[] { yesNo, false });
}
return testData;
}
#endregion
}
}

View File

@@ -0,0 +1,169 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using RedumpLib.Data;
using Xunit;
namespace MPF.Test.RedumpLib
{
public class SubmissionInfoTests
{
[Fact]
public void EmptySerializationTest()
{
var submissionInfo = new SubmissionInfo();
string json = JsonConvert.SerializeObject(submissionInfo, Formatting.Indented);
Assert.NotNull(json);
}
[Fact]
public void PartialSerializationTest()
{
var submissionInfo = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection(),
VersionAndEditions = new VersionAndEditionsSection(),
EDC = new EDCSection(),
ParentCloneRelationship = new ParentCloneRelationshipSection(),
Extras = new ExtrasSection(),
CopyProtection = new CopyProtectionSection(),
DumpersAndStatus = new DumpersAndStatusSection(),
TracksAndWriteOffsets = new TracksAndWriteOffsetsSection(),
SizeAndChecksums = new SizeAndChecksumsSection(),
};
string json = JsonConvert.SerializeObject(submissionInfo, Formatting.Indented);
Assert.NotNull(json);
}
[Fact]
public void FullSerializationTest()
{
var submissionInfo = new SubmissionInfo()
{
SchemaVersion = 1,
MatchedIDs = new List<int> { 0, 1, 2, 3 },
Added = DateTime.UtcNow,
LastModified = DateTime.UtcNow,
CommonDiscInfo = new CommonDiscInfoSection()
{
System = RedumpSystem.IBMPCcompatible,
Media = DiscType.CD,
Title = "Game Title",
ForeignTitleNonLatin = "Foreign Game Title",
DiscNumberLetter = "1",
DiscTitle = "Install Disc",
Category = DiscCategory.Games,
Region = Region.World,
Languages = new Language?[] { Language.English, Language.Spanish, Language.French },
LanguageSelection = new LanguageSelection?[] { LanguageSelection.BiosSettings },
Serial = "Disc Serial",
Layer0MasteringRing = "L0 Mastering Ring",
Layer0MasteringSID = "L0 Mastering SID",
Layer0ToolstampMasteringCode = "L0 Toolstamp",
Layer0MouldSID = "L0 Mould SID",
Layer0AdditionalMould = "L0 Additional Mould",
Layer1MasteringRing = "L1 Mastering Ring",
Layer1MasteringSID = "L1 Mastering SID",
Layer1ToolstampMasteringCode = "L1 Toolstamp",
Layer1MouldSID = "L1 Mould SID",
Layer1AdditionalMould = "L1 Additional Mould",
Layer2MasteringRing = "L2 Mastering Ring",
Layer2MasteringSID = "L2 Mastering SID",
Layer2ToolstampMasteringCode = "L2 Toolstamp",
Layer3MasteringRing = "L3 Mastering Ring",
Layer3MasteringSID = "L3 Mastering SID",
Layer3ToolstampMasteringCode = "L3 Toolstamp",
RingWriteOffset = "+12",
Barcode = "UPC Barcode",
EXEDateBuildDate = "19xx-xx-xx",
ErrorsCount = "0",
Comments = "Comment data line 1\r\nComment data line 2",
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
{
[SiteCode.ISBN] = "ISBN",
},
Contents = "Special contents 1\r\nSpecial contents 2",
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
{
[SiteCode.PlayableDemos] = "Game Demo 1",
},
},
VersionAndEditions = new VersionAndEditionsSection()
{
Version = "Original",
VersionDatfile = "Alt",
CommonEditions = new string[] { "Taikenban" },
OtherEditions = "Rerelease",
},
EDC = new EDCSection()
{
EDC = YesNo.Yes,
},
ParentCloneRelationship = new ParentCloneRelationshipSection()
{
ParentID = "12345",
RegionalParent = false,
},
Extras = new ExtrasSection()
{
PVD = "PVD",
DiscKey = "Disc key",
DiscID = "Disc ID",
PIC = "PIC",
Header = "Header",
BCA = "BCA",
SecuritySectorRanges = "SSv1 Ranges",
},
CopyProtection = new CopyProtectionSection()
{
AntiModchip = YesNo.Yes,
LibCrypt = YesNo.No,
LibCryptData = "LibCrypt data",
Protection = "List of protections",
SecuROMData = "SecuROM data",
},
DumpersAndStatus = new DumpersAndStatusSection()
{
Status = DumpStatus.TwoOrMoHumanReadablesGreen,
Dumpers = new string[] { "Dumper1", "Dumper2" },
OtherDumpers = "Dumper3",
},
TracksAndWriteOffsets = new TracksAndWriteOffsetsSection()
{
ClrMameProData = "Datfile",
Cuesheet = "Cuesheet",
CommonWriteOffsets = new int[] { 0, 12, -12 },
OtherWriteOffsets = "-2",
},
SizeAndChecksums = new SizeAndChecksumsSection()
{
Layerbreak = 0,
Layerbreak2 = 1,
Layerbreak3 = 2,
Size = 12345,
CRC32 = "CRC32",
MD5 = "MD5",
SHA1 = "SHA1",
},
Artifacts = new Dictionary<string, string>()
{
["Sample Artifact"] = "Sample Data",
},
};
string json = JsonConvert.SerializeObject(submissionInfo, Formatting.Indented);
Assert.NotNull(json);
}
}
}

View File

@@ -1,30 +0,0 @@
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

@@ -1,5 +1,6 @@
using System.Linq;
using MPF.Data;
using MPF.Core.Data;
using RedumpLib.Data;
using Xunit;
namespace MPF.Test.Data

View File

@@ -1,139 +0,0 @@
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

@@ -1,88 +0,0 @@
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

@@ -1,37 +0,0 @@
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

@@ -1,46 +0,0 @@
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

@@ -1,71 +0,0 @@
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

@@ -1,46 +0,0 @@
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);
}
}
}

30
MPF.sln
View File

@@ -18,6 +18,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
README.md = README.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RedumpLib", "RedumpLib\RedumpLib.csproj", "{13574913-A426-4644-9955-F49AD0876E5F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.CueSheets", "MPF.CueSheets\MPF.CueSheets.csproj", "{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CICMMetadataEditor", "CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj", "{E4271454-6217-4500-BC36-F8856AC7AD6B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Core", "MPF.Core\MPF.Core.csproj", "{70B1265D-FE49-472A-A83D-0B462152D37A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Modules", "MPF.Modules\MPF.Modules.csproj", "{8A4254BD-552F-4238-B8EB-D59AACD768B9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -40,6 +50,26 @@ Global
{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
{13574913-A426-4644-9955-F49AD0876E5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13574913-A426-4644-9955-F49AD0876E5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13574913-A426-4644-9955-F49AD0876E5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13574913-A426-4644-9955-F49AD0876E5F}.Release|Any CPU.Build.0 = Release|Any CPU
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Release|Any CPU.Build.0 = Release|Any CPU
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Release|Any CPU.Build.0 = Release|Any CPU
{70B1265D-FE49-472A-A83D-0B462152D37A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{70B1265D-FE49-472A-A83D-0B462152D37A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70B1265D-FE49-472A-A83D-0B462152D37A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{70B1265D-FE49-472A-A83D-0B462152D37A}.Release|Any CPU.Build.0 = Release|Any CPU
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -1,9 +1,13 @@
<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">
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
xmlns:windows="clr-namespace:MPF.Windows"
x:Class="MPF.App">
<Application.MainWindow>
<windows:MainWindow Visibility="Visible"/>
</Application.MainWindow>
<Application.Resources>
<!-- Button -->
@@ -53,7 +57,7 @@
</Setter.Value>
</Setter>
</Style>
<!-- ComboBox -->
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
@@ -569,7 +573,7 @@
</Setter.Value>
</Setter>
</Style>
<!-- TabItem -->
<LinearGradientBrush x:Key="TabItem.Static.Background" EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#F0F0F0" Offset="0.0"/>

View File

@@ -1,11 +1,60 @@
using System.Windows;
using MPF.Core.Data;
using MPF.GUI.ViewModels;
using MPF.Windows;
namespace MPF
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
/// <remarks>
/// This application is not fully MVVM. The following steps are needed to get there:
/// - Use commands instead of event handlers, where at all possible
/// - Reduce the amount of manual UI adjustments needed, instead binding to the view models
/// </remarks>
public partial class App : Application
{
/// <summary>
/// Static application instance for reference
/// </summary>
private static App _appInstance;
/// <summary>
/// Read-only access to the current main window
/// </summary>
public static MainWindow Instance => _appInstance.MainWindow as MainWindow;
/// <summary>
/// Read-only access to the current log window
/// </summary>
public static LogViewModel Logger => Instance.LogOutput.LogViewModel;
/// <summary>
/// Access to the current options
/// </summary>
public static Options Options
{
get => _options;
set
{
_options = value;
OptionsLoader.SaveToConfig(_options);
}
}
/// <summary>
/// Internal reference to Options
/// </summary>
private static Options _options;
/// <summary>
/// Constructor
/// </summary>
public App()
{
_appInstance = this;
_options = OptionsLoader.LoadFromConfig();
}
}
}

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Utilities;
using MPF.Core.Converters;
namespace MPF
{
@@ -22,7 +22,7 @@ namespace MPF
public static implicit operator T? (Element<T> item) => item?.Data;
/// <inheritdoc/>
public string Name => Converters.GetLongName(Data);
public string Name => EnumConverter.GetLongName(Data);
public override string ToString() => Name;

View File

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

View File

@@ -11,14 +11,14 @@ namespace MPF
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();
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);
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

Before

Width:  |  Height:  |  Size: 423 KiB

After

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>net48;netcoreapp3.1;net5.0-windows</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms>
@@ -13,7 +13,7 @@
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.1</Version>
<Version>2.3</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -27,10 +27,10 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.7.0" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.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="System.Configuration.ConfigurationManager" Version="6.0.0-rc.1.21451.13" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -42,15 +42,22 @@
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Icon.ico" />
<Resource Include="Images\ring-code-guide.png" />
<None Remove="Images\ring-code-guide-2-layer.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Icon.ico" />
<Resource Include="Images\ring-code-guide-2-layer.png" />
<Resource Include="Images\ring-code-guide-1-layer.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj">
<Project>{51ab0928-13f9-44bf-a407-b6957a43a056}</Project>
<Name>MPF.Library</Name>
</ProjectReference>
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup>

44
MPF/OptionsLoader.cs Normal file
View File

@@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Configuration;
using MPF.Core.Data;
namespace MPF
{
public static class OptionsLoader
{
/// <summary>
/// Load the current set of options from the application configuration
/// </summary>
public static Options LoadFromConfig()
{
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var settings = configFile.AppSettings.Settings;
var dict = new Dictionary<string, string>();
foreach (string key in settings.AllKeys)
{
dict[key] = settings[key]?.Value ?? string.Empty;
}
return new Options(dict);
}
/// <summary>
/// Save the current set of options to the application configuration
/// </summary>
public static void SaveToConfig(Options options)
{
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// Loop through all settings in Options and save them, overwriting existing settings
foreach (var kvp in options)
{
configFile.AppSettings.Settings.Remove(kvp.Key);
configFile.AppSettings.Settings.Add(kvp.Key, kvp.Value);
}
configFile.Save(ConfigurationSaveMode.Modified);
}
}
}

View File

@@ -14,16 +14,16 @@
</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 Name="OutputViewer" Template="{DynamicResource CustomScrollViewerControlStyle}">
<RichTextBox Name="Output" FontFamily="Consolas" Background="#FF202020" IsReadOnly="true" />
</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}" />
<Button Name="ClearButton" Height="25" Width="80" Content="Clear" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="SaveButton" Height="25" Width="80" Content="Save" Style="{DynamicResource CustomButtonStyle}" />
<Label/> <!-- Empty label for padding -->
</UniformGrid>
</GroupBox>

View File

@@ -1,546 +1,19 @@
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;
using System.Windows.Controls;
using MPF.GUI.ViewModels;
namespace MPF.UserControls
{
public partial class LogOutput : UserControl
{
/// <summary>
/// Paragraph backing the log
/// Read-only access to the current log view model
/// </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 LogViewModel LogViewModel => DataContext as LogViewModel;
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);
DataContext = new LogViewModel(this);
}
#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

@@ -2,19 +2,29 @@
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">
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" x:Name="userInput">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="0.75*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Content="{Binding ElementName=userInput, Path=Label}" />
<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}"/>
<TextBox Grid.Row="0" Grid.Column="1"
Height="{Binding ElementName=userInput, Path=TextHeight}"
HorizontalAlignment="Stretch"
Text="{Binding ElementName=userInput, Path=Text}"
AcceptsTab="{Binding ElementName=userInput, Path=Tab}"
AcceptsReturn="{Binding ElementName=userInput, Path=Enter}"
TextWrapping="{Binding ElementName=userInput, Path=TextWrapping}"
IsReadOnly="{Binding ElementName=userInput, Path=IsReadOnly}"
VerticalContentAlignment="{Binding ElementName=userInput, Path=VerticalContentAlignmentValue}"
HorizontalScrollBarVisibility="{Binding ElementName=userInput, Path=HorizontalScrollBarVisibility}"
VerticalScrollBarVisibility="{Binding ElementName=userInput, Path=VerticalScrollBarVisibility}"/>
</Grid>
</UserControl>

View File

@@ -8,19 +8,118 @@ namespace MPF.UserControls
/// </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;
#region Dependency Properties
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof(string), typeof(UserInput));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(UserInput));
public static readonly DependencyProperty TextHeightProperty =
DependencyProperty.Register("TextHeight", typeof(string), typeof(UserInput));
public static readonly DependencyProperty TabProperty =
DependencyProperty.Register("Tab", typeof(bool), typeof(UserInput));
public static readonly DependencyProperty EnterProperty =
DependencyProperty.Register("Enter", typeof(bool), typeof(UserInput));
public static readonly DependencyProperty TextWrappingProperty =
DependencyProperty.Register("TextWrapping", typeof(TextWrapping), typeof(UserInput));
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(UserInput));
public static readonly DependencyProperty VerticalContentAlignmentValueProperty =
DependencyProperty.Register("VerticalContentAlignmentValue", typeof(VerticalAlignment), typeof(UserInput));
public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty =
DependencyProperty.Register("HorizontalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(UserInput));
public static readonly DependencyProperty VerticalScrollBarVisibilityProperty =
DependencyProperty.Register("VerticalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(UserInput));
#endregion
#region Properties
public string Label
{
get => (string)GetValue(LabelProperty);
set => SetValue(LabelProperty, value);
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public string TextHeight
{
get => (string)GetValue(TextHeightProperty);
set => SetValue(TextHeightProperty, value);
}
public bool Tab
{
get => (bool)GetValue(TabProperty);
set => SetValue(TabProperty, value);
}
public bool Enter
{
get => (bool)GetValue(EnterProperty);
set => SetValue(EnterProperty, value);
}
public TextWrapping TextWrapping
{
get => (TextWrapping)GetValue(TextWrappingProperty);
set => SetValue(TextWrappingProperty, value);
}
public bool IsReadOnly
{
get => (bool)GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, value);
}
public VerticalAlignment VerticalContentAlignmentValue
{
get => (VerticalAlignment)GetValue(VerticalContentAlignmentValueProperty);
set => SetValue(VerticalContentAlignmentValueProperty, value);
}
public ScrollBarVisibility HorizontalScrollBarVisibility
{
get => (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty);
set => SetValue(HorizontalScrollBarVisibilityProperty, value);
}
public ScrollBarVisibility VerticalScrollBarVisibility
{
get => (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty);
set => SetValue(VerticalScrollBarVisibilityProperty, value);
}
#endregion
public UserInput()
{
// Set default values
TextHeight = "22";
Tab = false;
Enter = false;
TextWrapping = TextWrapping.NoWrap;
IsReadOnly = false;
VerticalContentAlignmentValue = VerticalAlignment.Center;
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
InitializeComponent();
DataContext = this;
}
}
}

View File

@@ -1,38 +0,0 @@
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,627 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using MPF.Core.Utilities;
using MPF.Windows;
using RedumpLib.Data;
namespace MPF.GUI.ViewModels
{
public class DiscInformationViewModel
{
#region Fields
/// <summary>
/// Parent DiscInformationWindow object
/// </summary>
public DiscInformationWindow Parent { get; private set; }
/// <summary>
/// SubmissionInfo object to fill and save
/// </summary>
public SubmissionInfo SubmissionInfo { get; private set; }
#endregion
#region Lists
/// <summary>
/// List of available disc categories
/// </summary>
public List<Element<DiscCategory>> Categories { get; private set; } = Element<DiscCategory>.GenerateElements().ToList();
/// <summary>
/// List of available regions
/// </summary>
public List<Element<Region>> Regions { get; private set; } = Element<Region>.GenerateElements().ToList();
/// <summary>
/// List of available languages
/// </summary>
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
/// <summary>
/// List of available languages
/// </summary>
public List<Element<LanguageSelection>> LanguageSelections { get; private set; } = Element<LanguageSelection>.GenerateElements().ToList();
#endregion
/// <summary>
/// Constructor
/// </summary>
public DiscInformationViewModel(DiscInformationWindow parent, SubmissionInfo submissionInfo)
{
Parent = parent;
SubmissionInfo = submissionInfo.Clone() as SubmissionInfo ?? new SubmissionInfo();
// Add handlers
Parent.AcceptButton.Click += OnAcceptClick;
Parent.CancelButton.Click += OnCancelClick;
Parent.RingCodeGuideButton.Click += OnRingCodeGuideClick;
// Update UI with new values
ManipulateFields();
Load();
}
#region Helpers
/// <summary>
/// Enable tab entry on supported fields
/// </summary>
private void EnableTabsInInputFields()
{
// Additional Information
Parent.CommentsTextBox.Tab = true;
Parent.GeneralContent.Tab = true;
Parent.ExtrasTextBox.Tab = true;
Parent.GameFootageTextBox.Tab = true;
Parent.NetYarozeGamesTextBox.Tab = true;
Parent.PatchesTextBox.Tab = true;
Parent.PlayableDemosTextBox.Tab = true;
Parent.RollingDemosTextBox.Tab = true;
Parent.SavegamesTextBox.Tab = true;
Parent.TechDemosTextBox.Tab = true;
// L0
Parent.L0MasteringRing.Tab = true;
Parent.L0MasteringSID.Tab = true;
Parent.L0Toolstamp.Tab = true;
Parent.L0MouldSID.Tab = true;
Parent.L0AdditionalMould.Tab = true;
// L1
Parent.L1MasteringRing.Tab = true;
Parent.L1MasteringSID.Tab = true;
Parent.L1Toolstamp.Tab = true;
Parent.L1MouldSID.Tab = true;
Parent.L1AdditionalMould.Tab = true;
// L2
Parent.L2MasteringRing.Tab = true;
Parent.L2MasteringSID.Tab = true;
Parent.L2Toolstamp.Tab = true;
// L3
Parent.L3MasteringRing.Tab = true;
Parent.L3MasteringSID.Tab = true;
Parent.L3Toolstamp.Tab = true;
}
/// <summary>
/// Hide any optional, read-only fields if they don't have a value
/// </summary>
private void HideReadOnlyFields()
{
if (SubmissionInfo?.MatchedIDs == null)
Parent.MatchedIDs.Visibility = Visibility.Collapsed;
else
Parent.MatchedIDs.Text = string.Join(", ", SubmissionInfo.MatchedIDs);
if (SubmissionInfo?.CopyProtection?.AntiModchip == null)
Parent.AntiModchip.Visibility = Visibility.Collapsed;
else
Parent.AntiModchip.Text = SubmissionInfo.CopyProtection.AntiModchip.LongName();
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.DMIHash) != true)
Parent.DMIHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CommonDiscInfo?.ErrorsCount))
Parent.ErrorsCount.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CommonDiscInfo?.EXEDateBuildDate))
Parent.EXEDateBuildDate.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.EDC?.EDC == null)
Parent.EDC.Visibility = Visibility.Collapsed;
else
Parent.EDC.Text = SubmissionInfo.EDC.EDC.LongName();
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.Header))
Parent.Header.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.InternalSerialName) != true)
Parent.InternalSerialName.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CopyProtection?.LibCrypt == null)
Parent.LibCrypt.Visibility = Visibility.Collapsed;
else
Parent.LibCrypt.Text = SubmissionInfo.CopyProtection.LibCrypt.LongName();
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.LibCryptData))
Parent.LibCryptData.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.PFIHash) != true)
Parent.PFIHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.PIC))
Parent.PIC.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.Protection))
Parent.Protection.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.PVD))
Parent.PVD.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.SecuROMData))
Parent.SecuROMData.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSHash) != true)
Parent.SSHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.SecuritySectorRanges))
Parent.SecuritySectorRanges.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSVersion) != true)
Parent.SSVersion.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.VolumeLabel) != true)
Parent.VolumeLabel.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.XeMID) != true)
Parent.XeMID.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.XMID) != true)
Parent.XMID.Visibility = Visibility.Collapsed;
}
/// <summary>
/// Manipulate fields based on the current disc
/// </summary>
private void ManipulateFields()
{
// Enable tabs in all fields, if required
if (App.Options.EnableTabsInInputFields)
EnableTabsInInputFields();
// Hide read-only fields that don't have values set
HideReadOnlyFields();
// Different media types mean different fields available
UpdateFromDiscType();
// Different systems mean different fields available
UpdateFromSystemType();
}
/// <summary>
/// Load the current contents of the base SubmissionInfo to the UI
/// </summary>
private void Load()
{
Parent.CategoryComboBox.SelectedIndex = Categories.FindIndex(r => r == SubmissionInfo.CommonDiscInfo.Category);
Parent.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));
// TODO: Figure out if this can be automatically mapped instead
// Comment Fields
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields != null)
{
// Additional Information
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.AlternativeTitle))
Parent.AlternativeTitleTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeTitle];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.AlternativeForeignTitle))
Parent.AlternativeForeignTitleTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeForeignTitle];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Genre))
Parent.GenreTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Genre];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PostgapType))
Parent.PostgapTypeCheckBox.IsChecked = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PostgapType] != null;
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Series))
Parent.SeriesTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Series];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VCD))
Parent.VCDCheckBox.IsChecked = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VCD] != null;
// Physical Identifiers
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.BBFCRegistrationNumber))
Parent.BBFCRegistrationNumberTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BBFCRegistrationNumber];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DiscHologramID))
Parent.DiscHologramIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DiscHologramID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DNASDiscID))
Parent.DNASDiscIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DNASDiscID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ISBN))
Parent.ISBNTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISBN];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ISSN))
Parent.ISSNTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISSN];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PPN))
Parent.PPNTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PPN];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VFCCode))
Parent.VFCCodeTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VFCCode];
// Publisher Identifiers
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.AcclaimID))
Parent.AcclaimIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AcclaimID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ActivisionID))
Parent.ActivisionIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ActivisionID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.BandaiID))
Parent.BandaiIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BandaiID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ElectronicArtsID))
Parent.ElectronicArtsIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ElectronicArtsID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.FoxInteractiveID))
Parent.FoxInteractiveIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.FoxInteractiveID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.GTInteractiveID))
Parent.GTInteractiveIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.GTInteractiveID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.JASRACID))
Parent.JASRACIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.JASRACID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.KingRecordsID))
Parent.KingRecordsIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KingRecordsID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.KoeiID))
Parent.KoeiIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KoeiID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.KonamiID))
Parent.KonamiIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KonamiID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.LucasArtsID))
Parent.LucasArtsIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.LucasArtsID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.MicrosoftID))
Parent.MicrosoftIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.MicrosoftID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.NaganoID))
Parent.NaganoIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NaganoID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.NamcoID))
Parent.NamcoIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NamcoID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.NipponIchiSoftwareID))
Parent.NipponIchiSoftwareIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NipponIchiSoftwareID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.OriginID))
Parent.OriginIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.OriginID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PonyCanyonID))
Parent.PonyCanyonIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PonyCanyonID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SegaID))
Parent.SegaIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SegaID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SelenID))
Parent.SelenIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SelenID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SierraID))
Parent.SierraIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SierraID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.TaitoID))
Parent.TaitoIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.TaitoID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.UbisoftID))
Parent.UbisoftIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UbisoftID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ValveID))
Parent.ValveIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ValveID];
// Read-Only Information
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DMIHash))
Parent.DMIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalSerialName))
Parent.InternalSerialName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PFIHash))
Parent.PFIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSHash))
Parent.SSHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSVersion))
Parent.SSVersion.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VolumeLabel))
Parent.VolumeLabel.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.XeMID))
Parent.XeMID.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.XMID))
Parent.XMID.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID];
}
// Content Fields
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields != null)
{
// Games
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Games))
Parent.GamesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Games];
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.NetYarozeGames))
Parent.NetYarozeGamesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.NetYarozeGames];
// Demos
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.PlayableDemos))
Parent.PlayableDemosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.PlayableDemos];
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.RollingDemos))
Parent.RollingDemosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.RollingDemos];
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.TechDemos))
Parent.TechDemosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.TechDemos];
// Video
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.GameFootage))
Parent.GameFootageTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.GameFootage];
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Videos))
Parent.VideosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Videos];
// Miscellaneous
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Patches))
Parent.PatchesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Patches];
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Savegames))
Parent.SavegamesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Savegames];
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Extras))
Parent.ExtrasTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Extras];
}
}
/// <summary>
/// Save the current contents of the UI to the base SubmissionInfo
/// </summary>
public void Save()
{
SubmissionInfo.CommonDiscInfo.Category = (Parent.CategoryComboBox.SelectedItem as Element<DiscCategory>)?.Value ?? DiscCategory.Games;
SubmissionInfo.CommonDiscInfo.Region = (Parent.RegionComboBox.SelectedItem as Element<Region>)?.Value ?? Region.World;
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
SubmissionInfo.CommonDiscInfo.Languages = new Language?[] { null };
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
// TODO: Figure out if this can be automatically mapped instead
// Initialize the dictionaries, if needed
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields == null)
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields == null)
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields = new Dictionary<SiteCode?, string>();
#region Comment Fields
// Additional Information
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeTitle] = Parent.AlternativeTitleTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeForeignTitle] = Parent.AlternativeForeignTitleTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Genre] = Parent.GenreTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PostgapType] = Parent.PostgapTypeCheckBox.IsChecked?.ToString();
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Series] = Parent.SeriesTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VCD] = Parent.VCDCheckBox.IsChecked?.ToString();
// Physical Identifiers
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BBFCRegistrationNumber] = Parent.BBFCRegistrationNumberTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DiscHologramID] = Parent.DiscHologramIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DNASDiscID] = Parent.DNASDiscIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISBN] = Parent.ISBNTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISSN] = Parent.ISSNTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PPN] = Parent.PPNTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VFCCode] = Parent.VFCCodeTextBox.Text;
// Publisher Identifiers
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AcclaimID] = Parent.AcclaimIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ActivisionID] = Parent.ActivisionIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BandaiID] = Parent.BandaiIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ElectronicArtsID] = Parent.ElectronicArtsIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.FoxInteractiveID] = Parent.FoxInteractiveIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.GTInteractiveID] = Parent.GTInteractiveIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.JASRACID] = Parent.JASRACIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KingRecordsID] = Parent.KingRecordsIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KoeiID] = Parent.KoeiIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KonamiID] = Parent.KonamiIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.LucasArtsID] = Parent.LucasArtsIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.MicrosoftID] = Parent.MicrosoftIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NaganoID] = Parent.NaganoIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NamcoID] = Parent.NamcoIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NipponIchiSoftwareID] = Parent.NipponIchiSoftwareIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.OriginID] = Parent.OriginIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PonyCanyonID] = Parent.PonyCanyonIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SegaID] = Parent.SegaIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SelenID] = Parent.SelenIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SierraID] = Parent.SierraIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.TaitoID] = Parent.TaitoIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UbisoftID] = Parent.UbisoftIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ValveID] = Parent.ValveIDTextBox.Text;
// Read-Only Information
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = Parent.DMIHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = Parent.InternalSerialName.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = Parent.PFIHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = Parent.SSHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = Parent.SSVersion.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel] = Parent.VolumeLabel.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = Parent.XeMID.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = Parent.XMID.Text;
#endregion
#region Content Fields
// Games
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Games] = Parent.GamesTextBox.Text;
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.NetYarozeGames] = Parent.NetYarozeGamesTextBox.Text;
// Demos
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.PlayableDemos] = Parent.PlayableDemosTextBox.Text;
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.RollingDemos] = Parent.RollingDemosTextBox.Text;
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.TechDemos] = Parent.TechDemosTextBox.Text;
// Video
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.GameFootage] = Parent.GameFootageTextBox.Text;
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Videos] = Parent.VideosTextBox.Text;
// Miscellaneous
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Patches] = Parent.PatchesTextBox.Text;
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Savegames] = Parent.SavegamesTextBox.Text;
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Extras] = Parent.ExtrasTextBox.Text;
#endregion
}
/// <summary>
/// Update visible fields and sections based on the media type
/// </summary>
private void UpdateFromDiscType()
{
// Sony-printed discs have layers in the opposite order
var system = SubmissionInfo?.CommonDiscInfo?.System;
bool reverseOrder = system.HasReversedRingcodes();
switch (SubmissionInfo?.CommonDiscInfo?.Media)
{
case DiscType.CD:
case DiscType.GDROM:
Parent.L0Info.Header = "Data Side";
Parent.L0MasteringRing.Label = "Mastering Ring";
Parent.L0MasteringSID.Label = "Mastering SID";
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
Parent.L0MouldSID.Label = "Mould SID";
Parent.L0AdditionalMould.Label = "Additional Mould";
Parent.L1Info.Header = "Label Side";
Parent.L1MasteringRing.Visibility = Visibility.Collapsed;
Parent.L1MasteringSID.Visibility = Visibility.Collapsed;
Parent.L1Toolstamp.Visibility = Visibility.Collapsed;
Parent.L1MouldSID.Label = "Mould SID";
Parent.L1AdditionalMould.Label = "Additional Mould";
break;
case DiscType.DVD5:
case DiscType.DVD9:
case DiscType.HDDVDSL:
case DiscType.BD25:
case DiscType.BD50:
case DiscType.NintendoGameCubeGameDisc:
case DiscType.NintendoWiiOpticalDiscSL:
case DiscType.NintendoWiiOpticalDiscDL:
case DiscType.NintendoWiiUOpticalDiscSL:
// Quad-layer discs
if (SubmissionInfo?.SizeAndChecksums?.Layerbreak3 != default(long))
{
Parent.L2Info.Visibility = Visibility.Visible;
Parent.L3Info.Visibility = Visibility.Visible;
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
Parent.L0MasteringRing.Label = "Mastering Ring";
Parent.L0MasteringSID.Label = "Mastering SID";
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
Parent.L0MouldSID.Label = "Data Side Mould SID";
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
Parent.L1Info.Header = "Layer 1";
Parent.L1MasteringRing.Label = "Mastering Ring";
Parent.L1MasteringSID.Label = "Mastering SID";
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
Parent.L1MouldSID.Label = "Label Side Mould SID";
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
Parent.L2Info.Header = "Layer 2";
Parent.L2MasteringRing.Label = "Mastering Ring";
Parent.L2MasteringSID.Label = "Mastering SID";
Parent.L2Toolstamp.Label = "Toolstamp/Mastering Code";
Parent.L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
Parent.L3MasteringRing.Label = "Mastering Ring";
Parent.L3MasteringSID.Label = "Mastering SID";
Parent.L3Toolstamp.Label = "Toolstamp/Mastering Code";
}
// Triple-layer discs
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak2 != default(long))
{
Parent.L2Info.Visibility = Visibility.Visible;
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
Parent.L0MasteringRing.Label = "Mastering Ring";
Parent.L0MasteringSID.Label = "Mastering SID";
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
Parent.L0MouldSID.Label = "Data Side Mould SID";
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
Parent.L1Info.Header = "Layer 1";
Parent.L1MasteringRing.Label = "Mastering Ring";
Parent.L1MasteringSID.Label = "Mastering SID";
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
Parent.L1MouldSID.Label = "Label Side Mould SID";
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
Parent.L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
Parent.L2MasteringRing.Label = "Mastering Ring";
Parent.L2MasteringSID.Label = "Mastering SID";
Parent.L2Toolstamp.Label = "Toolstamp/Mastering Code";
}
// Double-layer discs
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
{
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
Parent.L0MasteringRing.Label = "Mastering Ring";
Parent.L0MasteringSID.Label = "Mastering SID";
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
Parent.L0MouldSID.Label = "Data Side Mould SID";
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
Parent.L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
Parent.L1MasteringRing.Label = "Mastering Ring";
Parent.L1MasteringSID.Label = "Mastering SID";
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
Parent.L1MouldSID.Label = "Label Side Mould SID";
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
}
// Single-layer discs
else
{
Parent.L0Info.Header = "Data Side";
Parent.L0MasteringRing.Label = "Mastering Ring";
Parent.L0MasteringSID.Label = "Mastering SID";
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
Parent.L0MouldSID.Label = "Mould SID";
Parent.L0AdditionalMould.Label = "Additional Mould";
Parent.L1Info.Header = "Label Side";
Parent.L1MasteringRing.Label = "Mastering Ring";
Parent.L1MasteringSID.Label = "Mastering SID";
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
Parent.L1MouldSID.Label = "Mould SID";
Parent.L1AdditionalMould.Label = "Additional Mould";
}
break;
// All other media we assume to have no rings
default:
Parent.L0Info.Visibility = Visibility.Collapsed;
Parent.L1Info.Visibility = Visibility.Collapsed;
Parent.L2Info.Visibility = Visibility.Collapsed;
Parent.L3Info.Visibility = Visibility.Collapsed;
break;
}
}
/// <summary>
/// Update visible fields and sections based on the system type
/// </summary>
private void UpdateFromSystemType()
{
var system = SubmissionInfo?.CommonDiscInfo?.System;
switch (system)
{
case RedumpSystem.SonyPlayStation2:
Parent.LanguageSelectionGrid.Visibility = Visibility.Visible;
break;
}
}
#endregion
#region Event Handlers
/// <summary>
/// Handler for AcceptButton Click event
/// </summary>
private void OnAcceptClick(object sender, RoutedEventArgs e)
{
Save();
Parent.DialogResult = true;
Parent.Close();
}
/// <summary>
/// Handler for CancelButton Click event
/// </summary>
private void OnCancelClick(object sender, RoutedEventArgs e)
{
Parent.DialogResult = false;
Parent.Close();
}
/// <summary>
/// Handler for RingCodeGuideButton Click event
/// </summary>
private void OnRingCodeGuideClick(object sender, RoutedEventArgs e)
{
var ringCodeGuideWindow = new RingCodeGuideWindow()
{
Owner = Parent,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
ringCodeGuideWindow.Show();
}
#endregion
}
}

View File

@@ -0,0 +1,577 @@
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.Core.Data;
using MPF.UserControls;
namespace MPF.GUI.ViewModels
{
public class LogViewModel
{
#region Fields
/// <summary>
/// Parent OptionsWindow object
/// </summary>
public LogOutput Parent { get; private set; }
#endregion
#region Private State Variables
/// <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+)$";
#endregion
/// <summary>
/// Constructor
/// </summary>
public LogViewModel(LogOutput parent)
{
Parent = parent;
// Add handlers
Parent.OutputViewer.SizeChanged += OutputViewerSizeChanged;
Parent.Output.TextChanged += OnTextChanged;
Parent.ClearButton.Click += OnClearButton;
Parent.SaveButton.Click += OnSaveButton;
// Update the internal state
var document = new FlowDocument()
{
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
};
_paragraph = new Paragraph();
document.Blocks.Add(_paragraph);
Parent.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) =>
{
Parent.ProgressBar.Value = 0;
Parent.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>
public 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, LogViewModel.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, LogViewModel.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, LogViewModel.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()
{
Parent.Dispatcher.Invoke(() =>
{
Parent.ProgressBar.Value = 0;
Parent.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 && !App.Options.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 = Parent.Dispatcher.Invoke(() => nextLogLine.Text);
if (nextText == null)
return;
try
{
// If we're not processing log formatting, just append and continue
if (!App.Options.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 = Parent.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 (App.Options.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)
{
Parent.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 Parent.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)
{
Parent.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)
{
Parent.Dispatcher.Invoke(() =>
{
lastLine.Text = logLine.Text;
lastLine.Foreground = logLine.GetForegroundColor();
});
}
#endregion
#region Helpers
/// <summary>
/// Scroll the current view to the bottom
/// </summary>
public void ScrollToBottom()
{
Parent.OutputViewer.ScrollToBottom();
}
#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) =>
ScrollToBottom();
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e) =>
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;
Parent.ProgressBar.Value = percentProgress;
Parent.ProgressLabel.Text = string.Format($"{text} ({percentProgress:N2}%)");
}
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,264 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Forms;
using MPF.Core.Data;
using MPF.Windows;
using RedumpLib.Web;
using WPFCustomMessageBox;
namespace MPF.GUI.ViewModels
{
public class OptionsViewModel
{
#region Fields
/// <summary>
/// Parent OptionsWindow object
/// </summary>
public OptionsWindow Parent { get; }
/// <summary>
/// Current set of options
/// </summary>
public Options Options { get; }
/// <summary>
/// Flag for if settings were saved or not
/// </summary>
public bool SavedSettings { get; private set; }
#endregion
#region Lists
/// <summary>
/// List of available internal programs
/// </summary>
public List<Element<InternalProgram>> InternalPrograms => PopulateInternalPrograms();
/// <summary>
/// Current list of supported system profiles
/// </summary>
public List<RedumpSystemComboBoxItem> Systems => RedumpSystemComboBoxItem.GenerateElements().ToList();
#endregion
/// <summary>
/// Constructor
/// </summary>
public OptionsViewModel(OptionsWindow parent)
{
Parent = parent;
Options = App.Options.Clone() as Options;
// Add handlers
Parent.AaruPathButton.Click += BrowseForPathClick;
Parent.DiscImageCreatorPathButton.Click += BrowseForPathClick;
Parent.DDPathButton.Click += BrowseForPathClick;
Parent.DefaultOutputPathButton.Click += BrowseForPathClick;
Parent.AcceptButton.Click += OnAcceptClick;
Parent.CancelButton.Click += OnCancelClick;
Parent.RedumpLoginTestButton.Click += OnRedumpTestClick;
// Update UI with new values
Load();
}
#region Load and Save
/// <summary>
/// Load any options-related elements
/// </summary>
private void Load()
{
Parent.InternalProgramComboBox.SelectedIndex = InternalPrograms.FindIndex(r => r == Options.InternalProgram);
Parent.DefaultSystemComboBox.SelectedIndex = Systems.FindIndex(r => r == Options.DefaultSystem);
Parent.RedumpPasswordBox.Password = Options.RedumpPassword;
}
/// <summary>
/// Save any options-related elements
/// </summary>
private void Save()
{
var selectedInternalProgram = Parent.InternalProgramComboBox.SelectedItem as Element<InternalProgram>;
Options.InternalProgram = selectedInternalProgram?.Value ?? InternalProgram.DiscImageCreator;
var selectedDefaultSystem = Parent.DefaultSystemComboBox.SelectedItem as RedumpSystemComboBoxItem;
Options.DefaultSystem = selectedDefaultSystem?.Value ?? null;
Options.RedumpPassword = Parent.RedumpPasswordBox.Password;
SavedSettings = true;
}
#endregion
#region Population
/// <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();
}
#endregion
#region UI Commands
/// <summary>
/// Browse and set a path based on the invoking button
/// </summary>
private void BrowseForPath(System.Windows.Controls.Button button)
{
// If the button is null, we can't do anything
if (button == null)
return;
// 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";
string currentPath = TextBoxForPathSetting(pathSettingName)?.Text;
string initialDirectory = AppDomain.CurrentDomain.BaseDirectory;
if (!shouldBrowseForPath && !string.IsNullOrEmpty(currentPath))
initialDirectory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
CommonDialog dialog = shouldBrowseForPath
? (CommonDialog)CreateFolderBrowserDialog()
: CreateOpenFileDialog(initialDirectory);
using (dialog)
{
DialogResult result = dialog.ShowDialog();
if (result == DialogResult.OK)
{
string path = string.Empty;
bool exists = false;
if (shouldBrowseForPath && dialog is FolderBrowserDialog folderBrowserDialog)
{
path = folderBrowserDialog.SelectedPath;
exists = Directory.Exists(path);
}
else if (dialog is OpenFileDialog openFileDialog)
{
path = openFileDialog.FileName;
exists = File.Exists(path);
}
if (exists)
{
Options[pathSettingName] = path;
TextBoxForPathSetting(pathSettingName).Text = path;
}
else
{
CustomMessageBox.Show(
"Specified path doesn't exist!",
"Error",
MessageBoxButton.OK,
MessageBoxImage.Error
);
}
}
}
}
/// <summary>
/// Optionally save the current options and close the parent window
/// </summary>
private void OptionalSaveAndClose(bool save)
{
// Save if we're supposed to
if (save)
Save();
Parent.Close();
}
/// <summary>
/// Test Redump login credentials
/// </summary>
private void TestRedumpLogin()
{
using (RedumpWebClient wc = new RedumpWebClient())
{
bool? loggedIn = wc.Login(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
if (loggedIn == true)
CustomMessageBox.Show(Parent, "Redump login credentials accepted!", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
else if (loggedIn == false)
CustomMessageBox.Show(Parent, "Redump login credentials denied!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
else
CustomMessageBox.Show(Parent, "Error validating credentials!", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
#endregion
#region UI Functionality
/// <summary>
/// Create an open folder dialog box
/// </summary>
private static FolderBrowserDialog CreateFolderBrowserDialog() => new FolderBrowserDialog();
/// <summary>
/// Create an open file dialog box
/// </summary>
private static OpenFileDialog CreateOpenFileDialog(string initialDirectory)
{
return new OpenFileDialog()
{
InitialDirectory = initialDirectory,
Filter = "Executables (*.exe)|*.exe",
FilterIndex = 0,
RestoreDirectory = true,
};
}
/// <summary>
/// Find a TextBox by setting name
/// </summary>
/// <param name="name">Setting name to find</param>
/// <returns>TextBox for that setting</returns>
private System.Windows.Controls.TextBox TextBoxForPathSetting(string name) =>
Parent.FindName(name + "TextBox") as System.Windows.Controls.TextBox;
#endregion
#region Event Handlers
/// <summary>
/// Handler for generic Click event
/// </summary>
private void BrowseForPathClick(object sender, EventArgs e) =>
BrowseForPath(sender as System.Windows.Controls.Button);
/// <summary>
/// Handler for AcceptButton Click event
/// </summary>
private void OnAcceptClick(object sender, EventArgs e) =>
OptionalSaveAndClose(true);
/// <summary>
/// Handler for CancelButtom Click event
/// </summary>
private void OnCancelClick(object sender, EventArgs e) =>
OptionalSaveAndClose(false);
/// <summary>
/// Test Redump credentials for validity
/// </summary>
private void OnRedumpTestClick(object sender, EventArgs e) =>
TestRedumpLogin();
#endregion
}
}

View File

@@ -1,10 +1,10 @@
<Window x:Class="MPF.Windows.DiscInformationWindow"
<windows:WindowBase 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"
xmlns:windows="clr-namespace:MPF.Windows"
mc:Ignorable="d"
Title="Disc Information" Width="515" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
@@ -15,7 +15,7 @@
</WindowChrome.WindowChrome>
<Grid>
<StackPanel Orientation="Vertical" Width="500">
<StackPanel Orientation="Vertical" Width="500" MaxHeight="650">
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
@@ -67,133 +67,329 @@
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"/>
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<GroupBox Header="Common Disc Information" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput Label="Title"
Text="{Binding SubmissionInfo.CommonDiscInfo.Title, Mode=TwoWay}"/>
<controls:UserInput x:Name="AlternativeTitleTextBox" Label="Alternative Title"/>
<controls:UserInput Label="Foreign Title (Non-Latin)"
Text="{Binding SubmissionInfo.CommonDiscInfo.ForeignTitleNonLatin, Mode=TwoWay}"/>
<controls:UserInput x:Name="AlternativeForeignTitleTextBox" Label="Alternative Foreign Title"/>
<controls:UserInput Label="Disc Number / Letter"
Text="{Binding SubmissionInfo.CommonDiscInfo.DiscNumberLetter, Mode=TwoWay}"/>
<controls:UserInput Label="Disc Title"
Text="{Binding SubmissionInfo.CommonDiscInfo.DiscTitle, Mode=TwoWay}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<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>
<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}"
Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<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>
<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}"
Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<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>
<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>
<Grid x:Name="LanguageSelectionGrid" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<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>
<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>
<controls:UserInput Label="Serial" Text="{Binding SubmissionInfo.CommonDiscInfo.Serial, Mode=TwoWay}"/>
<controls:UserInput Label="Barcode" Text="{Binding SubmissionInfo.CommonDiscInfo.Barcode, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="Version and Editions" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput Label="Version" Text="{Binding SubmissionInfo.VersionAndEditions.Version, Mode=TwoWay}"/>
<controls:UserInput Label="Edition" Text="{Binding SubmissionInfo.VersionAndEditions.OtherEditions, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</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 Header="Additional Info" Style="{DynamicResource CustomTabItemStyle}">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<GroupBox Header="Extras" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="CommentsTextBox" Label="Comments"
Text="{Binding SubmissionInfo.CommonDiscInfo.Comments, Mode=TwoWay}" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top" />
<controls:UserInput x:Name="GenreTextBox" Label="Genre"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label/>
<CheckBox Grid.Column="1" x:Name="PostgapTypeCheckBox" Content="Postgap type (Form 2)" Margin="0,4"/>
</Grid>
<controls:UserInput x:Name="SeriesTextBox" Label="Series"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label/>
<CheckBox Grid.Column="1" x:Name="VCDCheckBox" Content="V-CD" Margin="0,4"/>
</Grid>
</StackPanel>
</GroupBox>
<GroupBox Header="Physical Identifiers" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="BBFCRegistrationNumberTextBox" Label="BBFC Reg. No."/>
<controls:UserInput x:Name="DiscHologramIDTextBox" Label="Disc Hologram ID"/>
<controls:UserInput x:Name="DNASDiscIDTextBox" Label="DNAS Disc ID"/>
<controls:UserInput x:Name="ISBNTextBox" Label="ISBN"/>
<controls:UserInput x:Name="ISSNTextBox" Label="ISSN"/>
<controls:UserInput x:Name="PPNTextBox" Label="PPN"/>
<controls:UserInput x:Name="VFCCodeTextBox" Label="VFC code"/>
</StackPanel>
</GroupBox>
<GroupBox Header="Publisher Identifiers" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="AcclaimIDTextBox" Label="Acclaim ID"/>
<controls:UserInput x:Name="ActivisionIDTextBox" Label="Activision ID"/>
<controls:UserInput x:Name="BandaiIDTextBox" Label="Bandai ID"/>
<controls:UserInput x:Name="ElectronicArtsIDTextBox" Label="Electronic Arts ID"/>
<controls:UserInput x:Name="FoxInteractiveIDTextBox" Label="Fox Interactive ID"/>
<controls:UserInput x:Name="GTInteractiveIDTextBox" Label="GT Interactive ID"/>
<controls:UserInput x:Name="JASRACIDTextBox" Label="JASRAC ID"/>
<controls:UserInput x:Name="KingRecordsIDTextBox" Label="King Records ID"/>
<controls:UserInput x:Name="KoeiIDTextBox" Label="Koei ID"/>
<controls:UserInput x:Name="KonamiIDTextBox" Label="Konami ID"/>
<controls:UserInput x:Name="LucasArtsIDTextBox" Label="LucasArts ID"/>
<controls:UserInput x:Name="MicrosoftIDTextBox" Label="Microsoft ID"/>
<controls:UserInput x:Name="NaganoIDTextBox" Label="Nagano ID"/>
<controls:UserInput x:Name="NamcoIDTextBox" Label="Namco ID"/>
<controls:UserInput x:Name="NipponIchiSoftwareIDTextBox" Label="Nippon Ichi Software ID"/>
<controls:UserInput x:Name="OriginIDTextBox" Label="Origin ID"/>
<controls:UserInput x:Name="PonyCanyonIDTextBox" Label="Pony Canyon ID"/>
<controls:UserInput x:Name="SegaIDTextBox" Label="Sega ID"/>
<controls:UserInput x:Name="SelenIDTextBox" Label="Selen ID"/>
<controls:UserInput x:Name="SierraIDTextBox" Label="Sierra ID"/>
<controls:UserInput x:Name="TaitoIDTextBox" Label="Taito ID"/>
<controls:UserInput x:Name="UbisoftIDTextBox" Label="Ubisoft ID"/>
<controls:UserInput x:Name="ValveIDTextBox" Label="Valve ID"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</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 Header="Contents" Style="{DynamicResource CustomTabItemStyle}">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="GeneralContent" Label="General"
Text="{Binding SubmissionInfo.CommonDiscInfo.Contents, Mode=TwoWay}" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<!-- Games -->
<controls:UserInput x:Name="GamesTextBox" Label="Games" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<controls:UserInput x:Name="NetYarozeGamesTextBox" Label="Net Yaroze Games" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<!-- Demos -->
<controls:UserInput x:Name="PlayableDemosTextBox" Label="Playable Demos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<controls:UserInput x:Name="RollingDemosTextBox" Label="Rolling Demos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<controls:UserInput x:Name="TechDemosTextBox" Label="Tech Demos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<!-- Video -->
<controls:UserInput x:Name="GameFootageTextBox" Label="Game Footage" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<controls:UserInput x:Name="VideosTextBox" Label="Videos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<!-- Miscellaneous -->
<controls:UserInput x:Name="PatchesTextBox" Label="Patches" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<controls:UserInput x:Name="SavegamesTextBox" Label="Savegames" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<controls:UserInput x:Name="ExtrasTextBox" Label="Extras" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
</StackPanel>
</ScrollViewer>
</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 Header="Ringcodes" Style="{DynamicResource CustomTabItemStyle}">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<GroupBox x:Name="L0Info" Header="Data/L0 Info" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L0MasteringRing" Label="Data/L0 Mastering Ring"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0MasteringRing, Mode=TwoWay}"/>
<controls:UserInput x:Name="L0MasteringSID" Label="Data/L0 Mastering SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0MasteringSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L0Toolstamp" Label="Data/L0 Toolstamp/Mastering Code"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0ToolstampMasteringCode, Mode=TwoWay}"/>
<controls:UserInput x:Name="L0MouldSID" Label="Data/L0 Mould SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0MouldSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L0AdditionalMould" Label="Data/L0 Additional Mould"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0AdditionalMould, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
<GroupBox x:Name="L1Info" Header="Label/L1 Info" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L1MasteringRing" Label="Label/L1 Mastering Ring"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1MasteringRing, Mode=TwoWay}"/>
<controls:UserInput x:Name="L1MasteringSID" Label="Label/L1 Mastering SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1MasteringSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L1Toolstamp" Label="Label/L1 Toolstamp/Mastering Code"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1ToolstampMasteringCode, Mode=TwoWay}"/>
<controls:UserInput x:Name="L1MouldSID" Label="Label/L1 Mould SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1MouldSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L1AdditionalMould" Label="Label/L1 Additional Mould"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1AdditionalMould, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
<GroupBox x:Name="L2Info" Header="L2 Info" Visibility="Collapsed" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L2MasteringRing" Label="L2 Mastering Ring"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer2MasteringRing, Mode=TwoWay}"/>
<controls:UserInput x:Name="L2MasteringSID" Label="L2 Mastering SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer2MasteringSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L2Toolstamp" Label="L2 Toolstamp/Mastering Code"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer2ToolstampMasteringCode, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
<GroupBox x:Name="L3Info" Header="L3 Info" Visibility="Collapsed" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L3MasteringRing" Label="L3 Mastering Ring"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer3MasteringRing, Mode=TwoWay}"/>
<controls:UserInput x:Name="L3MasteringSID" Label="L3 Mastering SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer3MasteringSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L3Toolstamp" Label="L3 Toolstamp/Mastering Code"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer3ToolstampMasteringCode, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</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 Header="Read-Only Info" Style="{DynamicResource CustomTabItemStyle}">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="MatchedIDs" Label="Matched ID(s)" IsReadOnly="True"/>
<controls:UserInput x:Name="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"/>
<controls:UserInput x:Name="DMIHash" Label="DMI Hash" IsReadOnly="True"/>
<controls:UserInput x:Name="EDC" Label="EDC" IsReadOnly="True"/>
<controls:UserInput x:Name="ErrorsCount" Label="Error(s) Count" IsReadOnly="True"
Text="{Binding SubmissionInfo.CommonDiscInfo.ErrorsCount, Mode=TwoWay}"/>
<controls:UserInput x:Name="EXEDateBuildDate" Label="EXE/Build Date" IsReadOnly="True"
Text="{Binding SubmissionInfo.CommonDiscInfo.EXEDateBuildDate, Mode=TwoWay}"/>
<controls:UserInput x:Name="Header" Label="Header" IsReadOnly="True"
Text="{Binding SubmissionInfo.Extras.Header, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"/>
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"/>
<controls:UserInput x:Name="LibCryptData" Label="LibCrypt Data" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.LibCryptData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="PFIHash" Label="PFI Hash" IsReadOnly="True"/>
<controls:UserInput x:Name="PIC" Label="PIC" IsReadOnly="True"
Text="{Binding SubmissionInfo.Extras.PIC, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="Protection" Label="Protection" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.Protection, Mode=TwoWay}" TextHeight="75" TextWrapping="Wrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="PVD" Label="PVD" IsReadOnly="True"
Text="{Binding SubmissionInfo.Extras.PVD, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="SecuROMData" Label="SecuROM Data" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.SecuROMData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="SSHash" Label="Security Sector Hash" IsReadOnly="True"/>
<controls:UserInput x:Name="SecuritySectorRanges" Label="Security Sector Ranges" IsReadOnly="True"
Text="{Binding SubmissionInfo.Extras.SecuritySectorRanges, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="SSVersion" Label="Security Sector Version" IsReadOnly="True"/>
<controls:UserInput x:Name="VolumeLabel" Label="Volume Label" IsReadOnly="True"/>
<controls:UserInput x:Name="XeMID" Label="XeMID" IsReadOnly="True"/>
<controls:UserInput x:Name="XMID" Label="XMID" IsReadOnly="True"/>
<!-- TODO: Add track/checksum data once there's a reasonable way to do so -->
</StackPanel>
</ScrollViewer>
</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="AcceptButton" Height="25" Width="80" Content="Accept"
Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" Content="Cancel"
Style="{DynamicResource CustomButtonStyle}" />
<Button Name="RingCodeGuideButton" Grid.Column="2" Height="25" Width="120" Content="Ring Code Guide"
Click="OnRingCodeGuideClick" Style="{DynamicResource CustomButtonStyle}" />
Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</Window>
</windows:WindowBase>

View File

@@ -1,355 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using MPF.Data;
using MPF.GUI.ViewModels;
using RedumpLib.Data;
namespace MPF.Windows
{
/// <summary>
/// Interaction logic for DiscInformationWindow.xaml
/// </summary>
public partial class DiscInformationWindow : Window
public partial class DiscInformationWindow : WindowBase
{
#region Fields
/// <summary>
/// Read-only access to the current disc information view model
/// </summary>
public DiscInformationViewModel DiscInformationViewModel => DataContext as DiscInformationViewModel;
/// <summary>
/// List of available disc categories
/// Constructor
/// </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();
DataContext = new DiscInformationViewModel(this, submissionInfo);
}
/// <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
}
}

View File

@@ -1,10 +1,11 @@
<Window x:Class="MPF.Windows.MainWindow"
<windows:WindowBase 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"
xmlns:viewModels="clr-namespace:MPF.GUI.ViewModels"
xmlns:windows="clr-namespace:MPF.Windows"
mc:Ignorable="d"
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
@@ -13,7 +14,11 @@
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Window.DataContext>
<viewModels:MainViewModel/>
</Window.DataContext>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,2,0,0">
@@ -35,7 +40,7 @@
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"
<MenuItem x:Name="AppExitMenuItem" Header="E_xit" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
@@ -44,20 +49,24 @@
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"
<MenuItem x:Name="OptionsMenuItem" Header="_Options" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
<MenuItem x:Name="DebugViewMenuItem" Header="_Debug Info Window" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" Visibility="Collapsed" />
</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"
<MenuItem x:Name="AboutMenuItem" Header="_About" HorizontalAlignment="Left" Width="185"
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"
<MenuItem x:Name="CheckForUpdatesMenuItem" Header="_Check for Updates" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
@@ -133,7 +142,7 @@
<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}"/>
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}">
@@ -149,18 +158,18 @@
<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" />
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right" IsChecked="False" />
</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}" />
IsEnabled="False" Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="MediaScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for discs"
Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="CopyProtectScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for protection"
Click="CopyProtectScanButtonClick" Style="{DynamicResource CustomButtonStyle}" />
Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
@@ -175,4 +184,4 @@
</Expander>
</StackPanel>
</Grid>
</Window>
</windows:WindowBase>

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,10 @@
<Window x:Class="MPF.Windows.OptionsWindow"
<windows:WindowBase 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"
xmlns:windows="clr-namespace:MPF.Windows"
mc:Ignorable="d"
Title="Options" Width="515.132" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
@@ -71,6 +71,11 @@
IsChecked="{Binding Options.EnableDarkMode}"
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Check for Updates on Startup"
IsChecked="{Binding Options.CheckForUpdatesOnStartup}"
ToolTip="Check for updates when the application starts" Margin="0,4"
/>
</UniformGrid>
</TabItem>
@@ -92,17 +97,17 @@
<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}" />
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}" />
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}" />
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"
@@ -111,14 +116,14 @@
<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}" />
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">
<UniformGrid Columns="2" Rows="8">
<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"
@@ -167,9 +172,19 @@
ToolTip="Enable showing the disc information output after dumping" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Tab Input"
IsChecked="{Binding Options.EnableTabsInInputFields}"
ToolTip="Enable entering tabs in supported input fields instead of tab navigation" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Show Eject Reminder"
IsChecked="{Binding Options.ShowDiscEjectReminder}"
ToolTip="Enable showing the disc eject reminder after information is gathered" 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"
ToolTip="Enable adding placeholder text in the output for required and optional fields" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Output Submission JSON"
@@ -350,7 +365,7 @@
<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}" />
Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
</StackPanel>
@@ -362,12 +377,12 @@
<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}" />
Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
Click="OnCancelClick" Style="{DynamicResource CustomButtonStyle}" />
Style="{DynamicResource CustomButtonStyle}" />
<Label/> <!-- Empty label for padding -->
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</Window>
</windows:WindowBase>

View File

@@ -1,232 +1,24 @@
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;
using MPF.GUI.ViewModels;
namespace MPF.Windows
{
/// <summary>
/// Interaction logic for OptionsWindow.xaml
/// </summary>
public partial class OptionsWindow : Window
public partial class OptionsWindow : WindowBase
{
#region Fields
/// <summary>
/// Read-only access to the current options view model
/// </summary>
public OptionsViewModel OptionsViewModel => DataContext as OptionsViewModel;
/// <summary>
/// Current set of options
/// Constructor
/// </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)
public OptionsWindow()
{
InitializeComponent();
DataContext = this;
Options = options.Clone() as Options;
Load();
DataContext = new OptionsViewModel(this);
}
#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
}
}

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