Compare commits

...

448 Commits
2.6.4 ... 3.1.9

Author SHA1 Message Date
Matt Nadareski
f97e293ad2 Bump version 2024-05-19 19:48:51 -04:00
Deterous
2a040effde Prefer PlayStation info from Redumper logs (#702)
* Prefer PlayStation info from Redumper logs

* Restrict parsing PS data to info field

* Typo

* Readd version field
2024-05-19 19:45:25 -04:00
Matt Nadareski
862e676590 Update BinaryObjectScanner to 3.1.12 2024-05-18 22:30:27 -04:00
Deterous
bffa70bcc9 Add Xbox Backup Creator support to MPF.Check (#701)
* Initial XBC support

* Complete but untested XBC support

* Update changelog

* Fix SS recreation bug

* Parse XeMID from DMI

* Nitpicks
2024-05-16 23:11:33 -04:00
Matt Nadareski
bb596c49f4 Use IO implementation of IniFile 2024-05-16 12:35:39 -04:00
Matt Nadareski
917986530b Remove now-unused Hash enum 2024-05-16 12:29:48 -04:00
Matt Nadareski
14bc7609c5 Update BinaryObjectScanner to 3.1.11 2024-05-15 20:44:15 -04:00
Matt Nadareski
a2361c34bc Update RedumpLib and related 2024-05-15 17:17:23 -04:00
Matt Nadareski
3d29eeb3c3 Add site code listing to Check 2024-05-15 15:49:40 -04:00
Matt Nadareski
c908a55ce6 Get volume label from UIC outputs 2024-05-15 11:57:34 -04:00
Deterous
c2b3932363 Trim PIC for XboxOne/XboxSX (#700) 2024-05-14 10:34:11 -04:00
Deterous
b4d47aea37 Fix XboxOne/XboxSX Filename bug (#698) 2024-05-10 09:13:38 -04:00
Deterous
f8d3ae7bc7 Fix CleanRip not pulling info (#697) 2024-05-10 08:09:20 -04:00
Matt Nadareski
9f50277888 Update Redumper to build 325 2024-05-09 09:32:06 -04:00
Matt Nadareski
96f826994a Bump version 2024-05-09 08:56:53 -04:00
Matt Nadareski
eda3c97465 Note 2024-05-08 23:36:27 -04:00
Matt Nadareski
ff380451db Note 2024-05-08 23:35:35 -04:00
Matt Nadareski
a9ee6667d0 Add _PFI.bin support for UIC (fixes #696) 2024-05-07 19:17:48 -04:00
Matt Nadareski
54415241d2 Critical update to BinaryObjectScanner 3.1.10 2024-05-07 09:03:42 -04:00
Matt Nadareski
79d2957ede Omit false positives on formatting protections 2024-05-06 22:58:23 -04:00
Matt Nadareski
0b5d52da7d Note 2024-05-06 22:04:26 -04:00
Matt Nadareski
274ad9fc9a Note 2024-05-06 22:01:39 -04:00
Matt Nadareski
a2217b536b Note 2024-05-06 21:51:14 -04:00
Deterous
43e7883ac9 Option for default Redumper leadin retries (#693)
* Option for default Redumper leadin retries

* Gate custom default leadin retries behind option

* typo

* False by default

* change wording

* whitespace?

* better whitespace?
2024-05-02 23:41:07 -04:00
Matt Nadareski
c37d098eee Bump version 2024-04-28 20:17:48 -04:00
Matt Nadareski
17c2ca6fa8 Critical update to BinaryObjectScanner 3.1.9 2024-04-28 19:57:10 -04:00
Matt Nadareski
4b2d30bc01 Bump version 2024-04-27 19:37:49 -04:00
Matt Nadareski
ec8b65a7fa Update packages 2024-04-26 22:21:41 -04:00
Matt Nadareski
ec5611f5ff Update packages 2024-04-24 18:12:05 -04:00
Deterous
3e350b666b Custom non-redump Redumper options (#691)
* Gate advanced Redumper options in UI, add read method + sector order

* Update changelog

* Fixed-width text/combo boxes
2024-04-16 10:11:09 -04:00
Deterous
e83f69fc3e Define better default categories (#689) 2024-04-08 09:37:31 -04:00
Deterous
6ecbbb6978 Fix parameter parsing for = symbol (#687) 2024-04-05 21:27:57 -04:00
Matt Nadareski
771483ac14 Bump version 2024-04-05 16:03:28 -04:00
Matt Nadareski
ccde878286 Update BinaryObjectScanner to 3.1.5 2024-04-05 15:58:44 -04:00
Matt Nadareski
e0ab3e048b Enable remaining fields for label-side information 2024-04-05 15:35:54 -04:00
Matt Nadareski
cf2ae163c4 Enable label-side mastering SID and toolstamp 2024-04-05 15:30:37 -04:00
Deterous
5025a3e91a Fix CleanRip hash output for Check (#685)
* ClrMamePro format is for CDs, dont assume hashes are in CleanRip logs

* Rename folder

* Rename folder again

* Update changelog

* Don't trust cleanrip log hashes if any are missing
2024-04-05 08:54:07 -04:00
Matt Nadareski
dab774dab3 Fix test project project includes 2024-04-02 22:58:02 -04:00
Matt Nadareski
04c6131d28 Enable Windows targeting for test project 2024-04-02 22:28:10 -04:00
Deterous
47561baee8 Detect Xbox Series X discs (#683)
* Detect Xbox Series X discs via catalog.js deserialization

* Update changelog
2024-04-02 19:02:05 -07:00
Matt Nadareski
a8b1a8342d Update BinaryObjectScanner to 3.1.4 2024-04-02 17:10:49 -04:00
Matt Nadareski
7b8ef00d59 Merge with main 2024-04-02 16:39:42 -04:00
Matt Nadareski
65cc502a94 Update packages 2024-04-02 16:38:51 -04:00
fuzzball
d38b465b08 Fix information pulling for redumper (#682)
* Fix dat pull failure

* Fix c2 error exception

* Update changelist
2024-03-30 09:00:58 -07:00
Matt Nadareski
783c323fd0 Update BinaryObjectScanner to 3.1.3 2024-03-27 12:09:19 -04:00
Matt Nadareski
04af8807e5 Language selections unchecked by default (fixes #681) 2024-03-23 12:17:37 -04:00
Matt Nadareski
1260dfdff2 Fix missing information in README 2024-03-18 12:45:11 -04:00
Matt Nadareski
e5b883fb73 Fix outdated information in README 2024-03-18 12:44:24 -04:00
Matt Nadareski
1c08451487 Add Konami Python 2 system detection (fixes #680) 2024-03-18 12:30:02 -04:00
Matt Nadareski
29b483f805 Read last instance of hash data from Redumper 2024-03-18 12:20:11 -04:00
Matt Nadareski
2eff4a7488 Read C2 error count from Redumper logs (fixes #666) 2024-03-18 12:15:56 -04:00
Matt Nadareski
5e94d02503 Handle .0.physical files from Redumper (fixes #679) 2024-03-17 19:23:25 -04:00
Matt Nadareski
ccf2166b72 Bump version 2024-03-16 11:50:02 -04:00
Matt Nadareski
024394bbec Update BinaryObjectScanner to 3.1.2 2024-03-16 11:46:32 -04:00
Matt Nadareski
301a0cb188 Bump version 2024-03-15 13:43:36 -04:00
Matt Nadareski
64231da666 Update to RedumpLib 1.3.6 2024-03-15 13:00:34 -04:00
Deterous
5f56977021 Add MPF version to Submission info (#678) 2024-03-15 09:38:02 -07:00
Deterous
436ccf7a34 Fix Redumper generic drive type (#677) 2024-03-15 06:56:54 -07:00
Matt Nadareski
ef7510804e Update packages 2024-03-14 13:29:14 -04:00
Deterous
8c61b87954 Update LibIRD to 0.9.0 (#674) 2024-03-12 21:32:20 -07:00
Deterous
17ba117949 Update LibIRD to 0.8.0 (#673) 2024-03-12 07:12:22 -07:00
Matt Nadareski
0737ba7641 Fix formatting output formatting 2024-03-09 21:36:12 -05:00
Deterous
e9dba0767e Fix Check UI deadlock (#672)
* Fix Check UI deadlock

* Add region

* Niceties
2024-03-07 02:04:07 -08:00
Deterous
2d142e9e9d Fix config access permissions (#671) 2024-03-06 23:08:37 -08:00
Matt Nadareski
7a928decff Try updating PR check action 2024-03-06 22:46:08 -05:00
Deterous
eb5409bdee Enable LibIRD for all .NET frameworks (#670) 2024-03-06 19:11:54 -08:00
Matt Nadareski
1578193068 Update packages to latest 2024-03-06 11:04:50 -05:00
Matt Nadareski
131c95e6ef Update to SabreTools.RedumpLib 1.3.5 2024-03-05 12:56:06 -05:00
Matt Nadareski
a7790a271f Use SabreTools.Hashing 2024-03-04 21:23:45 -05:00
Matt Nadareski
1b342d56ef Ensure no labels are empty (fixes #669) 2024-03-01 12:57:51 -05:00
Deterous
a500211129 Fix title normalization (#668)
* Perform title normalization properly

* Use strings for old .net
2024-03-01 06:25:16 -08:00
Deterous
4d798fa547 Hide Size in DIW if value is 0 (#664)
* Hide Size field in DIW when size=0

* Update CHANGELIST.md
2024-02-28 22:26:07 -08:00
Matt Nadareski
597ebdc973 Update changelog 2024-02-28 22:00:31 -05:00
Deterous
c6a8a9265f Add PS3 CFW support to MPF.Check (#663)
* Add PS3 CFW support to MPF.Check

* Do everything better
2024-02-28 18:59:49 -08:00
Matt Nadareski
393c53769d Don't link to AppVeyor artifacts page anymore 2024-02-27 11:33:44 -05:00
Matt Nadareski
fa21999d3f Add PR check workflow 2024-02-27 11:20:53 -05:00
Matt Nadareski
dafbb05b16 Remove GHA pull request builds 2024-02-27 11:13:07 -05:00
Matt Nadareski
1c1b23a84b Make GHA debug-only 2024-02-27 10:41:50 -05:00
Matt Nadareski
fd0fe4912d Hide layerbreaks if value is 0 (fixes #662) 2024-02-27 10:04:50 -05:00
Matt Nadareski
b5b54d13a2 Gate debug publishing behind use all flag 2024-02-27 09:57:30 -05:00
Matt Nadareski
da77987db3 Bump version 2024-02-27 09:31:10 -05:00
Deterous
774f44c8ce Pull PS3 Disc Key from redump (#656)
* Pull PS3 Disc Key from redump

* Bump RedumpLib to 1.3.3
2024-02-26 17:35:04 -08:00
Matt Nadareski
251b3754e4 Add Mattel HyperScan detection (fixes #661) 2024-02-26 13:17:46 -05:00
Matt Nadareski
963acc3336 Update to BinaryObjectScanner 3.1.0 2024-02-26 12:24:40 -05:00
Matt Nadareski
90588a0f8b Show hashes in readonly data (fixes #660) 2024-02-23 22:10:21 -05:00
Matt Nadareski
a56c212488 Change link to WIP builds in README 2024-02-23 21:18:46 -05:00
Matt Nadareski
6484ab5fe0 Remove generation, just in case 2024-02-23 21:04:17 -05:00
Matt Nadareski
1ff48258b8 Generate release notes automatically 2024-02-23 21:00:16 -05:00
Matt Nadareski
81019f9d56 Unified tag for rolling release 2024-02-23 20:57:46 -05:00
Matt Nadareski
d47c435236 Remove unnecessary empty section 2024-02-23 13:47:35 -05:00
Matt Nadareski
d59b114cba Don't omit body when setting body 2024-02-23 13:37:55 -05:00
Matt Nadareski
7f26dcba4e Use commit SHA as body of rolling releases 2024-02-23 13:35:20 -05:00
Matt Nadareski
5e2766f982 Change link to WIP builds in README 2024-02-23 13:32:26 -05:00
Matt Nadareski
c883f899bb Build artifact before upload 2024-02-23 13:28:45 -05:00
Matt Nadareski
8c9950d5fa Use newer download version 2024-02-23 13:11:33 -05:00
Matt Nadareski
3e842af273 Download all artifacts? 2024-02-23 13:06:19 -05:00
Matt Nadareski
b837623da2 Try using download-artifact 2024-02-23 13:01:03 -05:00
Matt Nadareski
6742901243 Revert artifact ID, use name? 2024-02-23 12:53:21 -05:00
Matt Nadareski
d6460a2b68 Use recommendation from upload-artifact 2024-02-23 12:46:23 -05:00
Matt Nadareski
7af59dacc6 Try fixing the artifact upload 2024-02-23 12:41:24 -05:00
Matt Nadareski
fc3ef36fef Attempt to add CD to existing actions 2024-02-23 12:36:55 -05:00
Matt Nadareski
6298487346 Fix link in README 2024-02-23 11:26:45 -05:00
Matt Nadareski
727d9844d5 Fix whitespace that got unwhitespaced 2024-02-23 11:18:36 -05:00
Matt Nadareski
72e7619e2d Remove net35 from MPF... again 2024-02-23 11:17:44 -05:00
Matt Nadareski
24b4647037 Remove now-unnecessary restore step 2024-02-23 11:16:44 -05:00
Matt Nadareski
713b3f0557 Fix net35 build issue 2024-02-23 11:11:48 -05:00
Matt Nadareski
f796a9b131 More tweaks to CI 2024-02-23 10:59:40 -05:00
Matt Nadareski
2cdf92bf92 Rename badges for GHA 2024-02-23 10:26:27 -05:00
Matt Nadareski
ccc1687f1a Add GHA CI status badges 2024-02-23 10:23:18 -05:00
Matt Nadareski
6057ec3a59 Split CI workflow files 2024-02-23 10:20:30 -05:00
Matt Nadareski
2a5e736285 Reorganize solution items 2024-02-23 10:11:39 -05:00
Deterous
010ef9016b Add CI via GitHub Workflows (#657)
* Create ci.yml

* Update ci.yml

* Rename to master

* Update CHANGELIST.md
2024-02-23 07:08:59 -08:00
Deterous
02606318b0 Opt-in automatic IRD creation after PS3 dump (#655)
* Opt-in automatic IRD creation after PS3 dump

* Add tabs before an endregion

* Prevent double checking for existing files

* Spin off IRD creation into new thread
2024-02-22 19:55:10 -08:00
Matt Nadareski
d4f641b122 Update README with current build instructions 2024-02-21 19:28:11 -05:00
Matt Nadareski
a1dd6e2d21 Fix misattributed artifact 2024-02-21 11:14:56 -05:00
Matt Nadareski
d35679d688 Make AppVeyor builds framework-dependent 2024-02-21 11:00:18 -05:00
Matt Nadareski
83f5083ce7 Add x86 builds to AppVeyor 2024-02-21 10:59:30 -05:00
Matt Nadareski
5b6457f4b7 Remove DIC and Aaru bundles from CI 2024-02-21 10:51:05 -05:00
Deterous
c6517d526b Hide unavailable dumping programs (#654) 2024-02-21 07:14:39 -08:00
Matt Nadareski
e35f1fc2ec Readd x86 builds by default 2024-02-21 00:52:40 -05:00
Matt Nadareski
14f4128d4a Fix double git hash version (feat. Deterous) 2024-02-21 00:03:39 -05:00
Matt Nadareski
5465252dc7 Port build script fixes from BOS 2024-02-20 23:16:22 -05:00
Matt Nadareski
2573b47a79 Remove debugging lines from build script 2024-02-20 20:52:49 -05:00
Matt Nadareski
fe20905524 Bump version 2024-02-20 10:34:48 -05:00
Deterous
88f19180a4 Update LibIRD, disable UI elements when creating IRD (#653)
* Update LibIRD, disable UI when creating IRD

* Update changelog
2024-02-20 07:25:03 -08:00
Deterous
de89968a1d Add a GUI for PS3 IRD Creation (#647)
* Create a non-functional Create IRD window

* Add PIC and getkey.log options for IRDs

* Disable IRD creation window for unsupport .NET versions

* Finalise UI and parse inputs

* Add LibIRD package

* Manually define PIC in IRD creation

* Better output file browser

* Bump LibIRD version

* Update changelog

* Custom Disc ID, bump LibIRD version

* Ignore custom Disc ID for BD-50

* Provide a status message when creating IRD

* Better logpath enabled logic

* Nicer PIC UX

* Scrollbar only appears for unusually tall PIC
2024-02-18 17:38:28 -08:00
Matt Nadareski
8fc53c91b0 Limit DVD protection outputs (fixes #651) 2024-02-17 23:23:00 -05:00
Matt Nadareski
1a1fbd4b40 Fix Aaru drive parameter generation (fixes #652) 2024-02-17 23:09:29 -05:00
Matt Nadareski
cac6c3049b Add funworld Photo Play detection (fixes #650) 2024-02-15 12:56:30 -05:00
Matt Nadareski
6a6871e922 Write outputs with UTF-8 2024-02-15 12:00:40 -05:00
Deterous
4a02a3efac Fix DIC log parsing for SS version (#649) 2024-02-12 08:30:50 -08:00
Matt Nadareski
f6eb961af4 Make Redumper the default for new users (fixes #638) 2024-02-06 12:57:26 -05:00
Matt Nadareski
faeaaef02a Remove .NET 6 from auto-builds (fixes #646) 2024-02-06 12:50:01 -05:00
Matt Nadareski
ebf393e634 Bump version and copyright 2024-02-06 11:20:57 -05:00
Deterous
3fbd4ea719 Fix compiler warning (#645)
* Fix compiler warning about PresentationFramework.Aero2

* Update changelog
2024-02-06 07:53:45 -08:00
Matt Nadareski
d09ff6cf1c Enable Windows builds on Linux and Mac 2024-02-05 00:41:11 -05:00
Matt Nadareski
1dc0d57d47 Remove -disc2 from Cleanrip serial (fixes #644) 2024-02-03 19:39:30 -05:00
Matt Nadareski
a748bd4d3a Fix build from rushed code 2024-02-01 11:11:13 -05:00
Matt Nadareski
35dec7fe57 Retrieve serial from Cleanrip (fixes #643) 2024-02-01 11:05:42 -05:00
Deterous
c22d16349a Verbose Redumper log by default (#642) 2024-01-30 19:12:30 -08:00
Deterous
0d77a8950c Exclude extra tracks when finding disc matches (#641) 2024-01-30 19:08:41 -08:00
Deterous
285e94ca69 Parse PSX/PS2/KP2 exe date from logs (#639)
* Parse EXE date from Redumper

* Parse EXE date from DIC logs

* Fix DIC exe date parsing

* Split PS EXE name from EXE info

* Remove redundant path splitting
2024-01-30 19:00:08 -08:00
Matt Nadareski
747ac4ea3b Detect Photo CD 2024-01-25 20:53:21 -05:00
Matt Nadareski
405ae7c7e4 Add UMD handling for the disc info window 2024-01-24 22:56:50 -05:00
Matt Nadareski
f5ebe968c0 Fix information pulling for CleanRip and UIC (fixes #637) 2024-01-24 22:50:16 -05:00
Deterous
06a61b17cb Add a GUI for MPF.Check (#635)
* Draft MPF.Check GUI window

* Functional MPF.Check GUI

* Update changelog

* Show DiscInformationWindow after Check Dump

* Change layout

* Refactor
2024-01-24 11:57:21 -08:00
Matt Nadareski
9e8e4f6e36 Skip warning line in Redumper log 2024-01-24 10:17:58 -05:00
Deterous
fa72211b57 Normalize Disc Title in Submission Info (#634)
* NormalizeDiscTitle for SubmissionInfo

* Update changelog
2024-01-19 09:53:52 -08:00
Deterous
d5f66000a9 Differentiate CD32 from CDTV (#633) 2024-01-18 20:28:23 -08:00
Deterous
a52ba0aa7a Detect CDTV discs (#632) 2024-01-18 20:00:45 -08:00
Deterous
eb045928f9 Prevent crashing on invalid parameters (#631)
* Prevent crashing on invalid parameters

* Parse hex strings properly

* Helper function for hex numbers

* remove region label
2024-01-18 19:16:09 -08:00
fuzzball
440302495b Correct missing space in PVD (#628)
* Remove trim

* Update changelist
2024-01-16 05:54:56 -08:00
Deterous
0732e9db78 Retrieve volume label from logs (#627)
* Retrieve volume label from DIC and redumper logs

* Fix logic

* Remove unnecessary using

* Update changelog

* Sanitise label somewhat

* Refactor
2024-01-15 19:14:34 -08:00
Deterous
a167652b2b Check for presence of complete dump from other programs (#625)
* Check for presence of complete dump from other programs

* Better changelog message

* Refactor
2024-01-10 19:24:17 -08:00
Deterous
cfa07c1918 Allow variables in output path (#624)
* Allow variables in output path and default output path

* Remove angle brackets when normalising path

* Add tooltip hover text for default output path

* Better tooltip formatting

* Use percent sign rather than angle brackets as variable delimiter

* Refactor
2024-01-10 16:42:34 -08:00
Deterous
53b31f91cf Use PSX/PS2 serial as filename when Volume Label not present (#623)
* Use PSX/PS2 serial as volume label if none found

* Refactor
2024-01-09 14:02:17 -08:00
Deterous
01cbd2cff5 Update Redumper to build 311 (#622)
* Update redumper to build 311

* Update changelog

* Deal with new redumper parameters properly
2024-01-08 17:45:08 -08:00
Deterous
65ad629ee0 Cleanup !protectionInfo.txt (#621)
* Remove drive letter from !protectionInfo.txt

* Sort files in !protectionInfo.txt

* Add option for removing drive letter

* Refactor niceties
2024-01-08 17:20:30 -08:00
fuzzball
06adbde715 Support ringcode and PIC for triple/quad-layer (#620)
* Support ringcodes for triple/quad-layer

* Increase PIC length of triple/quad-layer for dic

* Increase PIC length of triple/quad-layer for redumper

* Change the method of determining PIC length

* Update change logs

* Remove braces from if statements
2024-01-08 13:53:48 -08:00
Deterous
1e5000bd8a Support redumper skeleton and hash files (#616)
* Add .hash/.skeleton files to log zip, update redumper to build 306, update SabreTools.Serialization

* Improvements to .skeleton/.hash code

* Revert redumper update, remove redudant string creation

* Deal with nullable strings

* Update changelog

* Invert if for readability
2024-01-05 20:06:46 -08:00
Deterous
8cb0b37e80 Get BD PIC Identifier for redumper (#619)
* Get PIC Identifier for bluray redumper

* Update changelog
2024-01-02 17:50:48 -08:00
Matt Nadareski
32c12e1332 Make missing hash data clearer 2023-12-14 20:27:38 -05:00
Matt Nadareski
09b307aa24 Fix commented out code 2023-12-07 23:22:12 -05:00
Matt Nadareski
a5a8fbbf51 Update Redumper to build 294 2023-12-07 21:38:38 -05:00
Matt Nadareski
b366d236c8 Update RedumpLib 2023-12-05 11:18:58 -05:00
Matt Nadareski
a833e926f3 Bump version 2023-12-04 12:10:19 -05:00
Matt Nadareski
950be07bf0 Handle or suppress some messages 2023-12-01 23:58:01 -05:00
Matt Nadareski
4c5c1417e9 Remove .NET Framework 3.5 from build script 2023-12-01 23:31:59 -05:00
Matt Nadareski
6fdc3412e0 Fix build warning for NRE 2023-12-01 23:28:45 -05:00
Matt Nadareski
807b0c5f9e Fix using SHA-1 for track checks (fixes #613) 2023-12-01 20:46:38 -05:00
Matt Nadareski
9e0b64a1d1 Fix broken tests 2023-12-01 20:39:36 -05:00
Matt Nadareski
8cfbf2d9f1 Bump version 2023-12-01 12:49:43 -05:00
Matt Nadareski
0064737130 Handle most VS and dotnet differences 2023-11-30 23:34:44 -05:00
Matt Nadareski
292e3999c5 Reference .NET Framework 3.0 for 3.5 2023-11-30 20:51:04 -05:00
Matt Nadareski
5ed1e94d84 Import WinFX for .NET Framework 3.5 2023-11-30 18:00:45 -05:00
Matt Nadareski
5b094f57cb Update USE_ALL in Powershell script 2023-11-30 13:43:20 -05:00
Matt Nadareski
8066b5541e Update Redumper to build 271 2023-11-30 13:39:32 -05:00
Matt Nadareski
921d0207c2 Fix cross-framework UI styles 2023-11-30 13:12:56 -05:00
Matt Nadareski
4374ff7f74 Fix most .NET Framework 3.5 issues 2023-11-30 12:58:06 -05:00
Matt Nadareski
0be5825b5e Fix cross-framework UI rendering 2023-11-30 11:54:40 -05:00
Matt Nadareski
14c630bea7 Fix Powershell build script 2023-11-30 03:19:39 -05:00
Matt Nadareski
9a66c685fd Replace build script with Powershell
Thanks to Deterous for the original script conversion.
2023-11-30 00:48:20 -05:00
Matt Nadareski
5e0fa1ad47 Add Disc ID and Key fields in info window (fixes #609) 2023-11-30 00:08:39 -05:00
Matt Nadareski
79065dcc69 Read CSS for some copy protections (fixes #608) 2023-11-29 23:57:58 -05:00
Matt Nadareski
3d7355aee1 Bump version 2023-11-29 23:36:57 -05:00
Matt Nadareski
6e9a6724c3 Update Redumper to build 268 2023-11-29 17:30:46 -05:00
Matt Nadareski
be35acfb48 Support .NET Framework 3.5 in UI 2023-11-23 03:37:00 -05:00
Matt Nadareski
f1a46c2e82 Get UI building with Framework 4.0 again 2023-11-23 03:05:52 -05:00
Matt Nadareski
872959c889 Temporarily remove .NET Framework 4.0 from UI 2023-11-23 02:32:57 -05:00
Matt Nadareski
b848a401f8 Get UI building with Framework 4.0 2023-11-23 01:19:17 -05:00
Matt Nadareski
ee4762f8b3 Support .NET Framework 3.5 in UI.Core 2023-11-22 23:50:37 -05:00
Matt Nadareski
d68bcfb96a Support .NET Framework 2.0
This does not include the UI, as per the same reasons why .NET Framework 4.0 doesn't support it.
2023-11-22 23:38:59 -05:00
Matt Nadareski
d2433e4749 Update changelog 2023-11-22 23:28:57 -05:00
Matt Nadareski
56ec0e7057 Update compatibility libraries 2023-11-22 23:28:24 -05:00
Matt Nadareski
26e5d33d17 Support .NET Framework 3.5
This does not include the UI, as per the same reasons why .NET Framework 4.0 doesn't support it.
2023-11-22 16:26:31 -05:00
Matt Nadareski
8b8b51ace4 Re-enable .NET Framework 4.0 building in Check 2023-11-22 16:02:08 -05:00
Matt Nadareski
f350904441 Re-enable .NET Framework 4.0 building in UI.Core 2023-11-22 16:00:18 -05:00
Matt Nadareski
8e8e3368d0 Re-enable .NET Framework 4.0 building in Core 2023-11-22 15:56:43 -05:00
Matt Nadareski
4d8153dba1 Update to BinaryObjectScanner 3.0.2 2023-11-22 13:46:39 -05:00
Matt Nadareski
e4e4b5ecde Temporarily remove .NET Framework 4.0 2023-11-21 11:13:58 -05:00
Matt Nadareski
8373a6b8f5 Support proper async in .NET Framework 4.0 2023-11-20 16:46:54 -05:00
Matt Nadareski
45c51ebc80 Use TryGetValue on dictionaries 2023-11-20 13:24:17 -05:00
Matt Nadareski
af27085cc1 Get Check building with Framework 4.0 2023-11-20 13:20:48 -05:00
Matt Nadareski
82e3707dce Get UI.Core building with Framework 4.0 2023-11-20 13:19:30 -05:00
Matt Nadareski
85192e8d3e Get Core building with Framework 4.0 2023-11-20 13:15:06 -05:00
Matt Nadareski
6f784a352e Update Xunit packages 2023-11-20 12:29:33 -05:00
Matt Nadareski
ee707cf1af Update to BinaryObjectScanner 3.0.1 2023-11-20 12:28:52 -05:00
Matt Nadareski
a14c998b3b More C# 12 cleanup in Core 2023-11-16 15:33:37 -05:00
Matt Nadareski
004208df6a Perform some post-move cleanup 2023-11-16 15:20:19 -05:00
Matt Nadareski
2f765146d1 Update RedumpLib and use moved methods 2023-11-16 12:45:47 -05:00
Matt Nadareski
a7d548f7ce Trim PS3 serial and add unrelated notes (fixes #607) 2023-11-16 12:18:07 -05:00
Matt Nadareski
fbdb9875f3 Add C#12 syntax to tests 2023-11-15 16:39:52 -05:00
Matt Nadareski
39a524e3cc Fix reversed ringcode test 2023-11-15 16:32:23 -05:00
Matt Nadareski
9740ca3a7a Suppress deprecation warnings 2023-11-15 16:29:47 -05:00
Matt Nadareski
f8d81972bf Prepare XAML for ancient .NET support 2023-11-15 12:10:54 -05:00
Matt Nadareski
fe9302a553 Perform more ancient .NET support work 2023-11-15 11:58:53 -05:00
Matt Nadareski
c0ed7a7a0e Fix TLS for older .NET 2023-11-15 00:18:52 -05:00
Matt Nadareski
b0b48743ac Support ancient .NET in UI 2023-11-15 00:08:43 -05:00
Matt Nadareski
47e79dab31 Support ancient .NET in Check 2023-11-14 23:53:15 -05:00
Matt Nadareski
90edc42fdf Support C# 12 syntax 2023-11-14 23:40:41 -05:00
Matt Nadareski
45db365705 Support ancient .NET in UI Core 2023-11-14 23:14:33 -05:00
Matt Nadareski
952828dddd Support ancient .NET in Core
This does not include .NET Framework 4.0 due to some issues with libraries.
2023-11-14 23:09:55 -05:00
Matt Nadareski
4a1e953ffd Fix BE flag logic bug in DIC (fixes #606) 2023-11-14 21:27:09 -05:00
Matt Nadareski
25740c2936 Zip manufacturer files for Redumper (fixes #604) 2023-11-14 21:19:52 -05:00
Matt Nadareski
3696257940 Add Bandai Pippin detection (fixes #600) 2023-11-14 21:16:47 -05:00
Matt Nadareski
4d46b7db5c Bump version 2023-11-14 16:39:19 -05:00
Matt Nadareski
21f5f29ac0 Update to .NET 8 (fixes #593) 2023-11-14 16:37:41 -05:00
Matt Nadareski
436e198826 Bump library versions 2023-11-14 16:32:32 -05:00
Matt Nadareski
91eb8c96c3 Update changelog 2023-11-08 21:03:27 -05:00
Matt Nadareski
edf0bcb4fe Update documentation for BOS change 2023-11-08 21:03:09 -05:00
Matt Nadareski
1526714ab9 Remove Unshield reference 2023-11-07 14:32:18 -05:00
Matt Nadareski
767e0bb05b Remove .NET Framework 4.8 from issue report 2023-11-07 12:03:45 -05:00
Matt Nadareski
52e781329c Make releases less verbose 2023-11-07 11:13:12 -05:00
Matt Nadareski
0c1395d3ec Remove lingering script reference 2023-11-07 00:23:55 -05:00
Matt Nadareski
791d9cdb7b Consolidate build script information 2023-11-07 00:22:54 -05:00
Matt Nadareski
7d7dc4ee4e Even more README cleanup 2023-11-07 00:18:54 -05:00
Matt Nadareski
97fd04b13a Remove 4.8 note from build script 2023-11-06 23:38:27 -05:00
Matt Nadareski
899db4c8c0 Remove redundant information 2023-11-06 23:15:27 -05:00
Matt Nadareski
bba204cbfe Update README for Legacy 2023-11-06 23:13:08 -05:00
Matt Nadareski
145660e9f9 Address lingering modern .NET syntax 2023-11-06 23:06:11 -05:00
Matt Nadareski
24de14aea5 Remove .NET Framework 4.8 gated code 2023-11-06 22:07:40 -05:00
Matt Nadareski
d57161e4d1 Remove .NET Framework 4.8 from projects 2023-11-06 21:00:07 -05:00
Matt Nadareski
a066a5234a Remove .NET Framework 4.8 from build 2023-11-06 20:57:03 -05:00
Matt Nadareski
229db5dda2 Bump version 2023-11-06 10:14:16 -05:00
Deterous
4f2a8d354a Update to MMI 3.0.0 (#603) 2023-11-06 07:10:25 -08:00
Matt Nadareski
b886471d72 Update changelog 2023-11-05 19:03:52 -05:00
Deterous
2bab266ae2 Enable browsing for Redumper path (#602) 2023-11-05 15:54:09 -08:00
Matt Nadareski
6c5fd9bac8 Fix default layerbreak output (fixes #601) 2023-11-05 11:39:56 -05:00
Matt Nadareski
08e93d7f13 Update changelog 2023-11-03 23:35:41 -04:00
Deterous
7a510e084b Pull PS3 Firmware Version (#599)
* Attempt to read PS3 firmware version

* Improve PS3 Firmware Version parsing

* Enable PS3 Firmware Version detection for all 3 Programs

* Big endian byte location

* Contents field not Comments field
2023-11-03 20:35:24 -07:00
Matt Nadareski
da46d20ffc Fix PS3 version finding 2023-11-03 22:41:22 -04:00
Matt Nadareski
234c0bfbab Try to get PS3 data from SFB 2023-11-03 15:34:05 -04:00
Matt Nadareski
82d60dbf4a Update changelog 2023-11-03 11:23:28 -04:00
Deterous
6dffb80609 Focus main window after child window closes (#596) 2023-11-03 08:23:11 -07:00
Matt Nadareski
267c0e3184 Update Redumper to build 247 2023-11-01 13:07:36 -04:00
Matt Nadareski
033ccbbe67 Update changelog 2023-10-31 19:59:38 -04:00
Deterous
c31b3f5894 Remove psxt001z Pkg Ref in MPF.Test (#592) 2023-10-31 16:58:43 -07:00
Matt Nadareski
9b1a303fea Bump version 2023-10-31 14:21:46 -04:00
Matt Nadareski
80a0f6da35 Attempt to fix window owner issue 2023-10-30 21:59:30 -04:00
Matt Nadareski
0c30eb7a60 Enable HD-DVD for Redumper in UI 2023-10-30 21:55:03 -04:00
Matt Nadareski
7a8125bb71 Update Redumper to build 246 2023-10-30 21:52:34 -04:00
Matt Nadareski
c4beffd845 Add PIC output for Redumper 2023-10-30 21:43:43 -04:00
Matt Nadareski
f97c112a53 Remove duplicate check 2023-10-30 18:44:08 -04:00
Matt Nadareski
5ef43ab6be Fix build 2023-10-30 18:40:17 -04:00
Matt Nadareski
2c399f99bf Update changelog 2023-10-30 18:38:03 -04:00
Deterous
42e9eb0b96 Fix typo in ProgramSupportsMeida (#591) 2023-10-30 15:36:51 -07:00
Matt Nadareski
e67d65f908 Remove .manufacturer for Bluray 2023-10-30 18:32:58 -04:00
Matt Nadareski
4ec8954b14 Handle a couple of messages 2023-10-30 16:42:15 -04:00
Matt Nadareski
1a6abb039c Compile most regex statements 2023-10-30 16:31:50 -04:00
Matt Nadareski
bb5d1e5ac8 Remove now-obsolete notes 2023-10-30 13:07:29 -04:00
Matt Nadareski
03c4c475eb Add Bluray layerbreak support for Redumper 2023-10-30 12:56:58 -04:00
Matt Nadareski
04d7817d28 Enable Bluray dumping with Redumper 2023-10-30 12:45:19 -04:00
Matt Nadareski
7cd100bc53 Update Redumper to build 244 2023-10-30 12:09:38 -04:00
Matt Nadareski
019924232a Remove and Sort Usings 2023-10-30 01:55:56 -04:00
Matt Nadareski
b5fc9f0275 Ignore empty protection results (fixes #590) 2023-10-30 01:05:15 -04:00
Matt Nadareski
44509b72ed Fix whitespace 2023-10-26 12:37:36 -04:00
Matt Nadareski
d532a63dbd Version-gate new switch statement 2023-10-26 12:36:44 -04:00
Matt Nadareski
227785b079 Remove unncessary summary 2023-10-26 12:33:28 -04:00
Matt Nadareski
0e364be998 Separate out static hashing 2023-10-26 12:16:42 -04:00
Matt Nadareski
7ae1f64ee3 Move all hashing to Hasher 2023-10-26 11:56:38 -04:00
Matt Nadareski
92463a103d Make Hasher more consistent 2023-10-26 11:54:13 -04:00
Matt Nadareski
101cdb7b34 Avoid unncessary allocation in hashing 2023-10-26 11:42:36 -04:00
Matt Nadareski
e924299f85 Add other non-cryptographic hashes 2023-10-26 11:37:41 -04:00
Matt Nadareski
b983f7eb4a Move Hash enum and simplify 2023-10-26 11:30:00 -04:00
Matt Nadareski
33b4be8b24 Bump version 2023-10-26 09:35:25 -04:00
Matt Nadareski
71a4edc8ba Update changelog 2023-10-26 09:18:13 -04:00
Deterous
ceb305eb54 Use relative path internally (#589) 2023-10-26 06:17:17 -07:00
Matt Nadareski
0b0d13dcf3 Use Array.Empty in hasher 2023-10-26 01:25:34 -04:00
Matt Nadareski
9f02368622 Fix two small nullability issues 2023-10-26 01:00:09 -04:00
Matt Nadareski
152010ee14 Update to MMI 3.0.0-preview.4 2023-10-26 00:58:44 -04:00
Matt Nadareski
c6d5f0aea5 Update to BurnOutSharp 2.9.0 2023-10-26 00:57:23 -04:00
Matt Nadareski
8c2ad64bb8 Update XUnit packages 2023-10-25 23:19:44 -04:00
Matt Nadareski
fa54d694b6 Fix CRC32 hashing (fixes #587) 2023-10-25 22:38:30 -04:00
Matt Nadareski
dc35b08624 Attempt to parse out PS5 params.json 2023-10-25 16:56:13 -04:00
Matt Nadareski
4429515ba2 Update package versions (fixes #586) 2023-10-25 16:22:32 -04:00
Matt Nadareski
fdbc7b34e5 Update changelog 2023-10-19 20:58:15 -04:00
Deterous
a1ab442cf0 Set UDF CD threshold at 800MB (#585)
* Set UDF CD threshold at 800MB

* Condense DriveFormat checks
2023-10-19 17:56:58 -07:00
Matt Nadareski
9ed5c297f6 Expose suffix setting (fixes #428) 2023-10-18 22:23:40 -04:00
Matt Nadareski
4ce9b214b0 Wire through filename suffix 2023-10-18 22:19:25 -04:00
Matt Nadareski
7dcdadda75 Add filename suffix setting (nw) 2023-10-18 22:04:15 -04:00
Matt Nadareski
f87a4d9fe2 Fix up DumpEnvironment a bit 2023-10-18 21:54:04 -04:00
Matt Nadareski
e4e5d173f0 Rearrange OptionsWindow to be easier to navigate 2023-10-18 16:35:34 -04:00
Matt Nadareski
115b242d59 Add optional file deletion (fixes #126) 2023-10-18 15:49:57 -04:00
Matt Nadareski
706bf8a431 Add deleteable file lists for Redumper and DIC 2023-10-18 15:41:51 -04:00
Matt Nadareski
87ecf1aa99 Add framework for deleteable files 2023-10-18 15:33:32 -04:00
Matt Nadareski
b5cf274333 Simply theme application 2023-10-18 10:31:58 -04:00
Matt Nadareski
4f4b6879b6 Fix drive letter check (fixes #584) 2023-10-18 10:22:05 -04:00
Matt Nadareski
3b19463913 Create method for applying theme 2023-10-18 10:21:40 -04:00
Matt Nadareski
37386cd182 Fix multiple handler invocation 2023-10-18 03:28:41 -04:00
Matt Nadareski
e04bdad16c Add first-run Options title, fix saving bug 2023-10-18 02:59:41 -04:00
Matt Nadareski
e37f12705d Fix drive letter issue in UI 2023-10-18 02:30:38 -04:00
Matt Nadareski
5c8dc2c23a Show options window on first run (fixes #467) 2023-10-18 02:10:29 -04:00
Matt Nadareski
e9121f3b03 Create skeleton for first-run 2023-10-18 01:59:55 -04:00
Matt Nadareski
d68175db4e Convert Drive to use paths internally 2023-10-18 01:47:42 -04:00
Matt Nadareski
9d8181b0e2 Add path variants for PlayStation info 2023-10-18 01:29:26 -04:00
Matt Nadareski
6d657f268a Split InfoTool into 2 classes 2023-10-18 01:17:12 -04:00
Matt Nadareski
3b3c5f823d Bump version 2023-10-17 20:53:07 -04:00
Matt Nadareski
09fc313492 Support Redumper 231 outputs 2023-10-17 20:51:48 -04:00
Matt Nadareski
316d0f6e54 Clean up issue templates 2023-10-16 00:44:57 -04:00
Matt Nadareski
a0033238bd Get SecuROM data from Redumper (fixes #583) 2023-10-16 00:38:56 -04:00
Matt Nadareski
5b1c6a7f46 Update changlog 2023-10-15 21:51:51 -04:00
fuzzball
8c0dff6552 Correct the condition (#582) 2023-10-15 18:51:23 -07:00
Matt Nadareski
43b230c84a Update Redumper to build 230 2023-10-15 21:42:45 -04:00
Matt Nadareski
f1b657011d Enable parameters checkbox by default (fixes #580) 2023-10-15 15:33:18 -04:00
Matt Nadareski
e4d8ac8e1c Fix Redumper retry count not showing 2023-10-14 23:04:05 -04:00
Matt Nadareski
08f44173dd Update changelog 2023-10-13 10:56:10 -04:00
Deterous
54765c71fd Remove code for getting UH from DIC logs (#577) 2023-10-13 07:55:52 -07:00
Matt Nadareski
01f8b49214 Update changelog 2023-10-13 10:54:57 -04:00
Deterous
e8b0b3efaa Improve check for which program supports which media (#578)
* Move ProgramSupportsMedia to MPF.Core.Utilities.Tools

* Implement ProgramSupportsMedia slightly less naively

* ProgramSupportsMedia only considers MPF-supported types
2023-10-13 07:54:10 -07:00
Matt Nadareski
f637938858 Update changelog 2023-10-12 15:47:13 -04:00
Deterous
ae326c1d2f Disable dumping button when Redumper selected with unsupported media type (#576)
* Disable dumping button when HDDVD + Redumper selected

* Exclude Bluray+Redumper as well

* Move program-supports-mediatype logic to helper function

* Fix for net48

* Fix for net48

* Undo fixes for net48
2023-10-12 12:46:44 -07:00
Matt Nadareski
a4a1e6bf0a Suppress some unnecessary messages 2023-10-12 01:27:54 -04:00
Matt Nadareski
ecee44966e Gate some switch expressions 2023-10-12 01:19:47 -04:00
Matt Nadareski
83437977ba Update changelog 2023-10-11 23:56:00 -04:00
Matt Nadareski
8fcac1d425 Cleanup and gated code 2023-10-11 23:55:50 -04:00
Matt Nadareski
705b5f1049 Fix options loading for Check 2023-10-11 16:37:10 -04:00
Matt Nadareski
367d0c104b Bump version 2023-10-11 13:48:32 -04:00
Matt Nadareski
5d4bed2d9e Remove unnecessary namespacing 2023-10-11 13:41:42 -04:00
Matt Nadareski
63756192d8 Enable path browse button by default 2023-10-11 13:13:36 -04:00
Matt Nadareski
b68ec78184 Version-gate some more newer syntax 2023-10-11 13:07:45 -04:00
Matt Nadareski
c55d5183fb Fix Check always showing Help text 2023-10-11 12:54:40 -04:00
Matt Nadareski
f82e925944 Version-gate some newer syntax 2023-10-11 12:37:31 -04:00
Matt Nadareski
c19f9ea173 Var-ify many instances 2023-10-11 12:29:06 -04:00
Matt Nadareski
76b2dd79ab Handle some suggested changes 2023-10-11 12:26:24 -04:00
Matt Nadareski
c96ff23ad1 Update changelog 2023-10-11 12:14:56 -04:00
Matt Nadareski
cd68b55b93 Enable nullability in MPF.Test 2023-10-11 12:14:42 -04:00
Matt Nadareski
cad14d96f7 Enable nullability in MPF.UI.Core 2023-10-11 11:49:56 -04:00
Matt Nadareski
daaf9f1932 Enable nullability in MPF 2023-10-11 11:16:34 -04:00
Matt Nadareski
cb7502b450 Update changelog 2023-10-11 10:34:00 -04:00
Deterous
ece142bbf1 Place message boxes at center of main window (#574) 2023-10-11 07:33:26 -07:00
Matt Nadareski
611fee4605 Remove all but .NET 6 for AppVeyor packaging (fixes #573) 2023-10-10 23:44:30 -04:00
Matt Nadareski
791e2d0272 Enable nullability in Check 2023-10-10 23:22:21 -04:00
Matt Nadareski
81742a4676 Unify handling of enable/disable events 2023-10-10 22:05:39 -04:00
Matt Nadareski
1ff3f2210c Handle numeric disc titles as issue numbers (fixes #543) 2023-10-10 21:55:51 -04:00
Matt Nadareski
be9e4b91d5 Add generic drive flag for Redumper (fixes #546) 2023-10-10 21:45:17 -04:00
Matt Nadareski
854dcc5f95 Add BE read flag for Redumper (fixes #545) 2023-10-10 21:42:16 -04:00
Matt Nadareski
29b71db33a Allow user-supplied information in Check (fixes #572) 2023-10-10 21:27:36 -04:00
Matt Nadareski
2ee64b222a Fix errant version detection issue 2023-10-10 20:56:53 -04:00
Matt Nadareski
afda54f97b Add pull-all flag for Check (fixes #571) 2023-10-10 20:51:39 -04:00
Matt Nadareski
ad37c573b6 Bump version 2023-10-10 20:25:11 -04:00
Matt Nadareski
b6cb3104ae Update changelog 2023-10-10 16:16:06 -04:00
Deterous
af9b0dc214 Fix media combobox not updating (#568)
* Fix media combobox not updating

* Don't call SetCurrentDiscType rather than clear the variable
2023-10-10 13:15:47 -07:00
Matt Nadareski
b5440032de Recentralize some Check functionality 2023-10-10 15:15:42 -04:00
Matt Nadareski
a8e783235c Fix dumping button not enabling 2023-10-10 02:03:18 -04:00
Matt Nadareski
fc97fe99e3 Fix media type ordering 2023-10-10 01:52:47 -04:00
Matt Nadareski
1c73b1133f Consolidate some constants 2023-10-09 11:49:26 -04:00
Matt Nadareski
908eccaafa Remove LogOutputViewModel 2023-10-09 11:44:06 -04:00
Matt Nadareski
e114d126c5 Move MainViewModel to Core 2023-10-09 11:35:49 -04:00
Matt Nadareski
c07b4e4a28 Remove message boxes from MainViewModel 2023-10-09 11:01:43 -04:00
Matt Nadareski
06491a6611 Detected type and selected type are different 2023-10-09 10:35:44 -04:00
Matt Nadareski
b9a35850ad Fix null reference exception in disc type 2023-10-09 00:42:33 -04:00
Matt Nadareski
17a0c5d083 Remove more Windows from MainViewModel 2023-10-09 00:13:40 -04:00
Matt Nadareski
1b9523c799 Remove some message boxes from MainViewModel 2023-10-09 00:03:02 -04:00
Matt Nadareski
ae80ecefc8 Remove WinForms from MainViewModel 2023-10-08 23:46:24 -04:00
Matt Nadareski
50ae32e3db Use callback for logging, fix Options window 2023-10-08 23:40:56 -04:00
Matt Nadareski
2d43873398 Remove MainWindow from MainViewModel 2023-10-08 23:15:24 -04:00
Matt Nadareski
a3df433fef Small updtes to MainViewModel 2023-10-08 23:06:17 -04:00
Matt Nadareski
35e71d8527 Perform most of MainViewModel changes 2023-10-08 22:49:46 -04:00
Matt Nadareski
c5d07e4be1 Start migrating MainViewModel 2023-10-08 20:52:29 -04:00
Matt Nadareski
0df5093b45 Fix log output 2023-10-08 00:39:31 -04:00
Matt Nadareski
05d920d095 Move decoupled view models 2023-10-07 23:37:01 -04:00
Matt Nadareski
d2f7ac9843 Refine info window bindings further 2023-10-07 23:12:40 -04:00
Matt Nadareski
857a302aad Refine options window bindings further 2023-10-07 22:48:43 -04:00
Matt Nadareski
46de589791 Refine options window bindings 2023-10-07 22:40:51 -04:00
Matt Nadareski
f14961061c Handle Redump password changing better 2023-10-07 21:31:39 -04:00
Matt Nadareski
453fcf5cb1 Use binding for more disc info textboxes 2023-10-07 21:23:14 -04:00
Matt Nadareski
24cdb27cdb Split options window code a bit more 2023-10-07 11:47:24 -04:00
Matt Nadareski
24235c5896 Update changelog 2023-10-07 11:32:19 -04:00
Deterous
c226f8cf58 Clarify build instructions in README (#566)
* Update README.md

* publish-win.bat requirements

* Mention publish-nix.sh and its requirements
2023-10-07 08:31:59 -07:00
Matt Nadareski
b8f6c9f65f Split info window code a bit more 2023-10-07 02:19:04 -04:00
Matt Nadareski
bb42e2db10 Split logging code a bit more 2023-10-07 01:59:28 -04:00
Matt Nadareski
db544fd4b7 Move LogLevel enumeration 2023-10-07 01:34:59 -04:00
Matt Nadareski
370c6bde5a Move element items to Core 2023-10-07 01:30:11 -04:00
Matt Nadareski
d7965ee37f Remove unnecessary include 2023-10-07 01:22:50 -04:00
Matt Nadareski
b2c6e07ed1 Fix failing tests 2023-10-07 01:11:56 -04:00
Matt Nadareski
19cef20ceb Allow nullability for modern .NET 2023-10-07 01:02:21 -04:00
Deterous
058a1aeeaa Remove debug symbols in release builds (#565)
* Remove debugging symbols from appveyor builds

* Remove debugging symbols from release build with nix build script

* Remove debug symbols from release build with win build script

* Explicitly define Debug profile

* Add missing Debug profile flag
2023-10-06 18:35:20 -07:00
Matt Nadareski
d185077925 Fix failing tests 2023-10-06 20:42:34 -04:00
Matt Nadareski
e3948ba91b Consolidate into MPF.Core 2023-10-06 20:39:59 -04:00
Matt Nadareski
4a30f94007 Remove IMAPI2 as a dependency (#563)
* Remove IMAPI2 with no substitution

* Remove framework exceptions

* Slight reorganization of code

* Update README with support information

* Remove msbuild-specific AppVeyor items

* Be trickier when it comes to type from size

* Fix formatting of changelist

* Fix "detected" message

* Be smarter about media type based on system
2023-10-06 17:22:09 -07:00
Matt Nadareski
1ae27bf9e3 Fix missing comma 2023-10-04 20:53:33 -04:00
Matt Nadareski
bc88103471 Add submission info preamble 2023-10-04 20:51:07 -04:00
Matt Nadareski
9ec6fbfe52 Don't reverse the CRC32 output 2023-10-04 16:40:46 -04:00
Matt Nadareski
ebdb8de6b3 Use System.IO.Hashing for CRC32 2023-10-04 16:37:33 -04:00
Matt Nadareski
b8b56f6308 Build number not job number 2023-10-04 16:03:11 -04:00
Matt Nadareski
0726f5a402 Undo accidential temp commit 2023-10-04 15:51:10 -04:00
Matt Nadareski
90da5d1a7e Fix errant space in variable name 2023-10-04 15:50:56 -04:00
Matt Nadareski
bd3c518fa0 Job number not build ID 2023-10-04 15:39:11 -04:00
Matt Nadareski
44272f60bf Try out using version number in AppVeyor 2023-10-04 15:31:43 -04:00
Matt Nadareski
640ce8d854 Add note about git being required 2023-10-04 14:53:55 -04:00
Matt Nadareski
74732058f2 Publish, but don't package, release builds 2023-10-04 14:48:56 -04:00
Matt Nadareski
22e480ccd1 Try alternate archive naming 2023-10-04 14:35:46 -04:00
Matt Nadareski
32a0cb0394 Attempt to replace NRT 2023-10-04 14:26:13 -04:00
Matt Nadareski
88551bc2ed Bump version 2023-10-04 13:14:05 -04:00
Matt Nadareski
039af56f6a Be clearer with protection outputs (fixes #561) 2023-10-04 12:51:29 -04:00
Matt Nadareski
7a428e2add Ensure multisession info is populated (fixes #560) 2023-10-04 12:01:01 -04:00
Matt Nadareski
c8dd85eb72 Update redumper to build 221 (fixes #559) 2023-10-03 17:41:23 -04:00
Matt Nadareski
7c7a19c5a0 Clean up csproj files 2023-10-02 18:18:14 -04:00
Matt Nadareski
9bedd26b24 Fix path tests 2023-10-02 17:50:18 -04:00
Matt Nadareski
09afdf52fb Skip system detection on inactive drives (fixes #558) 2023-10-02 01:17:14 -04:00
Matt Nadareski
c69afe69dd Try out more UI functionality 2023-10-02 01:00:31 -04:00
Matt Nadareski
ab18c7920a Ensure popups are topmost 2023-10-01 23:28:58 -04:00
Matt Nadareski
5af1841d13 Fix XGD4 PIC reading (fixes #557) 2023-09-29 11:12:35 -04:00
Matt Nadareski
8c324e3b8b Fix redumper EDC detection output (fixes #556) 2023-09-28 22:30:51 -04:00
Matt Nadareski
d99099d587 Tweak README again 2023-09-27 15:50:30 -04:00
Matt Nadareski
fa7b46a516 Omit track 0.2 and 00.2 from hash search (fixes #554) 2023-09-27 13:28:40 -04:00
Matt Nadareski
f7c746b536 Move to config.json 2023-09-27 11:29:44 -04:00
Matt Nadareski
b6e109133f Add setting for pulling comment/contents (fixes #552) 2023-09-27 10:45:53 -04:00
Matt Nadareski
0d694c1bde Address some warnings and infos 2023-09-27 00:38:00 -04:00
Matt Nadareski
47f45fa46f Stop compiling Chime finally 2023-09-27 00:05:36 -04:00
Matt Nadareski
481f4b41d1 Fully sync AppVeyor build with script 2023-09-26 23:44:06 -04:00
Matt Nadareski
359ad87faa Add placeholders for release builds 2023-09-26 23:40:17 -04:00
Matt Nadareski
ba47cb7da2 Remove errant character from script 2023-09-26 23:30:53 -04:00
Matt Nadareski
8927c49963 Update to MMI 3.0.0-preview.2 2023-09-26 23:26:48 -04:00
Matt Nadareski
21f9668093 Update Nuget packages 2023-09-26 23:01:05 -04:00
Matt Nadareski
371571d13f Bump version 2023-09-26 22:15:05 -04:00
Matt Nadareski
b231f82c4c Reset debug option to false 2023-09-26 21:41:19 -04:00
Matt Nadareski
1741326253 Force information window to top 2023-09-26 21:39:41 -04:00
Matt Nadareski
1878ef5ad6 Combine build scripts 2023-09-26 21:35:43 -04:00
Matt Nadareski
ae42f5edd7 Fix options not saving on update 2023-09-26 21:27:36 -04:00
Matt Nadareski
4362ed71e0 Handle invalid characters when changing program 2023-09-26 21:09:53 -04:00
Matt Nadareski
f02904ea49 Add release publish scripts 2023-09-26 17:13:15 -04:00
Matt Nadareski
abf4eb9b7c Update AppVeyor to match scripts 2023-09-26 16:43:57 -04:00
Matt Nadareski
1f942977cc Normalize publish scripts 2023-09-26 16:15:43 -04:00
Matt Nadareski
7edadd4739 Disable subdump download in AppVeyor 2023-09-26 14:12:32 -04:00
Matt Nadareski
cb09816c63 Add .NET 7 to build scripts 2023-09-26 14:11:33 -04:00
Matt Nadareski
48f0a826ca Update redumper to build 219 2023-09-26 13:59:09 -04:00
Matt Nadareski
9c10842924 Normalize Redumper CSS output (fixes #553) 2023-09-26 11:26:56 -04:00
116 changed files with 21158 additions and 14868 deletions

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
[*.cs]
# SYSLIB1045: Convert to 'GeneratedRegexAttribute'.
dotnet_diagnostic.SYSLIB1045.severity = silent

View File

@@ -8,9 +8,9 @@ assignees: mnadareski
---
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
- .NET 6.0 has known limitations, so make sure that what you're asking for isn't already in another build.
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
If none of those apply, then continue...

View File

@@ -8,9 +8,9 @@ assignees: mnadareski
---
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
- .NET 6.0 has known limitations, so make sure that what you're giving information on isn't already in another build.
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
If none of those apply, then continue...
@@ -19,4 +19,4 @@ If none of those apply, then continue...
A clear and concise description of what the information is. Ex. With the latest build of DumpingProgram, it [...]
**Additional context**
Add any other context or screenshots about the information here.
Add any other context or screenshots about the information here.

View File

@@ -8,16 +8,16 @@ assignees: mnadareski
---
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the issue has already been addressed.
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
- .NET 6.0 has known issues, please try using another build to reproduce the error
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
- Check multiple discs to help narrow down the issue
- Check the Options to see if changing any of those affects your issue.
If all of those fail, then continue...
**Version**
What version are you using?
What version are you using?
- [ ] Stable release (version here)
- [ ] WIP release (version here)
@@ -25,14 +25,15 @@ What version are you using?
**Build**
What runtime version are you using?
- [ ] .NET Framework 4.8 running on (Operating System)
- [ ] .NET 6.0 running on (Operating System)
- [ ] .NET 8.0 running on (Operating System)
**Describe the issue**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'

50
.github/workflows/build_check.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: MPF Check
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
project: [MPF.Check]
runtime: [win-x86, win-x64, linux-x64, osx-x64] #[win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c Debug --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
- name: Archive build
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip ${{ matrix.project }}/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

57
.github/workflows/build_ui.yml vendored Normal file
View File

@@ -0,0 +1,57 @@
name: MPF UI
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
project: [MPF]
runtime: [win-x86, win-x64]
framework: [net8.0-windows] #[net40, net452, net472, net48, netcoreapp3.1, net5.0-windows, net6.0-windows, net7.0-windows, net8.0-windows]
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c Debug --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
- name: Bundle Redumper
run: |
wget https://github.com/superg/redumper/releases/download/build_325/redumper-2024.05.06_build325-win64.zip
unzip redumper-2024.05.06_build325-win64.zip
mkdir -p MPF/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper
mv redumper-2024.05.06_build325-win64/bin/redumper.exe MPF/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper/
- name: Archive build
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip ${{ matrix.project }}/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

23
.github/workflows/check_pr.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Build PR
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-restore --verbosity normal

15
.vscode/tasks.json vendored
View File

@@ -3,22 +3,11 @@
"tasks": [
{
"label": "build",
"command": "msbuild",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/MPF.sln",
"-property:RuntimeIdentifiers=win7-x64"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "msbuild",
"type": "process",
"args": [
"${workspaceFolder}/MPF/MPF.csproj",
"-target:Publish",
"-property:RuntimeIdentifiers=win7-x64"
],
"problemMatcher": "$msCompile"
},

View File

@@ -1,3 +1,474 @@
### 3.1.9 (2024-05-19)
- Update Redumper to build 325
- Fix CleanRip not pulling info (Deterous)
- Fix XboxOne/XboxSX Filename bug (Deterous)
- Trim PIC for XboxOne/XboxSX (Deterous)
- Get volume label from UIC outputs
- Add site code listing to Check
- Update RedumpLib and related
- Update BinaryObjectScanner to 3.1.11
- Remove now-unused Hash enum
- Use IO implementation of IniFile
- Add Xbox Backup Creator support to MPF.Check (Deterous)
- Update BinaryObjectScanner to 3.1.12
- Prefer PlayStation info from Redumper logs (Deterous)
### 3.1.8 (2024-05-09)
- Option for default Redumper leadin retries (Deterous)
- Omit false positives on formatting protections
- Critical update to BinaryObjectScanner 3.1.10
- Add _PFI.bin support for UIC
### 3.1.7 (2024-04-28)
- Critical update to BinaryObjectScanner 3.1.9
### 3.1.6 (2024-04-27)
- Fix parameter parsing for `=` symbol (Deterous)
- Define better default categories (Deterous)
- Custom non-redump Redumper options (Deterous)
- Update packages
- Update packages
### 3.1.5 (2024-04-05)
- Handle `.0.physical` files from Redumpers
- Read C2 error count from Redumper logs
- Read last instance of hash data from Redumper
- Add Konami Python 2 system detection
- Fix outdated information in README
- Fix missing information in README
- Language selections unchecked by default
- Update BinaryObjectScanner to 3.1.3
- Fix information pulling for redumper (fuzz6001)
- Update packages
- Update BinaryObjectScanner to 3.1.4
- Detect Xbox Series X discs (Deterous)
- Enable Windows targeting for test project
- Fix test project project includes
- Fix CleanRip hash output for Check (Deterous)
- Enable label-side mastering SID and toolstamp
- Enable remaining fields for label-side information
- Update BinaryObjectScanner to 3.1.5
### 3.1.4 (2024-03-16)
- Update BinaryObjectScanner to 3.1.2
### 3.1.3 (2024-03-15)
- Gate debug publishing behind use all flag
- Hide layerbreaks if value is 0
- Make GHA debug-only
- Remove GHA pull request builds
- Add PR check workflow
- Don't link to AppVeyor artifacts page anymore
- Add PS3 CFW support to MPF.Check (Deterous)
- Hide size if value is 0 (Deterous)
- Fix title normalization (Deterous)
- Ensure no labels are empty
- Use SabreTools.Hashing
- Update to SabreTools.RedumpLib 1.3.5
- Update packages to latest
- Enable LibIRD for all .NET frameworks (Deterous)
- Try updating PR check action
- Fix config access persmission (Deterous)
- Fix Check UI deadlock (Deterous)
- Fix formatting output formatting
- Update LibIRD to 0.9.0 (Deterous)
- Update packages
- Fix Redumper generic drive type (Deterous)
- Add MPF version to Submission info (Deterous)
- Update to RedumpLib 1.3.6
### 3.1.2 (2024-02-27)
- Remove debugging lines from build script
- Port build script fixes from BOS
- Fix double git hash version (feat. Deterous)
- Readd x86 builds by default
- Hide unavailable dumping programs (Deterous)
- Remove DIC and Aaru bundles from CI
- Add x86 builds to AppVeyor
- Make AppVeyor builds framework-dependent
- Fix misattributed artifact
- Update README with current build instructions
- Opt-in automatic IRD creation after PS3 dump (Deterous)
- Add CI via Github Workflows (Deterous)
- Reorganize solution items
- Split CI workflow files
- Add GHA CI status badges
- Rename badges for GHA
- More tweaks to CI
- Fix net35 build issue
- Remove now-unnecessary restore step
- Remove net35 from MPF... again
- Fix whitespace that got unwhitespaced
- Fix link in README
- Attempt to add CD to existing actions
- Try fixing the artifact upload
- Use recommendation from upload-artifact
- Revert artifact ID, use name?
- Try using download-artifact
- Download all artifacts?
- Use newer download version
- Build artifact before upload
- Change link to WIP builds in README
- Use commit SHA as body of rolling releases
- Don't omit body when setting body
- Remove unnecessary empty section
- Unified tag for rolling release
- Generate release notes automatically
- Remove generation, just in case
- Change link to WIP builds in README
- Show hashes in readonly data
- Update to BinaryObjectScanner 3.1.0
- Add Mattel HyperScan detection
- Pull PS3 Disc Key from redump (Deterous)
### 3.1.1 (2024-02-20)
- Remove .NET 6 from auto-builds
- Make Redumper the default for new users
- Fix DIC log parsing for SS version (Deterous)
- Write outputs with UTF-8
- Add funworld Photo Play detection
- Fix Aaru drive parameter generation
- Limit DVD protection outputs
- Add a GUI for PS3 IRD Creation (Deterous)
- Update LibIRD, disable UI elements when creating IRD (Deterous)
### 3.1.0 (2024-02-06)
- Update RedumpLib
- Update Redumper to build 294
- Fix commented out code
- Make missing hash data clearer
- Get BD PIC Identifier for redumper (Deterous)
- Support redumper skeleton and hash files (Deterous)
- Support ringcode and PIC for triple/quad-layer (fuzz6001)
- Cleanup !protectionInfo.txt (Deterous)
- Update Redumper to build 311 (Deterous)
- Use PSX/PS2 serial as filename when Volume Label not present (Deterous)
- Allow variables in output path (Deterous)
- Check for presence of complete dump from other programs (Deterous)
- Retrieve volume label from logs (Deterous)
- Correct missing space in PVD (fuzz6001)
- Prevent crashing on invalid parameters (Deterous)
- Detect CDTV discs (Deterous)
- Differentiate CD32 from CDTV (Deterous)
- Normalise Disc Titles in Submission Info (Deterous)
- Skip warning line in Redumper log
- Add a GUI for MPF.Check (Deterous)
- Fix information pulling for CleanRip and UIC
- Add UMD handling for the disc info window
- Detect Photo CD
- Parse PSX/PS2/KP2 exe date from logs (Deterous)
- Exclude extra tracks when finding disc matches (Deterous)
- Verbose Redumper log by default (Deterous)
- Retrieve serial from Cleanrip
- Fix build from rushed code
- Remove `-disc2` from Cleanrip serial
- Enable Windows builds on Linux and Mac
- Fix compiler warning (Deterous)
### 3.0.3 (2023-12-04)
- Fix broken tests
- Fix using SHA-1 for track checks
- Fix build warning for NRE
- Remove .NET Framework 3.5 from build script
- Handle or suppress some messages
### 3.0.2 (2023-12-01)
- Read CSS for some copy protections
- Add Disc ID and Key fields in info window
- Replace build script with Powershell
- Fix Powershell build script
- Fix cross-framework UI rendering
- Fix most .NET Framework 3.5 issues
- Fix cross-framework UI styles
- Update Redumper to build 271
- Update USE_ALL in Powershell script
- Import WinFX for .NET Framework 3.5
- Reference .NET Framework 3.0 for 3.5
- Handle most VS and dotnet differences
### 3.0.1 (2023-11-30)
- Add Bandai Pippin detection
- Zip manufacturer files for Redumper
- Fix BE flag logic bug in DIC
- Support ancient .NET in Core
- Support ancient .NET in UI Core
- Support C# 12 syntax
- Support ancient .NET in Check
- Support ancient .NET in UI
- Fix TLS for older .NET
- Perform more ancient .NET support work
- Prepare XAML for ancient .NET support
- Suppress deprecation warnings
- Fix reversed ringcode test
- Add C#12 syntax to tests
- Trim PS3 serial and add unrelated notes
- Update RedumpLib and use moved methods
- Perform some post-move cleanup
- More C# 12 cleanup in Core
- Update to BinaryObjectScanner 3.0.1
- Update Xunit packages
- Get Core building with Framework 4.0
- Get UI.Core building with Framework 4.0
- Get Check building with Framework 4.0
- Use TryGetValue on dictionaries
- Support proper async in .NET Framework 4.0
- Temporarily remove .NET Framework 4.0
- Update to BinaryObjectScanner 3.0.2
- Re-enable .NET Framework 4.0 building in Core
- Re-enable .NET Framework 4.0 building in UI.Core
- Re-enable .NET Framework 4.0 building in Check
- Support .NET Framework 3.5
- Update compatibility libraries
- Support .NET Framework 2.0
- Support .NET Framework 3.5 in UI.Core
- Get UI building with Framework 4.0
- Temporarily remove .NET Framework 4.0 from UI
- Get UI building with Framework 4.0 again
- Support .NET Framework 3.5 in UI
- Update Redumper to build 268
### 3.0.0 (2023-11-14)
- Remove .NET Framework 4.8 from build
- Remove .NET Framework 4.8 from projects
- Remove .NET Framework 4.8 gated code
- Address lingering modern .NET syntax
- Update README for Legacy
- Remove redundant information
- Remove 4.8 note from build script
- Even more README cleanup
- Consolidate build script information
- Remove lingering script reference
- Make releases less verbose
- Remove .NET Framework 4.8 from issue report
- Remove Unshield reference
- Update documentation for BOS change
- Bump library versions
- Update to .NET 8
### 2.7.5 (2023-11-06)
- Remove psxt001z Pkg Ref in MPF.Test (Deterous)
- Update Redumper to build 247
- Focus main window after child window closes (Deterous)
- Try to get PS3 data from SFB
- Fix PS3 version finding
- Pull PS3 Firmware Version (Deterous)
- Fix default layerbreak output
- Enable browsing for Redumper path (Deterous)
- Update to MMI 3.0.0 (Deterous)
### 2.7.4 (2023-10-31)
- Move Hash enum and simplify
- Add other non-cryptographic hashes
- Avoid unncessary allocation in hashing
- Make Hasher more consistent
- Move all hashing to Hasher
- Separate out static hashing
- Remove unncessary summary
- Version-gate new switch statement
- Ignore empty protection results
- Remove and Sort Usings
- Update Redumper to build 244
- Enable Bluray dumping with Redumper
- Add Bluray layerbreak support for Redumper
- Remove now-obsolete notes
- Compile most regex statements
- Handle a couple of messages
- Remove .manufacturer for Bluray
- Fix typo that disables DIC during media check (Deterous)
- Fix build
- Remove duplicate check
- Add PIC output for Redumper
- Update Redumper to build 246
- Enable HD-DVD for Redumper in UI
- Attempt to fix window owner issue
### 2.7.3 (2023-10-26)
- Split InfoTool into 2 classes
- Add path variants for PlayStation info
- Convert Drive to use paths internally
- Create skeleton for first-run
- Show options window on first run
- Fix drive letter issue in UI
- Add first-run Options title, fix saving bug
- Fix multiple handler invocation
- Create method for applying theme
- Fix drive letter check
- Simply theme application
- Add framework for deleteable files
- Add deleteable file lists for Redumper and DIC
- Add optional file deletion
- Rearrange OptionsWindow to be easier to navigate
- Fix up DumpEnvironment a bit
- Add filename suffix setting (nw)
- Wire through filename suffix
- Expose suffix setting
- Set UDF CD threshold at 800MB (Deterous)
- Update package versions
- Attempt to parse out PS5 params.json
- Fix CRC32 hashing
- Update XUnit packages
- Update to BurnOutSharp 2.9.0
- Update to MMI 3.0.0-preview.4
- Fix two small nullability issues
- Use Array.Empty in hasher
- Always use relative path internally (Deterous)
### 2.7.2 (2023-10-17)
- Fix options loading for Check
- Cleanup and gated code
- Gate some switch expressions
- Suppress some unnecessary messages
- Disable dumping button when Redumper selected with unsupported media type (Deterous)
- Improve check for which program supports which media (Deterous)
- Remove code for getting Universal Hash from DIC logs (Deterous)
- Fix Redumper retry count not showing
- Enable parameters checkbox by default
- Update Redumper to build 230
- Fix GetPVD in dic (fuzz6001)
- Get SecuROM data from Redumper
- Clean up issue templates
- Support Redumper 231 outputs
### 2.7.1 (2023-10-11)
- Add pull-all flag for Check
- Fix errant version detection issue
- Allow user-supplied information in Check
- Add BE read flag for Redumper
- Add generic drive flag for Redumper
- Handle numeric disc titles as issue numbers
- Unify handling of enable/disable events
- Enable nullability in Check
- Remove all but .NET 6 for AppVeyor packaging
- Place message boxes at center of main window (Deterous)
- Enable nullability in MPF
- Enable nullability in MPF.UI.Core
- Enable nullability in MPF.Test
- Handle some suggested changes
- Var-ify many instances
- Version-gate some newer syntax
- Fix Check always showing Help text
- Version-gate some more newer syntax
- Enable path browse button by default
- Remove unnecessary namespacing
### 2.7.0 (2023-10-11)
- Attempt to replace NRT
- Try alternate archive naming
- Publish, but don't package, release builds
- Add note about git being required
- Try out using version number in AppVeyor
- Job number not build ID
- Fix errant space in variable name
- Build number not job number
- Use System.IO.Hashing for CRC32
- Don't reverse the CRC32 output
- Add submission info preamble
- Fix missing comma
- Remove IMAPI2 with no substitution
- Remove framework exceptions
- Slight reorganization of code
- Update README with support information
- Remove msbuild-specific AppVeyor items
- Be trickier when it comes to type from size
- Fix "detected" message
- Be smarter about media type based on system
- Consolidate into MPF.Core
- Fix failing tests
- Remove debug symbols in release builds (Deterous)
- Allow nullability for modern .NET
- Fix failing tests
- Remove unnecessary include
- Move element items to Core
- Move LogLevel enumeration
- Split logging code a bit more
- Split info window code a bit more
- Clarify build instructions in README (Deterous)
- Split options window code a bit more
- Use binding for more disc info textboxes
- Handle Redump password changing better
- Refine options window bindings
- Refine options window bindings further
- Refine info window bindings further
- Move decoupled view models
- Fix log output
- Start migrating MainViewModel
- Perform most of MainViewModel changes
- Small updtes to MainViewModel
- Remove MainWindow from MainViewModel
- Use callback for logging, fix Options window
- Remove WinForms from MainViewModel
- Remove some message boxes from MainViewModel
- Remove more Windows from MainViewModel
- Fix null reference exception in disc type
- Detected type and selected type are different
- Remove message boxes from MainViewModel
- Move MainViewModel to Core
- Remove LogOutputViewModel
- Consolidate some constants
- Fix media type ordering
- Fix dumping button not enabling
- Recentralize some Check functionality
- Fix media combobox not updating (Deterous)
### 2.6.6 (2023-10-04)
- Update Nuget packages
- Update to MMI 3.0.0-preview.2
- Remove errant character from script
- Add placeholders for release builds
- Fully sync AppVeyor build with script
- Stop compiling Chime finally
- Address some warnings and infos
- Add setting for pulling comment/contents
- Move to config.json
- Omit track 0.2 and 00.2 from hash search
- Tweak README again
- Fix redumper EDC detection output
- Fix XGD4 PIC reading
- Ensure popups are topmost
- Try out more UI functionality
- Skip system detection on inactive drives
- Fix path tests
- Clean up csproj files
- Update redumper to build 221
- Ensure multisession info is populated
- Be clearer with protection outputs
### 2.6.5 (2023-09-27)
- Normalize Redumper CSS output
- Update redumper to build 219
- Add .NET 7 to build scripts
- Disable subdump download in AppVeyor
- Normalize publish scripts
- Update AppVeyor to match scripts
- Add release publish scripts
- Handle invalid characters when changing program
- Fix options not saving on update
- Combine build scripts
- Force information window to top
- Reset debug option to false
### 2.6.4 (2023-09-25)
- Add CD Projekt ID field

View File

@@ -1,24 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<Title>MPF Check</Title>
<AssemblyName>MPF.Check</AssemblyName>
<Description>Validator for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.9</VersionPrefix>
<PropertyGroup>
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
<!-- Package Properties -->
<Title>MPF Check</Title>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Validator for various dumping programs</Description>
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>
@@ -27,22 +29,17 @@
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.12" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
</Project>

View File

@@ -1,10 +1,9 @@
using System;
using System.IO;
using BurnOutSharp;
using MPF.Core.Converters;
using BinaryObjectScanner;
using MPF.Core;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.Library;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
@@ -15,16 +14,24 @@ namespace MPF.Check
public static void Main(string[] args)
{
// Try processing the standalone arguments
if (ProcessStandaloneArguments(args))
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
if (standaloneProcessed != false)
{
if (standaloneProcessed == null)
DisplayHelp();
return;
}
// Try processing the common arguments
(bool success, MediaType mediaType, RedumpSystem? knownSystem) = ProcessCommonArguments(args);
(bool success, MediaType mediaType, RedumpSystem? knownSystem, var error) = OptionsLoader.ProcessCommonArguments(args);
if (!success)
{
DisplayHelp(error);
return;
}
// Loop through and process options
(Core.Data.Options options, string path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
(var options, var seedInfo, var path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
if (options.InternalProgram == InternalProgram.NONE)
{
DisplayHelp("A program name needs to be provided");
@@ -33,17 +40,17 @@ namespace MPF.Check
// Make new Progress objects
var resultProgress = new Progress<Result>();
resultProgress.ProgressChanged += ProgressUpdated;
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ProgressUpdated;
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
// Validate the supplied credentials
#if NET48
(bool? _, string message) = RedumpWebClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword);
#if NETFRAMEWORK
(bool? _, string? message) = RedumpWebClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty);
#else
(bool? _, string message) = RedumpHttpClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword).ConfigureAwait(false).GetAwaiter().GetResult();
(bool? _, string? message) = RedumpHttpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
if (!string.IsNullOrWhiteSpace(message))
if (!string.IsNullOrEmpty(message))
Console.WriteLine(message);
// Loop through all the rest of the args
@@ -60,14 +67,20 @@ namespace MPF.Check
string filepath = Path.GetFullPath(args[i].Trim('"'));
// Now populate an environment
Drive drive = null;
if (!string.IsNullOrWhiteSpace(path))
drive = Drive.Create(null, path);
Drive? drive = null;
if (!string.IsNullOrEmpty(path))
drive = Drive.Create(null, path!);
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, internalProgram: null, parameters: null);
// Finally, attempt to do the output dance
#if NET40
var resultTask = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress);
resultTask.Wait();
var result = resultTask.Result;
#else
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
Console.WriteLine(result.Message);
}
}
@@ -76,7 +89,7 @@ namespace MPF.Check
/// Display help for MPF.Check
/// </summary>
/// <param name="error">Error string to prefix the help text with</param>
private static void DisplayHelp(string error = null)
private static void DisplayHelp(string? error = null)
{
if (error != null)
Console.WriteLine(error);
@@ -86,6 +99,7 @@ namespace MPF.Check
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
Console.WriteLine("-lc, --listcodes List supported comment/content site codes");
Console.WriteLine("-lm, --listmedia List supported media types");
Console.WriteLine("-ls, --listsystems List supported system types");
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");
@@ -99,101 +113,5 @@ namespace MPF.Check
}
Console.WriteLine();
}
/// <summary>
/// Process common arguments for all functionality
/// </summary>
/// <returns>True if all arguments pass, false otherwise</returns>
private static (bool, MediaType, RedumpSystem?) ProcessCommonArguments(string[] args)
{
// All other use requires at least 3 arguments
if (args.Length < 3)
{
DisplayHelp("Invalid number of arguments");
return (false, MediaType.NONE, null);
}
// Check the MediaType
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
if (mediaType == MediaType.NONE)
{
DisplayHelp($"{args[0]} is not a recognized media type");
return (false, MediaType.NONE, null);
}
// Check the RedumpSystem
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
if (knownSystem == null)
{
DisplayHelp($"{args[1]} is not a recognized system");
return (false, MediaType.NONE, null);
}
return (true, mediaType, knownSystem);
}
/// <summary>
/// Process any standalone arguments for the program
/// </summary>
/// <returns>True if one of the arguments was processed, false otherwise</returns>
private static bool ProcessStandaloneArguments(string[] args)
{
// Help options
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
{
DisplayHelp();
return true;
}
// List options
if (args[0] == "-lm" || args[0] == "--listmedia")
{
Console.WriteLine("Supported Media Types:");
foreach (string mediaType in Extensions.ListMediaTypes())
{
Console.WriteLine(mediaType);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-lp" || args[0] == "--listprograms")
{
Console.WriteLine("Supported Programs:");
foreach (string program in EnumExtensions.ListPrograms())
{
Console.WriteLine(program);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-ls" || args[0] == "--listsystems")
{
Console.WriteLine("Supported Systems:");
foreach (string system in Extensions.ListSystems())
{
Console.WriteLine(system);
}
Console.ReadLine();
return true;
}
return false;
}
/// <summary>
/// Simple process counter to write to console
/// </summary>
private static void ProgressUpdated(object sender, Result value)
{
Console.WriteLine(value.Message);
}
/// <summary>
/// Simple process counter to write to console
/// </summary>
private static void ProgressUpdated(object sender, ProtectionProgress value)
{
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
}
}
}

View File

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

25
MPF.Core/ConsoleLogger.cs Normal file
View File

@@ -0,0 +1,25 @@
using System;
using BinaryObjectScanner;
using MPF.Core.Data;
namespace MPF.Core
{
public static class ConsoleLogger
{
/// <summary>
/// Simple process counter to write to console
/// </summary>
public static void ProgressUpdated(object? sender, Result value)
{
Console.WriteLine(value.Message);
}
/// <summary>
/// Simple process counter to write to console
/// </summary>
public static void ProgressUpdated(object? sender, ProtectionProgress value)
{
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
}
}
}

View File

@@ -1,10 +1,11 @@
using System;
#if NET20 || NET35
using System.Collections.Generic;
#else
using System.Collections.Concurrent;
#endif
using System.IO;
using System.Reflection;
#if NETFRAMEWORK
using IMAPI2;
#endif
using MPF.Core.Data;
using SabreTools.RedumpLib.Data;
@@ -21,60 +22,15 @@ namespace MPF.Core.Converters
/// <returns>InternalDriveType, if possible, null on error</returns>
public static InternalDriveType? ToInternalDriveType(this DriveType driveType)
{
switch (driveType)
return driveType switch
{
case DriveType.CDRom:
return InternalDriveType.Optical;
case DriveType.Fixed:
return InternalDriveType.HardDisk;
case DriveType.Removable:
return InternalDriveType.Removable;
default:
return null;
}
DriveType.CDRom => (InternalDriveType?)InternalDriveType.Optical,
DriveType.Fixed => (InternalDriveType?)InternalDriveType.HardDisk,
DriveType.Removable => (InternalDriveType?)InternalDriveType.Removable,
_ => null,
};
}
#if NETFRAMEWORK
/// <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
@@ -82,7 +38,11 @@ namespace MPF.Core.Converters
/// <summary>
/// Long name method cache
/// </summary>
private static readonly ConcurrentDictionary<Type, MethodInfo> LongNameMethods = new ConcurrentDictionary<Type, MethodInfo>();
#if NET20 || NET35
private static readonly Dictionary<Type, MethodInfo?> LongNameMethods = [];
#else
private static readonly ConcurrentDictionary<Type, MethodInfo?> LongNameMethods = [];
#endif
/// <summary>
/// Get the string representation of a generic enumerable value
@@ -93,20 +53,23 @@ namespace MPF.Core.Converters
{
try
{
var sourceType = value?.GetType();
var sourceType = value.GetType();
sourceType = Nullable.GetUnderlyingType(sourceType) ?? sourceType;
if (!LongNameMethods.TryGetValue(sourceType, out MethodInfo method))
if (!LongNameMethods.TryGetValue(sourceType, out var method))
{
method = typeof(SabreTools.RedumpLib.Data.Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
if (method == null)
method = typeof(EnumConverter).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
method = typeof(Extensions).GetMethod("LongName", [typeof(Nullable<>).MakeGenericType(sourceType)]);
method ??= typeof(EnumConverter).GetMethod("LongName", [typeof(Nullable<>).MakeGenericType(sourceType)]);
#if NET20 || NET35
LongNameMethods[sourceType] = method;
#else
LongNameMethods.TryAdd(sourceType, method);
#endif
}
if (method != null)
return method.Invoke(null, new[] { value }) as string;
return method.Invoke(null, new[] { value }) as string ?? string.Empty;
else
return string.Empty;
}
@@ -124,36 +87,66 @@ namespace MPF.Core.Converters
/// <returns>String representing the value, if possible</returns>
public static string LongName(this InternalProgram? prog)
{
switch (prog)
return (prog) switch
{
#region Dumping support
case InternalProgram.Aaru:
return "Aaru";
case InternalProgram.DiscImageCreator:
return "DiscImageCreator";
case InternalProgram.Redumper:
return "redumper";
InternalProgram.Aaru => "Aaru",
InternalProgram.DiscImageCreator => "DiscImageCreator",
InternalProgram.Redumper => "Redumper",
#endregion
#region Verification support only
case InternalProgram.CleanRip:
return "CleanRip";
case InternalProgram.DCDumper:
return "DCDumper";
case InternalProgram.UmdImageCreator:
return "UmdImageCreator";
InternalProgram.CleanRip => "CleanRip",
InternalProgram.DCDumper => "DCDumper",
InternalProgram.PS3CFW => "PS3 CFW",
InternalProgram.UmdImageCreator => "UmdImageCreator",
InternalProgram.XboxBackupCreator => "XboxBackupCreator",
#endregion
case InternalProgram.NONE:
default:
return "Unknown";
}
InternalProgram.NONE => "Unknown",
_ => "Unknown",
};
}
/// <summary>
/// Get the string representation of the RedumperReadMethod enum values
/// </summary>
/// <param name="method">RedumperReadMethod value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this RedumperReadMethod? method)
{
return (method) switch
{
RedumperReadMethod.D8 => "D8",
RedumperReadMethod.BE => "BE",
RedumperReadMethod.BE_CDDA => "BE_CDDA",
RedumperReadMethod.NONE => "Default",
_ => "Unknown",
};
}
/// <summary>
/// Get the string representation of the RedumperSectorOrder enum values
/// </summary>
/// <param name="order">RedumperSectorOrder value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this RedumperSectorOrder? order)
{
return (order) switch
{
RedumperSectorOrder.DATA_C2_SUB => "DATA_C2_SUB",
RedumperSectorOrder.DATA_SUB_C2 => "DATA_SUB_C2",
RedumperSectorOrder.DATA_SUB => "DATA_SUB",
RedumperSectorOrder.DATA_C2 => "DATA_C2",
RedumperSectorOrder.NONE => "Default",
_ => "Unknown",
};
}
#endregion
@@ -165,42 +158,45 @@ namespace MPF.Core.Converters
/// </summary>
/// <param name="internalProgram">String value to convert</param>
/// <returns>InternalProgram represented by the string, if possible</returns>
public static InternalProgram ToInternalProgram(string internalProgram)
public static InternalProgram ToInternalProgram(string? internalProgram)
{
switch (internalProgram.ToLowerInvariant())
return (internalProgram?.ToLowerInvariant()) switch
{
// 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 "rd":
case "redumper":
return InternalProgram.Redumper;
"aaru"
or "chef"
or "dichef"
or "discimagechef" => InternalProgram.Aaru,
"creator"
or "dic"
or "dicreator"
or "discimagecreator" => InternalProgram.DiscImageCreator,
"rd"
or "redumper" => InternalProgram.Redumper,
// 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;
"cleanrip"
or "cr" => InternalProgram.CleanRip,
"dc"
or "dcd"
or "dcdumper" => InternalProgram.DCDumper,
"ps3cfw"
or "ps3"
or "getkey"
or "managunz"
or "multiman" => InternalProgram.PS3CFW,
"uic"
or "umd"
or "umdcreator"
or "umdimagecreator" => InternalProgram.UmdImageCreator,
"xbc"
or "xbox"
or "xbox360"
or "xboxcreator"
or "xboxbackupcreator" => InternalProgram.XboxBackupCreator,
default:
return InternalProgram.NONE;
}
_ => InternalProgram.NONE,
};
}
/// <summary>
@@ -210,170 +206,190 @@ namespace MPF.Core.Converters
/// <returns>MediaType represented by the string, if possible</returns>
public static MediaType ToMediaType(string type)
{
switch (type.ToLowerInvariant())
return (type.ToLowerInvariant()) switch
{
#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;
"aperture"
or "aperturecard"
or "aperture card" => MediaType.ApertureCard,
"jacquardloom"
or "jacquardloomcard"
or "jacquard loom card" => MediaType.JacquardLoomCard,
"magneticstripe"
or "magneticstripecard"
or "magnetic stripe card" => MediaType.MagneticStripeCard,
"opticalphone"
or "opticalphonecard"
or "optical phonecard" => MediaType.OpticalPhonecard,
"punchcard"
or "punchedcard"
or "punched card" => MediaType.PunchedCard,
"punchtape"
or "punchedtape"
or "punched tape" => 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;
"openreel"
or "openreeltape"
or "open reel tape" => MediaType.OpenReel,
"datacart"
or "datacartridge"
or "datatapecartridge"
or "data tape cartridge" => MediaType.DataCartridge,
"cassette"
or "cassettetape"
or "cassette tape" => 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;
"bd"
or "bdrom"
or "bd-rom"
or "bluray" => MediaType.BluRay,
"cd"
or "cdrom"
or "cd-rom" => MediaType.CDROM,
"dvd"
or "dvd5"
or "dvd-5"
or "dvd9"
or "dvd-9"
or "dvdrom"
or "dvd-rom" => MediaType.DVD,
"fd"
or "floppy"
or "floppydisk"
or "floppy disk"
or "floppy diskette" => MediaType.FloppyDisk,
"floptical" => MediaType.Floptical,
"gd"
or "gdrom"
or "gd-rom" => MediaType.GDROM,
"hddvd"
or "hd-dvd"
or "hddvdrom"
or "hd-dvd-rom" => MediaType.HDDVD,
"hdd"
or "harddisk"
or "hard disk" => MediaType.HardDisk,
"bernoullidisk"
or "iomegabernoullidisk"
or "bernoulli disk"
or "iomega bernoulli disk" => MediaType.IomegaBernoulliDisk,
"jaz"
or "iomegajaz"
or "iomega jaz" => MediaType.IomegaJaz,
"zip"
or "zipdisk"
or "iomegazip"
or "iomega zip" => MediaType.IomegaZip,
"ldrom"
or "lvrom"
or "ld-rom"
or "lv-rom"
or "laserdisc"
or "laservision"
or "ld-rom / lv-rom" => MediaType.LaserDisc,
"64dd"
or "n64dd"
or "64dddisk"
or "n64dddisk"
or "64dd disk"
or "n64dd disk" => MediaType.Nintendo64DD,
"fds"
or "famicom"
or "nfds"
or "nintendofamicom"
or "famicomdisksystem"
or "famicom disk system"
or "famicom disk system disk" => MediaType.NintendoFamicomDiskSystem,
"gc"
or "gamecube"
or "nintendogamecube"
or "nintendo gamecube"
or "gamecube disc"
or "gamecube game disc" => MediaType.NintendoGameCubeGameDisc,
"wii"
or "nintendowii"
or "nintendo wii"
or "nintendo wii disc"
or "wii optical disc" => MediaType.NintendoWiiOpticalDisc,
"wiiu"
or "nintendowiiu"
or "nintendo wiiu"
or "nintendo wiiu disc"
or "wiiu optical disc"
or "wii u optical disc" => MediaType.NintendoWiiUOpticalDisc,
"umd" => 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;
"cartridge" => MediaType.Cartridge,
"ced"
or "rcaced"
or "rca ced"
or "videodisc"
or "rca videodisc" => MediaType.CED,
default:
return MediaType.NONE;
}
_ => MediaType.NONE,
};
}
/// <summary>
/// Get the RedumperReadMethod enum value for a given string
/// </summary>
/// <param name="method">String value to convert</param>
/// <returns>RedumperReadMethod represented by the string, if possible</returns>
public static RedumperReadMethod ToRedumperReadMethod(string? method)
{
return (method?.ToLowerInvariant()) switch
{
"d8" => RedumperReadMethod.D8,
"be" => RedumperReadMethod.BE,
"be_cdda"
or "be cdda"
or "be-cdda"
or "becdda" => RedumperReadMethod.BE_CDDA,
_ => RedumperReadMethod.NONE,
};
}
/// <summary>
/// Get the RedumperSectorOrder enum value for a given string
/// </summary>
/// <param name="order">String value to convert</param>
/// <returns>RedumperSectorOrder represented by the string, if possible</returns>
public static RedumperSectorOrder ToRedumperSectorOrder(string? order)
{
return (order?.ToLowerInvariant()) switch
{
"data_c2_sub"
or "data c2 sub"
or "data-c2-sub"
or "datac2sub" => RedumperSectorOrder.DATA_C2_SUB,
"data_sub_c2"
or "data sub c2"
or "data-sub-c2"
or "datasubc2" => RedumperSectorOrder.DATA_SUB_C2,
"data_sub"
or "data sub"
or "data-sub"
or "datasub" => RedumperSectorOrder.DATA_SUB,
"data_c2"
or "data c2"
or "data-c2"
or "datac2" => RedumperSectorOrder.DATA_C2,
_ => RedumperSectorOrder.NONE,
};
}
#endregion

View File

@@ -14,38 +14,45 @@ namespace MPF.Core.Data
public const string StopDumping = "Stop Dumping";
// Byte arrays for signatures
public static readonly byte[] SaturnSectorZeroStart = new byte[] { 0x53, 0x45, 0x47, 0x41, 0x20, 0x53, 0x45, 0x47, 0x41, 0x53, 0x41, 0x54, 0x55, 0x52, 0x4E, 0x20 };
public static readonly byte[] SaturnSectorZeroStart = [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> HDDVD { 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 };
// Lists of known drive speed ranges
#if NET20 || NET35 || NET40
public static IList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
public static IList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
public static IList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
public static IList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
public static IList<int> Unknown { get; } = new List<int> { 1 };
#else
public 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 };
public static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
public static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
public static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
public static IReadOnlyList<int> Unknown { get; } = new List<int> { 1 };
#endif
/// <summary>
/// Get list of all drive speeds for a given MediaType
/// </summary>
/// <param name="type">MediaType? that represents the current item</param>
/// <returns>Read-only list of drive speeds</returns>
#if NET20 || NET35 || NET40
public static IList<int> GetSpeedsForMediaType(MediaType? type)
#else
public static IReadOnlyList<int> GetSpeedsForMediaType(MediaType? type)
#endif
{
switch (type)
return type switch
{
case MediaType.CDROM:
case MediaType.GDROM:
return CD;
case MediaType.DVD:
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
return DVD;
case MediaType.HDDVD:
return HDDVD;
case MediaType.BluRay:
return BD;
default:
return Unknown;
}
MediaType.CDROM
or MediaType.GDROM => CD,
MediaType.DVD
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => DVD,
MediaType.HDDVD => HDDVD,
MediaType.BluRay => BD,
_ => Unknown,
};
}
}
@@ -54,65 +61,6 @@ namespace MPF.Core.Data
/// </summary>
public static class Template
{
// Manual information
public const string TitleField = "Title";
public const string ForeignTitleField = "Foreign Title (Non-latin)";
public const string DiscNumberField = "Disc Number / Letter";
public const string DiscTitleField = "Disc Title";
public const string SystemField = "System";
public const string MediaTypeField = "Media Type";
public const string CategoryField = "Category";
public const string RegionField = "Region";
public const string LanguagesField = "Languages";
public const string PlaystationLanguageSelectionViaField = "Language Selection Via";
public const string DiscSerialField = "Disc Serial";
public const string BarcodeField = "Barcode";
public const string CommentsField = "Comments";
public const string ContentsField = "Contents";
public const string VersionField = "Version";
public const string EditionField = "Edition/Release";
public const string PlayStation3WiiDiscKeyField = "Disc Key";
public const string PlayStation3DiscIDField = "Disc ID";
public const string GameCubeWiiBCAField = "BCA";
public const string CopyProtectionField = "Copy Protection";
public const string MasteringRingField = "Mastering Code (laser branded/etched)";
public const string MasteringSIDField = "Mastering SID Code";
public const string MouldSIDField = "Mould SID Code";
public const string AdditionalMouldField = "Additional Mould";
public const string ToolstampField = "Toolstamp or Mastering Code (engraved/stamped)";
// Automatic Information
public const string DumpingProgramField = "Dumping Program";
public const string DumpingDateField = "Date";
public const string DumpingDriveManufacturer = "Manufacturer";
public const string DumpingDriveModel = "Model";
public const string DumpingDriveFirmware = "Firmware";
public const string ReportedDiscType = "Reported Disc Type";
public const string PVDField = "Primary Volume Descriptor (PVD)";
public const string DATField = "DAT";
public const string SizeField = "Size";
public const string CRC32Field = "CRC32";
public const string MD5Field = "MD5";
public const string SHA1Field = "SHA1";
public const string FullyMatchingIDField = "Fully Matching ID";
public const string PartiallyMatchingIDsField = "Partially Matching IDs";
public const string ErrorCountField = "Error Count";
public const string CuesheetField = "Cuesheet";
public const string SubIntentionField = "SubIntention Data (SecuROM/LibCrypt)";
public const string WriteOffsetField = "Write Offset";
public const string LayerbreakField = "Layerbreak";
public const string EXEDateBuildDate = "EXE/Build Date";
public const string HeaderField = "Header";
public const string PICField = "Permanent Information & Control (PIC)";
public const string PlayStationEDCField = "EDC";
public const string PlayStationAntiModchipField = "Anti-modchip";
public const string PlayStationLibCryptField = "LibCrypt";
public const string XBOXSSRanges = "Security Sector Ranges";
// Default values
public const string RequiredValue = "(REQUIRED)";
public const string RequiredIfExistsValue = "(REQUIRED, IF EXISTS)";
public const string OptionalValue = "(OPTIONAL)";

View File

@@ -2,14 +2,13 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if NET462_OR_GREATER || NETCOREAPP
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Generic;
using MPF.Core.Converters;
using MPF.Core.Utilities;
using SabreTools.RedumpLib.Data;
#if NETFRAMEWORK
using IMAPI2;
#endif
using MPF.Core.Converters;
using SabreTools.IO;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
@@ -17,7 +16,6 @@ namespace MPF.Core.Data
/// Represents information for a single drive
/// </summary>
/// <remarks>
/// TODO: This needs to be less Windows-centric. Devices do not always have a single letter that can be used.
/// TODO: Can the Aaru models be used instead of the ones I've created here?
/// </remarks>
public class Drive
@@ -32,12 +30,12 @@ namespace MPF.Core.Data
/// <summary>
/// Drive partition format
/// </summary>
public string DriveFormat { get; private set; } = null;
public string? DriveFormat { get; private set; } = null;
/// <summary>
/// Windows drive path
/// </summary>
public string Name { get; private set; } = null;
public string? Name { get; private set; } = null;
/// <summary>
/// Represents if Windows has marked the drive as active
@@ -53,7 +51,7 @@ namespace MPF.Core.Data
/// 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; private set; } = null;
public string? VolumeLabel { get; private set; } = null;
#endregion
@@ -61,31 +59,50 @@ namespace MPF.Core.Data
/// <summary>
/// Media label as read by Windows, formatted to avoid odd outputs
/// If no volume label present, use PSX or PS2 serial if valid
/// Otherwise, use "track" as volume label
/// </summary>
public string FormattedVolumeLabel
public string? FormattedVolumeLabel
{
get
{
string volumeLabel = Template.DiscNotDetected;
if (this.MarkedActive)
string? volumeLabel = Template.DiscNotDetected;
if (!this.MarkedActive)
return volumeLabel;
if (!string.IsNullOrEmpty(this.VolumeLabel))
{
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
volumeLabel = "track";
else
volumeLabel = this.VolumeLabel;
volumeLabel = this.VolumeLabel;
}
else
{
// No Volume Label found, fallback to something sensible
switch (this.GetRedumpSystem(null))
{
case RedumpSystem.SonyPlayStation:
case RedumpSystem.SonyPlayStation2:
InfoTool.GetPlayStationExecutableInfo(this.Name, out string? serial, out _, out _);
volumeLabel = serial ?? "track";
break;
default:
volumeLabel = "track";
break;
}
}
foreach (char c in Path.GetInvalidFileNameChars())
volumeLabel = volumeLabel.Replace(c, '_');
volumeLabel = volumeLabel?.Replace(c, '_');
return volumeLabel;
}
}
/// <summary>
/// Windows drive letter
/// Read-only access to the drive letter
/// </summary>
public char Letter => this.Name == null || this.Name.Length == 0 ? '\0' : this.Name[0];
/// <remarks>Should only be used in UI applications</remarks>
public char? Letter => this.Name?[0] ?? '\0';
#endregion
@@ -99,7 +116,7 @@ namespace MPF.Core.Data
/// </summary>
/// <param name="driveType">InternalDriveType value representing the drive type</param>
/// <param name="devicePath">Path to the device according to the local machine</param>
public static Drive Create(InternalDriveType? driveType, string devicePath)
public static Drive? Create(InternalDriveType? driveType, string devicePath)
{
// Create a new, empty drive object
var drive = new Drive()
@@ -108,7 +125,7 @@ namespace MPF.Core.Data
};
// If we have an invalid device path, return null
if (string.IsNullOrWhiteSpace(devicePath))
if (string.IsNullOrEmpty(devicePath))
return null;
// Sanitize a Windows-formatted long device path
@@ -130,7 +147,7 @@ namespace MPF.Core.Data
/// Populate all fields from a DriveInfo object
/// </summary>
/// <param name="driveInfo">DriveInfo object to populate from</param>
private void PopulateFromDriveInfo(DriveInfo driveInfo)
private void PopulateFromDriveInfo(DriveInfo? driveInfo)
{
// If we have an invalid DriveInfo, just return
if (driveInfo == null || driveInfo == default)
@@ -163,91 +180,86 @@ namespace MPF.Core.Data
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
{
var drives = GetDriveList(ignoreFixedDrives);
drives = drives?.OrderBy(i => i.Letter)?.ToList();
drives = [.. drives.OrderBy(i => i == null ? "\0" : i.Name)];
return drives;
}
/// <summary>
/// Get the current media type from drive letter
/// </summary>
/// <param name="system"></param>
/// <returns></returns>
public (MediaType?, string) GetMediaType()
public (MediaType?, string?) GetMediaType(RedumpSystem? system)
{
// 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 NET6_0_OR_GREATER
else
return GetMediaTypeFromSize();
#endif
// Get the current drive information
string deviceId = null;
bool loaded = false;
try
switch (this.InternalDriveType)
{
// Get the device ID first
CimSession session = CimSession.Create(null);
var collection = session.QueryInstances("root\\CIMV2", "WQL", $"SELECT * FROM Win32_CDROMDrive WHERE Id = '{this.Letter}:\'");
foreach (CimInstance instance in collection)
{
CimKeyedCollection<CimProperty> properties = instance.CimInstanceProperties;
deviceId = (string)properties["DeviceID"]?.Value;
loaded = (bool)properties["MediaLoaded"]?.Value;
}
// 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");
#if NETFRAMEWORK
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);
#else
return (null, "IMAPI2 recorder not supported");
#endif
case Data.InternalDriveType.Floppy:
return (MediaType.FloppyDisk, null);
case Data.InternalDriveType.HardDisk:
return (MediaType.HardDisk, null);
case Data.InternalDriveType.Removable:
return (MediaType.FlashDrive, null);
}
catch (Exception ex)
// Some systems should default to certain media types
switch (system)
{
return (null, ex.Message);
// CD
case RedumpSystem.Panasonic3DOInteractiveMultiplayer:
case RedumpSystem.PhilipsCDi:
case RedumpSystem.SegaDreamcast:
case RedumpSystem.SegaSaturn:
case RedumpSystem.SonyPlayStation:
case RedumpSystem.VideoCD:
return (MediaType.CDROM, null);
// DVD
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
case RedumpSystem.MicrosoftXbox:
case RedumpSystem.MicrosoftXbox360:
return (MediaType.DVD, null);
// HD-DVD
case RedumpSystem.HDDVDVideo:
return (MediaType.HDDVD, null);
// Blu-ray
case RedumpSystem.BDVideo:
case RedumpSystem.MicrosoftXboxOne:
case RedumpSystem.MicrosoftXboxSeriesXS:
case RedumpSystem.SonyPlayStation3:
case RedumpSystem.SonyPlayStation4:
case RedumpSystem.SonyPlayStation5:
return (MediaType.BluRay, null);
// GameCube
case RedumpSystem.NintendoGameCube:
return (MediaType.NintendoGameCubeGameDisc, null);
// Wii
case RedumpSystem.NintendoWii:
return (MediaType.NintendoWiiOpticalDisc, null);
// WiiU
case RedumpSystem.NintendoWiiU:
return (MediaType.NintendoWiiUOpticalDisc, null);
// PSP
case RedumpSystem.SonyPlayStationPortable:
return (MediaType.UMD, null);
}
// Handle optical media by size and filesystem
if (this.TotalSize >= 0 && this.TotalSize <= 800_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
return (MediaType.CDROM, null);
else if (this.TotalSize > 800_000_000 && this.TotalSize <= 8_540_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
return (MediaType.DVD, null);
else if (this.TotalSize > 8_540_000_000)
return (MediaType.BluRay, null);
return (null, "Could not determine media type!");
}
/// <summary>
@@ -257,29 +269,47 @@ namespace MPF.Core.Data
/// <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))
if (string.IsNullOrEmpty(this.Name) || !Directory.Exists(this.Name))
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();
RedumpSystem? systemFromLabel = GetRedumpSystemFromVolumeLabel(this.VolumeLabel);
if (systemFromLabel != null)
return systemFromLabel;
// Get a list of files for quicker checking
#region Arcade
// funworld Photo Play
if (File.Exists(Path.Combine(this.Name, "PP.INF"))
&& Directory.Exists(Path.Combine(this.Name, "PPINC")))
{
return RedumpSystem.funworldPhotoPlay;
}
// Konami Python 2
if (Directory.Exists(Path.Combine(this.Name, "PY2.D")))
{
return RedumpSystem.KonamiPython2;
}
#endregion
#region Consoles
// Bandai Playdia Quick Interactive System
try
{
List<string> files = Directory.EnumerateFiles(drivePath, "*", SearchOption.TopDirectoryOnly).ToList();
#if NET20 || NET35
List<string> files = [.. Directory.GetFiles(this.Name, "*", SearchOption.TopDirectoryOnly)];
#else
List<string> files = Directory.EnumerateFiles(this.Name, "*", SearchOption.TopDirectoryOnly).ToList();
#endif
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
@@ -289,8 +319,37 @@ namespace MPF.Core.Data
}
catch { }
// Bandai Pippin
if (File.Exists(Path.Combine(this.Name, "PippinAuthenticationFile")))
{
return RedumpSystem.BandaiPippin;
}
// Commodore CDTV/CD32
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(this.Name, "S"), "STARTUP-SEQUENCE")))
#else
if (File.Exists(Path.Combine(this.Name, "S", "STARTUP-SEQUENCE")))
#endif
{
if (File.Exists(Path.Combine(this.Name, "CDTV.TM")))
return RedumpSystem.CommodoreAmigaCDTV;
else
return RedumpSystem.CommodoreAmigaCD32;
}
// Mattel HyperScan -- TODO: May need case-insensitivity added
if (File.Exists(Path.Combine(this.Name, "hyper.exe")))
{
return RedumpSystem.MattelHyperScan;
}
// Mattel Fisher-Price iXL
if (File.Exists(Path.Combine(drivePath, "iXL", "iXLUpdater.exe")))
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(this.Name, "iXL"), "iXLUpdater.exe")))
#else
if (File.Exists(Path.Combine(this.Name, "iXL", "iXLUpdater.exe")))
#endif
{
return RedumpSystem.MattelFisherPriceiXL;
}
@@ -298,8 +357,12 @@ namespace MPF.Core.Data
// Microsoft Xbox 360
try
{
if (Directory.Exists(Path.Combine(drivePath, "$SystemUpdate"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any()
if (Directory.Exists(Path.Combine(this.Name, "$SystemUpdate"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
#endif
&& this.TotalSize <= 500_000_000)
{
return RedumpSystem.MicrosoftXbox360;
@@ -307,47 +370,72 @@ namespace MPF.Core.Data
}
catch { }
// Microsoft Xbox One
// Microsoft Xbox One and Series X
try
{
if (Directory.Exists(Path.Combine(drivePath, "MSXC"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "MSXC")).Any())
if (Directory.Exists(Path.Combine(this.Name, "MSXC")))
{
return RedumpSystem.MicrosoftXboxOne;
try
{
#if NET20 || NET35
string catalogjs = Path.Combine(this.Name, Path.Combine("MSXC", Path.Combine("Metadata", "catalog.js")));
#else
string catalogjs = Path.Combine(this.Name, "MSXC", "Metadata", "catalog.js");
#endif
if (!File.Exists(catalogjs))
return RedumpSystem.MicrosoftXboxOne;
SabreTools.Models.Xbox.Catalog? catalog = SabreTools.Serialization.Deserializers.Catalog.DeserializeFile(catalogjs);
if (catalog != null && catalog.Version != null && catalog.Packages != null)
{
if (!double.TryParse(catalog.Version, out double version))
return RedumpSystem.MicrosoftXboxOne;
if (version < 4)
return RedumpSystem.MicrosoftXboxOne;
foreach (var package in catalog.Packages)
{
if (package.Generation != "9")
return RedumpSystem.MicrosoftXboxOne;
}
return RedumpSystem.MicrosoftXboxSeriesXS;
}
}
catch
{
return RedumpSystem.MicrosoftXboxOne;
}
}
}
catch { }
// Sega Dreamcast
if (File.Exists(Path.Combine(drivePath, "IP.BIN")))
if (File.Exists(Path.Combine(this.Name, "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")))
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "IP.BIN"))
|| File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "SP.BIN"))
|| File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "SP_AS.BIN"))
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
#else
if (File.Exists(Path.Combine(this.Name, "_BOOT", "IP.BIN"))
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP.BIN"))
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP_AS.BIN"))
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
#endif
{
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");
string psxExePath = Path.Combine(this.Name, "PSX.EXE");
string systemCnfPath = Path.Combine(this.Name, "SYSTEM.CNF");
if (File.Exists(systemCnfPath))
{
// Check for either BOOT or BOOT2
@@ -365,9 +453,9 @@ namespace MPF.Core.Data
// Sony PlayStation 3
try
{
if (Directory.Exists(Path.Combine(drivePath, "PS3_GAME"))
|| Directory.Exists(Path.Combine(drivePath, "PS3_UPDATE"))
|| File.Exists(Path.Combine(drivePath, "PS3_DISC.SFB")))
if (Directory.Exists(Path.Combine(this.Name, "PS3_GAME"))
|| Directory.Exists(Path.Combine(this.Name, "PS3_UPDATE"))
|| File.Exists(Path.Combine(this.Name, "PS3_DISC.SFB")))
{
return RedumpSystem.SonyPlayStation3;
}
@@ -386,13 +474,17 @@ namespace MPF.Core.Data
// Is used as an on-disc update for the base game app without needing to get update from the internet.
// "/addcont/GAME_SERIAL/CONTENT_ID/ac.pkg" can be found in Redump entry 97619.
// Originally on disc as "/addcont/CUSA00288/FFXIVEXPS400001A/ac.pkg".
if (File.Exists(Path.Combine(drivePath, "PS4", "UPDATE", "PS4UPDATE.PUP")))
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(Path.Combine(this.Name, "PS4"), "UPDATE"), "PS4UPDATE.PUP")))
#else
if (File.Exists(Path.Combine(this.Name, "PS4", "UPDATE", "PS4UPDATE.PUP")))
#endif
{
return RedumpSystem.SonyPlayStation4;
}
// V.Tech V.Flash / V.Smile Pro
if (File.Exists(Path.Combine(drivePath, "0SYSTEM")))
if (File.Exists(Path.Combine(this.Name, "0SYSTEM")))
{
return RedumpSystem.VTechVFlashVSmilePro;
}
@@ -402,7 +494,7 @@ namespace MPF.Core.Data
#region Computers
// Sharp X68000
if (File.Exists(Path.Combine(drivePath, "COMMAND.X")))
if (File.Exists(Path.Combine(this.Name, "COMMAND.X")))
{
return RedumpSystem.SharpX68000;
}
@@ -412,7 +504,7 @@ namespace MPF.Core.Data
#region Video Formats
// BD-Video
if (Directory.Exists(Path.Combine(drivePath, "BDMV")))
if (Directory.Exists(Path.Combine(this.Name, "BDMV")))
{
// Technically BD-Audio has this as well, but it's hard to split that out right now
return RedumpSystem.BDVideo;
@@ -421,14 +513,22 @@ namespace MPF.Core.Data
// DVD-Audio and DVD-Video
try
{
if (Directory.Exists(Path.Combine(drivePath, "AUDIO_TS"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "AUDIO_TS")).Any())
if (Directory.Exists(Path.Combine(this.Name, "AUDIO_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
#endif
{
return RedumpSystem.DVDAudio;
}
else if (Directory.Exists(Path.Combine(drivePath, "VIDEO_TS"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VIDEO_TS")).Any())
else if (Directory.Exists(Path.Combine(this.Name, "VIDEO_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
#endif
{
return RedumpSystem.DVDVideo;
}
@@ -438,19 +538,42 @@ namespace MPF.Core.Data
// HD-DVD-Video
try
{
if (Directory.Exists(Path.Combine(drivePath, "HVDVD_TS"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "HVDVD_TS")).Any())
if (Directory.Exists(Path.Combine(this.Name, "HVDVD_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
#endif
{
return RedumpSystem.HDDVDVideo;
}
}
catch { }
// Photo CD
try
{
if (Directory.Exists(Path.Combine(this.Name, "PHOTO_CD"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "PHOTO_CD")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "PHOTO_CD")).Any())
#endif
{
return RedumpSystem.PhotoCD;
}
}
catch { }
// VCD
try
{
if (Directory.Exists(Path.Combine(drivePath, "VCD"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VCD")).Any())
if (Directory.Exists(Path.Combine(this.Name, "VCD"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "VCD")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VCD")).Any())
#endif
{
return RedumpSystem.VideoCD;
}
@@ -467,95 +590,53 @@ namespace MPF.Core.Data
/// 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()
public static RedumpSystem? GetRedumpSystemFromVolumeLabel(string? volumeLabel)
{
// If the volume label is empty, we can't do anything
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
if (string.IsNullOrEmpty(volumeLabel))
return null;
// Audio CD
if (this.VolumeLabel.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
if (volumeLabel!.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.AudioCD;
// Microsoft Xbox
if (this.VolumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase))
if (volumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox;
else if (this.VolumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase))
else if (volumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox;
// Microsoft Xbox 360
if (this.VolumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase))
if (volumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox360;
else if (this.VolumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase))
else if (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))
//if (volumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase))
// return RedumpSystem.MicrosoftXbox360; // Also for Xbox One?
//if (this.VolumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
//if (volumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
// return RedumpSystem.MicrosoftXbox360;
// Sega Mega-CD / Sega-CD
if (this.VolumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase))
if (volumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SegaMegaCDSegaCD;
// Sony PlayStation 3
if (this.VolumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
if (volumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SonyPlayStation3;
// Sony PlayStation 4
if (this.VolumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase))
if (volumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SonyPlayStation4;
// Sony PlayStation 5
if (this.VolumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase))
if (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.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();
}
}
/// <summary>
/// Refresh the current drive information based on path
/// </summary>
@@ -569,20 +650,6 @@ namespace MPF.Core.Data
#region Helpers
/// <summary>
/// Get the media type for a device path based on size
/// </summary>
/// <returns>MediaType, null on error</returns>
private (MediaType?, string) GetMediaTypeFromSize()
{
if (this.TotalSize >= 0 && this.TotalSize < 800_000_000 && this.DriveFormat == "CDFS")
return (MediaType.CDROM, null);
else if (this.TotalSize >= 400_000_000 && this.TotalSize <= 8_540_000_000 && this.DriveFormat == "UDF")
return (MediaType.DVD, null);
else
return (MediaType.BluRay, null);
}
/// <summary>
/// Get all current attached Drives
/// </summary>
@@ -605,14 +672,14 @@ namespace MPF.Core.Data
// https://github.com/aaru-dps/Aaru/blob/5164a154e2145941472f2ee0aeb2eff3338ecbb3/Aaru.Devices/Windows/ListDevices.cs#L66
// Create an output drive list
List<Drive> drives = new List<Drive>();
var drives = new List<Drive>();
// Get all standard supported drive types
try
{
drives = DriveInfo.GetDrives()
.Where(d => desiredDriveTypes.Contains(d.DriveType))
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name))
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name) ?? new Drive())
.ToList();
}
catch
@@ -621,6 +688,7 @@ namespace MPF.Core.Data
}
// Find and update all floppy drives
#if NET462_OR_GREATER || NETCOREAPP
try
{
CimSession session = CimSession.Create(null);
@@ -632,8 +700,8 @@ namespace MPF.Core.Data
uint? mediaType = properties["MediaType"]?.Value as uint?;
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
{
char devId = (properties["Caption"].Value as string)[0];
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
char devId = (properties["Caption"].Value as string ?? string.Empty)[0];
drives.ForEach(d => { if (d?.Name != null && d.Name[0] == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
}
}
}
@@ -641,6 +709,7 @@ namespace MPF.Core.Data
{
// No-op
}
#endif
return drives;
}

View File

@@ -26,6 +26,44 @@
// Verification support only
CleanRip,
DCDumper,
PS3CFW,
UmdImageCreator,
XboxBackupCreator,
}
/// <summary>
/// Drive read method option
/// </summary>
public enum RedumperReadMethod
{
NONE = 0,
BE,
D8,
BE_CDDA,
}
/// <summary>
/// Drive sector order option
/// </summary>
public enum RedumperSectorOrder
{
NONE = 0,
DATA_C2_SUB,
DATA_SUB_C2,
DATA_SUB,
DATA_C2,
}
/// <summary>
/// Log level for output
/// </summary>
public enum LogLevel
{
USER,
VERBOSE,
ERROR,
SECRET,
}
}

View File

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

View File

@@ -1,42 +1,52 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
using MPF.Core.Converters;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
public class Options : IDictionary<string, string>
public class Options
{
private Dictionary<string, string> _settings;
/// <summary>
/// All settings in the form of a dictionary
/// </summary>
public Dictionary<string, string?> Settings { get; private set; }
/// <summary>
/// Indicate if the program is being run with a clean configuration
/// </summary>
public bool FirstRun
{
get { return GetBooleanSetting(Settings, "FirstRun", true); }
set { Settings["FirstRun"] = value.ToString(); }
}
#region Internal Program
/// <summary>
/// Path to Aaru
/// </summary>
public string AaruPath
public string? AaruPath
{
get { return GetStringSetting(_settings, "AaruPath", "Programs\\Aaru\\Aaru.exe"); }
set { _settings["AaruPath"] = value; }
get { return GetStringSetting(Settings, "AaruPath", "Programs\\Aaru\\Aaru.exe"); }
set { Settings["AaruPath"] = value; }
}
/// <summary>
/// Path to DiscImageCreator
/// </summary>
public string DiscImageCreatorPath
public string? DiscImageCreatorPath
{
get { return GetStringSetting(_settings, "DiscImageCreatorPath", "Programs\\Creator\\DiscImageCreator.exe"); }
set { _settings["DiscImageCreatorPath"] = value; }
get { return GetStringSetting(Settings, "DiscImageCreatorPath", "Programs\\Creator\\DiscImageCreator.exe"); }
set { Settings["DiscImageCreatorPath"] = value; }
}
/// <summary>
/// Path to Redumper
/// </summary>
public string RedumperPath
public string? RedumperPath
{
get { return GetStringSetting(_settings, "RedumperPath", "Programs\\Redumper\\redumper.exe"); }
set { _settings["RedumperPath"] = value; }
get { return GetStringSetting(Settings, "RedumperPath", "Programs\\Redumper\\redumper.exe"); }
set { Settings["RedumperPath"] = value; }
}
/// <summary>
@@ -46,13 +56,13 @@ namespace MPF.Core.Data
{
get
{
string valueString = GetStringSetting(_settings, "InternalProgram", InternalProgram.DiscImageCreator.ToString());
var valueString = GetStringSetting(Settings, "InternalProgram", InternalProgram.Redumper.ToString());
var valueEnum = EnumConverter.ToInternalProgram(valueString);
return valueEnum == InternalProgram.NONE ? InternalProgram.DiscImageCreator : valueEnum;
return valueEnum == InternalProgram.NONE ? InternalProgram.Redumper : valueEnum;
}
set
{
_settings["InternalProgram"] = value.ToString();
Settings["InternalProgram"] = value.ToString();
}
}
@@ -65,8 +75,8 @@ namespace MPF.Core.Data
/// </summary>
public bool EnableDarkMode
{
get { return GetBooleanSetting(_settings, "EnableDarkMode", false); }
set { _settings["EnableDarkMode"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "EnableDarkMode", false); }
set { Settings["EnableDarkMode"] = value.ToString(); }
}
/// <summary>
@@ -74,8 +84,8 @@ namespace MPF.Core.Data
/// </summary>
public bool CheckForUpdatesOnStartup
{
get { return GetBooleanSetting(_settings, "CheckForUpdatesOnStartup", true); }
set { _settings["CheckForUpdatesOnStartup"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "CheckForUpdatesOnStartup", true); }
set { Settings["CheckForUpdatesOnStartup"] = value.ToString(); }
}
/// <summary>
@@ -83,17 +93,17 @@ namespace MPF.Core.Data
/// </summary>
public bool FastUpdateLabel
{
get { return GetBooleanSetting(_settings, "FastUpdateLabel", false); }
set { _settings["FastUpdateLabel"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "FastUpdateLabel", false); }
set { Settings["FastUpdateLabel"] = value.ToString(); }
}
/// <summary>
/// Default output path for dumps
/// </summary>
public string DefaultOutputPath
public string? DefaultOutputPath
{
get { return GetStringSetting(_settings, "DefaultOutputPath", "ISO"); }
set { _settings["DefaultOutputPath"] = value; }
get { return GetStringSetting(Settings, "DefaultOutputPath", "ISO"); }
set { Settings["DefaultOutputPath"] = value; }
}
/// <summary>
@@ -103,13 +113,13 @@ namespace MPF.Core.Data
{
get
{
string valueString = GetStringSetting(_settings, "DefaultSystem", null);
var valueEnum = Extensions.ToRedumpSystem(valueString);
var valueString = GetStringSetting(Settings, "DefaultSystem", null);
var valueEnum = Extensions.ToRedumpSystem(valueString ?? string.Empty);
return valueEnum;
}
set
{
_settings["DefaultSystem"] = value.LongName();
Settings["DefaultSystem"] = value.LongName();
}
}
@@ -119,8 +129,8 @@ namespace MPF.Core.Data
/// <remarks>This is a hidden setting</remarks>
public bool ShowDebugViewMenuItem
{
get { return GetBooleanSetting(_settings, "ShowDebugViewMenuItem", false); }
set { _settings["ShowDebugViewMenuItem"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "ShowDebugViewMenuItem", false); }
set { Settings["ShowDebugViewMenuItem"] = value.ToString(); }
}
#endregion
@@ -132,8 +142,8 @@ namespace MPF.Core.Data
/// </summary>
public int PreferredDumpSpeedCD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedCD", 24); }
set { _settings["PreferredDumpSpeedCD"] = value.ToString(); }
get { return GetInt32Setting(Settings, "PreferredDumpSpeedCD", 24); }
set { Settings["PreferredDumpSpeedCD"] = value.ToString(); }
}
/// <summary>
@@ -141,8 +151,8 @@ namespace MPF.Core.Data
/// </summary>
public int PreferredDumpSpeedDVD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedDVD", 16); }
set { _settings["PreferredDumpSpeedDVD"] = value.ToString(); }
get { return GetInt32Setting(Settings, "PreferredDumpSpeedDVD", 16); }
set { Settings["PreferredDumpSpeedDVD"] = value.ToString(); }
}
/// <summary>
@@ -150,8 +160,8 @@ namespace MPF.Core.Data
/// </summary>
public int PreferredDumpSpeedHDDVD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedHDDVD", 8); }
set { _settings["PreferredDumpSpeedHDDVD"] = value.ToString(); }
get { return GetInt32Setting(Settings, "PreferredDumpSpeedHDDVD", 8); }
set { Settings["PreferredDumpSpeedHDDVD"] = value.ToString(); }
}
/// <summary>
@@ -159,8 +169,8 @@ namespace MPF.Core.Data
/// </summary>
public int PreferredDumpSpeedBD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedBD", 8); }
set { _settings["PreferredDumpSpeedBD"] = value.ToString(); }
get { return GetInt32Setting(Settings, "PreferredDumpSpeedBD", 8); }
set { Settings["PreferredDumpSpeedBD"] = value.ToString(); }
}
#endregion
@@ -172,8 +182,8 @@ namespace MPF.Core.Data
/// </summary>
public bool AaruEnableDebug
{
get { return GetBooleanSetting(_settings, "AaruEnableDebug", false); }
set { _settings["AaruEnableDebug"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "AaruEnableDebug", false); }
set { Settings["AaruEnableDebug"] = value.ToString(); }
}
/// <summary>
@@ -181,8 +191,8 @@ namespace MPF.Core.Data
/// </summary>
public bool AaruEnableVerbose
{
get { return GetBooleanSetting(_settings, "AaruEnableVerbose", false); }
set { _settings["AaruEnableVerbose"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "AaruEnableVerbose", false); }
set { Settings["AaruEnableVerbose"] = value.ToString(); }
}
/// <summary>
@@ -190,8 +200,8 @@ namespace MPF.Core.Data
/// </summary>
public bool AaruForceDumping
{
get { return GetBooleanSetting(_settings, "AaruForceDumping", true); }
set { _settings["AaruForceDumping"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "AaruForceDumping", true); }
set { Settings["AaruForceDumping"] = value.ToString(); }
}
/// <summary>
@@ -199,8 +209,8 @@ namespace MPF.Core.Data
/// </summary>
public int AaruRereadCount
{
get { return GetInt32Setting(_settings, "AaruRereadCount", 5); }
set { _settings["AaruRereadCount"] = value.ToString(); }
get { return GetInt32Setting(Settings, "AaruRereadCount", 5); }
set { Settings["AaruRereadCount"] = value.ToString(); }
}
/// <summary>
@@ -208,8 +218,8 @@ namespace MPF.Core.Data
/// </summary>
public bool AaruStripPersonalData
{
get { return GetBooleanSetting(_settings, "AaruStripPersonalData", false); }
set { _settings["AaruStripPersonalData"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "AaruStripPersonalData", false); }
set { Settings["AaruStripPersonalData"] = value.ToString(); }
}
#endregion
@@ -221,8 +231,8 @@ namespace MPF.Core.Data
/// </summary>
public bool DICMultiSectorRead
{
get { return GetBooleanSetting(_settings, "DICMultiSectorRead", false); }
set { _settings["DICMultiSectorRead"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "DICMultiSectorRead", false); }
set { Settings["DICMultiSectorRead"] = value.ToString(); }
}
/// <summary>
@@ -230,8 +240,8 @@ namespace MPF.Core.Data
/// </summary>
public int DICMultiSectorReadValue
{
get { return GetInt32Setting(_settings, "DICMultiSectorReadValue", 0); }
set { _settings["DICMultiSectorReadValue"] = value.ToString(); }
get { return GetInt32Setting(Settings, "DICMultiSectorReadValue", 0); }
set { Settings["DICMultiSectorReadValue"] = value.ToString(); }
}
/// <summary>
@@ -244,8 +254,8 @@ namespace MPF.Core.Data
/// </remarks>
public bool DICParanoidMode
{
get { return GetBooleanSetting(_settings, "DICParanoidMode", false); }
set { _settings["DICParanoidMode"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "DICParanoidMode", false); }
set { Settings["DICParanoidMode"] = value.ToString(); }
}
/// <summary>
@@ -253,8 +263,8 @@ namespace MPF.Core.Data
/// </summary>
public bool DICQuietMode
{
get { return GetBooleanSetting(_settings, "DICQuietMode", false); }
set { _settings["DICQuietMode"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "DICQuietMode", false); }
set { Settings["DICQuietMode"] = value.ToString(); }
}
/// <summary>
@@ -262,8 +272,8 @@ namespace MPF.Core.Data
/// </summary>
public int DICRereadCount
{
get { return GetInt32Setting(_settings, "DICRereadCount", 20); }
set { _settings["DICRereadCount"] = value.ToString(); }
get { return GetInt32Setting(Settings, "DICRereadCount", 20); }
set { Settings["DICRereadCount"] = value.ToString(); }
}
/// <summary>
@@ -271,8 +281,8 @@ namespace MPF.Core.Data
/// </summary>
public int DICDVDRereadCount
{
get { return GetInt32Setting(_settings, "DICDVDRereadCount", 10); }
set { _settings["DICDVDRereadCount"] = value.ToString(); }
get { return GetInt32Setting(Settings, "DICDVDRereadCount", 10); }
set { Settings["DICDVDRereadCount"] = value.ToString(); }
}
/// <summary>
@@ -280,8 +290,8 @@ namespace MPF.Core.Data
/// </summary>
public bool DICResetDriveAfterDump
{
get { return GetBooleanSetting(_settings, "DICResetDriveAfterDump", false); }
set { _settings["DICResetDriveAfterDump"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "DICResetDriveAfterDump", false); }
set { Settings["DICResetDriveAfterDump"] = value.ToString(); }
}
/// <summary>
@@ -289,8 +299,8 @@ namespace MPF.Core.Data
/// </summary>
public bool DICUseCMIFlag
{
get { return GetBooleanSetting(_settings, "DICUseCMIFlag", false); }
set { _settings["DICUseCMIFlag"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "DICUseCMIFlag", false); }
set { Settings["DICUseCMIFlag"] = value.ToString(); }
}
#endregion
@@ -302,8 +312,17 @@ namespace MPF.Core.Data
/// </summary>
public bool RedumperEnableDebug
{
get { return GetBooleanSetting(_settings, "RedumperEnableDebug", false); }
set { _settings["RedumperEnableDebug"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "RedumperEnableDebug", false); }
set { Settings["RedumperEnableDebug"] = value.ToString(); }
}
/// <summary>
/// Enable Redumper custom lead-in retries for Plextor drives
/// </summary>
public bool RedumperEnableLeadinRetry
{
get { return GetBooleanSetting(Settings, "RedumperEnableLeadinRetry", false); }
set { Settings["RedumperEnableLeadinRetry"] = value.ToString(); }
}
/// <summary>
@@ -311,8 +330,67 @@ namespace MPF.Core.Data
/// </summary>
public bool RedumperEnableVerbose
{
get { return GetBooleanSetting(_settings, "RedumperEnableVerbose", false); }
set { _settings["RedumperEnableVerbose"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "RedumperEnableVerbose", true); }
set { Settings["RedumperEnableVerbose"] = value.ToString(); }
}
/// <summary>
/// Default number of redumper Plextor leadin retries
/// </summary>
public int RedumperLeadinRetryCount
{
get { return GetInt32Setting(Settings, "RedumperLeadinRetryCount", 4); }
set { Settings["RedumperLeadinRetryCount"] = value.ToString(); }
}
/// <summary>
/// Enable options incompatible with redump submissions
/// </summary>
public bool RedumperNonRedumpMode
{
get { return GetBooleanSetting(Settings, "RedumperNonRedumpMode", false); }
set { Settings["RedumperNonRedumpMode"] = value.ToString(); }
}
/// <summary>
/// Enable generic drive type by default with Redumper
/// </summary>
public bool RedumperUseGenericDriveType
{
get { return GetBooleanSetting(Settings, "RedumperUseGenericDriveType", false); }
set { Settings["RedumperUseGenericDriveType"] = value.ToString(); }
}
/// <summary>
/// Currently selected default redumper read method
/// </summary>
public RedumperReadMethod RedumperReadMethod
{
get
{
var valueString = GetStringSetting(Settings, "RedumperReadMethod", RedumperReadMethod.NONE.ToString());
return EnumConverter.ToRedumperReadMethod(valueString);
}
set
{
Settings["RedumperReadMethod"] = value.ToString();
}
}
/// <summary>
/// Currently selected default redumper sector order
/// </summary>
public RedumperSectorOrder RedumperSectorOrder
{
get
{
var valueString = GetStringSetting(Settings, "RedumperSectorOrder", RedumperSectorOrder.NONE.ToString());
return EnumConverter.ToRedumperSectorOrder(valueString);
}
set
{
Settings["RedumperSectorOrder"] = value.ToString();
}
}
/// <summary>
@@ -320,8 +398,8 @@ namespace MPF.Core.Data
/// </summary>
public int RedumperRereadCount
{
get { return GetInt32Setting(_settings, "RedumperRereadCount", 20); }
set { _settings["RedumperRereadCount"] = value.ToString(); }
get { return GetInt32Setting(Settings, "RedumperRereadCount", 20); }
set { Settings["RedumperRereadCount"] = value.ToString(); }
}
#endregion
@@ -333,8 +411,8 @@ namespace MPF.Core.Data
/// </summary>
public bool ScanForProtection
{
get { return GetBooleanSetting(_settings, "ScanForProtection", true); }
set { _settings["ScanForProtection"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "ScanForProtection", true); }
set { Settings["ScanForProtection"] = value.ToString(); }
}
/// <summary>
@@ -342,8 +420,8 @@ namespace MPF.Core.Data
/// </summary>
public bool OutputSeparateProtectionFile
{
get { return GetBooleanSetting(_settings, "OutputSeparateProtectionFile", true); }
set { _settings["OutputSeparateProtectionFile"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "OutputSeparateProtectionFile", true); }
set { Settings["OutputSeparateProtectionFile"] = value.ToString(); }
}
/// <summary>
@@ -351,8 +429,8 @@ namespace MPF.Core.Data
/// </summary>
public bool AddPlaceholders
{
get { return GetBooleanSetting(_settings, "AddPlaceholders", true); }
set { _settings["AddPlaceholders"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "AddPlaceholders", true); }
set { Settings["AddPlaceholders"] = value.ToString(); }
}
/// <summary>
@@ -360,8 +438,17 @@ namespace MPF.Core.Data
/// </summary>
public bool PromptForDiscInformation
{
get { return GetBooleanSetting(_settings, "PromptForDiscInformation", true); }
set { _settings["PromptForDiscInformation"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "PromptForDiscInformation", true); }
set { Settings["PromptForDiscInformation"] = value.ToString(); }
}
/// <summary>
/// Pull all information from Redump if signed in
/// </summary>
public bool PullAllInformation
{
get { return GetBooleanSetting(Settings, "PullAllInformation", false); }
set { Settings["PullAllInformation"] = value.ToString(); }
}
/// <summary>
@@ -369,8 +456,8 @@ namespace MPF.Core.Data
/// </summary>
public bool EnableTabsInInputFields
{
get { return GetBooleanSetting(_settings, "EnableTabsInInputFields", false); }
set { _settings["EnableTabsInInputFields"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "EnableTabsInInputFields", false); }
set { Settings["EnableTabsInInputFields"] = value.ToString(); }
}
/// <summary>
@@ -378,8 +465,8 @@ namespace MPF.Core.Data
/// </summary>
public bool EnableRedumpCompatibility
{
get { return GetBooleanSetting(_settings, "EnableRedumpCompatibility", true); }
set { _settings["EnableRedumpCompatibility"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "EnableRedumpCompatibility", true); }
set { Settings["EnableRedumpCompatibility"] = value.ToString(); }
}
/// <summary>
@@ -387,8 +474,8 @@ namespace MPF.Core.Data
/// </summary>
public bool ShowDiscEjectReminder
{
get { return GetBooleanSetting(_settings, "ShowDiscEjectReminder", true); }
set { _settings["ShowDiscEjectReminder"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "ShowDiscEjectReminder", true); }
set { Settings["ShowDiscEjectReminder"] = value.ToString(); }
}
/// <summary>
@@ -396,8 +483,8 @@ namespace MPF.Core.Data
/// </summary>
public bool EjectAfterDump
{
get { return GetBooleanSetting(_settings, "EjectAfterDump", false); }
set { _settings["EjectAfterDump"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "EjectAfterDump", false); }
set { Settings["EjectAfterDump"] = value.ToString(); }
}
/// <summary>
@@ -405,8 +492,8 @@ namespace MPF.Core.Data
/// </summary>
public bool IgnoreFixedDrives
{
get { return GetBooleanSetting(_settings, "IgnoreFixedDrives", true); }
set { _settings["IgnoreFixedDrives"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "IgnoreFixedDrives", true); }
set { Settings["IgnoreFixedDrives"] = value.ToString(); }
}
/// <summary>
@@ -414,8 +501,17 @@ namespace MPF.Core.Data
/// </summary>
public bool ToolsInSeparateWindow
{
get { return GetBooleanSetting(_settings, "ToolsInSeparateWindow", true); }
set { _settings["ToolsInSeparateWindow"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "ToolsInSeparateWindow", true); }
set { Settings["ToolsInSeparateWindow"] = value.ToString(); }
}
/// <summary>
/// Add the dump filename as a suffix to the auto-generated files
/// </summary>
public bool AddFilenameSuffix
{
get { return GetBooleanSetting(Settings, "AddFilenameSuffix", false); }
set { Settings["AddFilenameSuffix"] = value.ToString(); }
}
/// <summary>
@@ -423,8 +519,8 @@ namespace MPF.Core.Data
/// </summary>
public bool OutputSubmissionJSON
{
get { return GetBooleanSetting(_settings, "OutputSubmissionJSON", false); }
set { _settings["OutputSubmissionJSON"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "OutputSubmissionJSON", false); }
set { Settings["OutputSubmissionJSON"] = value.ToString(); }
}
/// <summary>
@@ -432,8 +528,8 @@ namespace MPF.Core.Data
/// </summary>
public bool IncludeArtifacts
{
get { return GetBooleanSetting(_settings, "IncludeArtifacts", false); }
set { _settings["IncludeArtifacts"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "IncludeArtifacts", false); }
set { Settings["IncludeArtifacts"] = value.ToString(); }
}
/// <summary>
@@ -441,8 +537,26 @@ namespace MPF.Core.Data
/// </summary>
public bool CompressLogFiles
{
get { return GetBooleanSetting(_settings, "CompressLogFiles", true); }
set { _settings["CompressLogFiles"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "CompressLogFiles", true); }
set { Settings["CompressLogFiles"] = value.ToString(); }
}
/// <summary>
/// Delete unnecessary files to reduce space
/// </summary>
public bool DeleteUnnecessaryFiles
{
get { return GetBooleanSetting(Settings, "DeleteUnnecessaryFiles", false); }
set { Settings["DeleteUnnecessaryFiles"] = value.ToString(); }
}
/// <summary>
/// Create a PS3 IRD file after dumping PS3 BD-ROM discs
/// </summary>
public bool CreateIRDAfterDumping
{
get { return GetBooleanSetting(Settings, "CreateIRDAfterDumping", false); }
set { Settings["CreateIRDAfterDumping"] = value.ToString(); }
}
#endregion
@@ -454,8 +568,8 @@ namespace MPF.Core.Data
/// </summary>
public bool SkipMediaTypeDetection
{
get { return GetBooleanSetting(_settings, "SkipMediaTypeDetection", false); }
set { _settings["SkipMediaTypeDetection"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "SkipMediaTypeDetection", false); }
set { Settings["SkipMediaTypeDetection"] = value.ToString(); }
}
/// <summary>
@@ -463,8 +577,8 @@ namespace MPF.Core.Data
/// </summary>
public bool SkipSystemDetection
{
get { return GetBooleanSetting(_settings, "SkipSystemDetection", false); }
set { _settings["SkipSystemDetection"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "SkipSystemDetection", false); }
set { Settings["SkipSystemDetection"] = value.ToString(); }
}
#endregion
@@ -476,8 +590,8 @@ namespace MPF.Core.Data
/// </summary>
public bool ScanArchivesForProtection
{
get { return GetBooleanSetting(_settings, "ScanArchivesForProtection", true); }
set { _settings["ScanArchivesForProtection"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "ScanArchivesForProtection", true); }
set { Settings["ScanArchivesForProtection"] = value.ToString(); }
}
/// <summary>
@@ -485,8 +599,8 @@ namespace MPF.Core.Data
/// </summary>
public bool ScanPackersForProtection
{
get { return GetBooleanSetting(_settings, "ScanPackersForProtection", false); }
set { _settings["ScanPackersForProtection"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "ScanPackersForProtection", false); }
set { Settings["ScanPackersForProtection"] = value.ToString(); }
}
/// <summary>
@@ -494,8 +608,17 @@ namespace MPF.Core.Data
/// </summary>
public bool IncludeDebugProtectionInformation
{
get { return GetBooleanSetting(_settings, "IncludeDebugProtectionInformation", false); }
set { _settings["IncludeDebugProtectionInformation"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "IncludeDebugProtectionInformation", false); }
set { Settings["IncludeDebugProtectionInformation"] = value.ToString(); }
}
/// <summary>
/// Remove drive letters from protection scan output
/// </summary>
public bool HideDriveLetters
{
get { return GetBooleanSetting(Settings, "HideDriveLetters", false); }
set { Settings["HideDriveLetters"] = value.ToString(); }
}
#endregion
@@ -507,8 +630,8 @@ namespace MPF.Core.Data
/// </summary>
public bool VerboseLogging
{
get { return GetBooleanSetting(_settings, "VerboseLogging", true); }
set { _settings["VerboseLogging"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "VerboseLogging", true); }
set { Settings["VerboseLogging"] = value.ToString(); }
}
/// <summary>
@@ -516,31 +639,34 @@ namespace MPF.Core.Data
/// </summary>
public bool OpenLogWindowAtStartup
{
get { return GetBooleanSetting(_settings, "OpenLogWindowAtStartup", true); }
set { _settings["OpenLogWindowAtStartup"] = value.ToString(); }
get { return GetBooleanSetting(Settings, "OpenLogWindowAtStartup", true); }
set { Settings["OpenLogWindowAtStartup"] = value.ToString(); }
}
#endregion
#region Redump Login Information
public string RedumpUsername
public string? RedumpUsername
{
get { return GetStringSetting(_settings, "RedumpUsername", ""); }
set { _settings["RedumpUsername"] = value; }
get { return GetStringSetting(Settings, "RedumpUsername", ""); }
set { Settings["RedumpUsername"] = value; }
}
// TODO: Figure out a way to keep this encrypted in some way, BASE64 to start?
public string RedumpPassword
public string? RedumpPassword
{
get { return GetStringSetting(_settings, "RedumpPassword", ""); }
set { _settings["RedumpPassword"] = value; }
get
{
return GetStringSetting(Settings, "RedumpPassword", "");
}
set { Settings["RedumpPassword"] = value; }
}
/// <summary>
/// Determine if a complete set of Redump credentials might exist
/// </summary>
public bool HasRedumpLogin { get => !string.IsNullOrWhiteSpace(RedumpUsername) && !string.IsNullOrWhiteSpace(RedumpPassword); }
public bool HasRedumpLogin { get => !string.IsNullOrEmpty(RedumpUsername) && !string.IsNullOrEmpty(RedumpPassword); }
#endregion
@@ -548,27 +674,27 @@ namespace MPF.Core.Data
/// Constructor taking a dictionary for settings
/// </summary>
/// <param name="settings"></param>
public Options(Dictionary<string, string> settings = null)
public Options(Dictionary<string, string?>? settings = null)
{
this._settings = settings ?? new Dictionary<string, string>();
this.Settings = settings ?? [];
}
/// <summary>
/// Constructor taking an existing Options object
/// </summary>
/// <param name="source"></param>
public Options(Options source)
public Options(Options? source)
{
_settings = new Dictionary<string, string>(source._settings);
Settings = new Dictionary<string, string?>(source?.Settings ?? []);
}
/// <summary>
/// Set all fields from an existing Options object
/// Accessor for the internal dictionary
/// </summary>
/// <param name="source"></param>
public void SetFromExisting(Options source)
public string? this[string key]
{
_settings = new Dictionary<string, string>(source._settings);
get => this.Settings[key];
set => this.Settings[key] = value;
}
#region Helpers
@@ -580,11 +706,11 @@ namespace MPF.Core.Data
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private bool GetBooleanSetting(Dictionary<string, string> settings, string key, bool defaultValue)
private static bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
{
if (settings.ContainsKey(key))
{
if (Boolean.TryParse(settings[key], out bool value))
if (bool.TryParse(settings[key], out bool value))
return value;
else
return defaultValue;
@@ -602,11 +728,11 @@ namespace MPF.Core.Data
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private int GetInt32Setting(Dictionary<string, string> settings, string key, int defaultValue)
private static int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
{
if (settings.ContainsKey(key))
{
if (Int32.TryParse(settings[key], out int value))
if (int.TryParse(settings[key], out int value))
return value;
else
return defaultValue;
@@ -624,7 +750,7 @@ namespace MPF.Core.Data
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private string GetStringSetting(Dictionary<string, string> settings, string key, string defaultValue)
private static string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
{
if (settings.ContainsKey(key))
return settings[key];
@@ -633,45 +759,5 @@ namespace MPF.Core.Data
}
#endregion
#region IDictionary implementations
public ICollection<string> Keys => _settings.Keys;
public ICollection<string> Values => _settings.Values;
public int Count => _settings.Count;
public bool IsReadOnly => ((IDictionary<string, string>)_settings).IsReadOnly;
public string this[string key]
{
get { return (_settings.ContainsKey(key) ? _settings[key] : null); }
set { _settings[key] = value; }
}
public bool ContainsKey(string key) => _settings.ContainsKey(key);
public void Add(string key, string value) => _settings.Add(key, value);
public bool Remove(string key) => _settings.Remove(key);
public bool TryGetValue(string key, out string value) => _settings.TryGetValue(key, out value);
public void Add(KeyValuePair<string, string> item) => _settings.Add(item.Key, item.Value);
public void Clear() => _settings.Clear();
public bool Contains(KeyValuePair<string, string> item) => ((IDictionary<string, string>)_settings).Contains(item);
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) => ((IDictionary<string, string>)_settings).CopyTo(array, arrayIndex);
public bool Remove(KeyValuePair<string, string> item) => ((IDictionary<string, string>)_settings).Remove(item);
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() => _settings.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _settings.GetEnumerator();
#endregion
}
}

View File

@@ -1,16 +1,24 @@
using System;
#if NET20 || NET35
using System.Collections.Generic;
#else
using System.Collections.Concurrent;
#endif
using System.Threading;
using System.Threading.Tasks;
namespace MPF.Core.Data
{
public class ProcessingQueue<T> : IDisposable
public sealed class ProcessingQueue<T> : IDisposable
{
/// <summary>
/// Internal queue to hold data to process
/// </summary>
#if NET20 || NET35
private readonly Queue<T> InternalQueue;
#else
private readonly ConcurrentQueue<T> InternalQueue;
#endif
/// <summary>
/// Custom processing step for dequeued data
@@ -24,10 +32,20 @@ namespace MPF.Core.Data
public ProcessingQueue(Action<T> customProcessing)
{
#if NET20 || NET35
this.InternalQueue = new Queue<T>();
#else
this.InternalQueue = new ConcurrentQueue<T>();
#endif
this.CustomProcessing = customProcessing;
this.TokenSource = new CancellationTokenSource();
#if NET20 || NET35
Task.Run(() => ProcessQueue());
#elif NET40
Task.Factory.StartNew(() => ProcessQueue());
#else
Task.Run(() => ProcessQueue(), this.TokenSource.Token);
#endif
}
/// <summary>
@@ -39,10 +57,10 @@ namespace MPF.Core.Data
/// Enqueue a new item for processing
/// </summary>
/// <param name="item"></param>
public void Enqueue(T item)
public void Enqueue(T? item)
{
// Only accept new data when not cancelled
if (!this.TokenSource.IsCancellationRequested)
if (item != null && !this.TokenSource.IsCancellationRequested)
this.InternalQueue.Enqueue(item);
}
@@ -54,7 +72,11 @@ namespace MPF.Core.Data
while (true)
{
// Nothing in the queue means we get to idle
if (this.InternalQueue.Count == 0)
#if NET20 || NET35
if (InternalQueue.Count == 0)
#else
if (InternalQueue.IsEmpty)
#endif
{
if (this.TokenSource.IsCancellationRequested)
break;
@@ -63,12 +85,17 @@ namespace MPF.Core.Data
continue;
}
#if NET20 || NET35
// Get the next item from the queue and invoke the lambda, if possible
this.CustomProcessing?.Invoke(this.InternalQueue.Dequeue());
#else
// Get the next item from the queue
if (!this.InternalQueue.TryDequeue(out T nextItem))
if (!this.InternalQueue.TryDequeue(out var nextItem))
continue;
// Invoke the lambda, if possible
this.CustomProcessing?.Invoke(nextItem);
#endif
}
}
}

View File

@@ -1,9 +1,11 @@
namespace MPF.Core.Data
using System;
namespace MPF.Core.Data
{
/// <summary>
/// Generic success/failure result object, with optional message
/// </summary>
public class Result
public class Result : System.EventArgs
{
/// <summary>
/// Internal representation of success
@@ -24,25 +26,30 @@
/// <summary>
/// Create a default success result with no message
/// </summary>
public static Result Success() => new Result(true, "");
public static Result Success() => new(true, "");
/// <summary>
/// Create a success result with a custom message
/// </summary>
/// <param name="message">String to add as a message</param>
public static Result Success(string message) => new Result(true, message);
public static Result Success(string? message) => new(true, message ?? string.Empty);
/// <summary>
/// Create a default failure result with no message
/// </summary>
/// <returns></returns>
public static Result Failure() => new Result(false, "");
public static Result Failure() => new(false, "");
/// <summary>
/// Create a failure result with a custom message
/// </summary>
/// <param name="message">String to add as a message</param>
public static Result Failure(string message) => new Result(false, message);
public static Result Failure(string? message) => new(false, message ?? string.Empty);
internal static Result Success(object value)
{
throw new NotImplementedException();
}
/// <summary>
/// Results can be compared to boolean values based on the success value

View File

@@ -1,180 +0,0 @@
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
/// <summary>
/// Contains information specific to an XGD disc
/// </summary>
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 RawXMID { get; private set; }
/// <summary>
/// XGD1 XMID
/// </summary>
public SabreTools.Models.Xbox.XMID XMID { get; private set; }
/// <summary>
/// XGD2/3 XeMID
/// </summary>
public SabreTools.Models.Xbox.XeMID XeMID { get; private set; }
#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>
public XgdInfo(string xmid)
{
this.Initialized = false;
if (string.IsNullOrWhiteSpace(xmid))
return;
this.RawXMID = xmid.TrimEnd('\0');
if (string.IsNullOrWhiteSpace(this.RawXMID))
return;
// XGD1 information is 8 characters
if (this.RawXMID.Length == 8)
this.Initialized = ParseXGD1XMID(this.RawXMID);
// XGD2/3 information is semi-variable length
else if (this.RawXMID.Length == 13 || this.RawXMID.Length == 14 || this.RawXMID.Length == 21 || this.RawXMID.Length == 22)
this.Initialized = ParseXGD23XeMID(this.RawXMID);
}
/// <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 (XMID != null)
return $"{XMID.PublisherIdentifier}-{XMID.GameID}";
// XGD2/3 uses a specific identifier
else if (XeMID?.PlatformIdentifier == '2')
return $"{XeMID.PublisherIdentifier}-{XeMID.PlatformIdentifier}{XeMID.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 (XMID != null)
return $"1.{XMID.VersionNumber}";
// XGD2/3 uses a specific identifier
else if (XeMID?.PlatformIdentifier == '2')
return $"1.{XeMID.SKU}";
return null;
}
catch
{
return null;
}
}
/// <summary>
/// Parse an XGD1 XMID string
/// </summary>
/// <param name="rawXmid">XMID string to attempt to parse</param>
/// <returns>True if the XMID could be parsed, false otherwise</returns>
private bool ParseXGD1XMID(string rawXmid)
{
try
{
var xmid = new SabreTools.Serialization.Files.XMID().Deserialize(rawXmid);
if (xmid == null)
return false;
this.XMID = xmid;
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Parse an XGD2/3 XeMID string
/// </summary>
/// <param name="rawXemid">XeMID string to attempt to parse</param>
/// <returns>True if the XeMID could be parsed, false otherwise</returns>
private bool ParseXGD23XeMID(string rawXemid)
{
try
{
var xemid = new SabreTools.Serialization.Files.XeMID().Deserialize(rawXemid);
if (xemid == null)
return false;
this.XeMID = xemid;
return true;
}
catch
{
return false;
}
}
#region Helpers
/// <summary>
/// Determine the region based on the XGD serial character
/// </summary>
/// <param name="region">Character denoting the region</param>
/// <returns>Region, if possible</returns>
public 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

@@ -2,14 +2,15 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BurnOutSharp;
using MPF.Core.Data;
using MPF.Core.Modules;
using MPF.Core.Utilities;
using MPF.Modules;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Library
namespace MPF.Core
{
/// <summary>
/// Represents the state of all settings to be used during dumping
@@ -30,7 +31,7 @@ namespace MPF.Library
/// <summary>
/// Drive object representing the current drive
/// </summary>
public Drive Drive { get; private set; }
public Drive? Drive { get; private set; }
/// <summary>
/// Currently selected system
@@ -50,36 +51,48 @@ namespace MPF.Library
/// <summary>
/// Options object representing user-defined options
/// </summary>
public Core.Data.Options Options { get; private set; }
public Data.Options Options { get; private set; }
/// <summary>
/// Parameters object representing what to send to the internal program
/// </summary>
public BaseParameters Parameters { get; private set; }
public BaseParameters? Parameters { get; private set; }
#endregion
#region Event Handlers
/// <summary>
/// Generic way of reporting a message
/// </summary>
public EventHandler<string> ReportStatus;
#if NET20 || NET35 || NET40
public EventHandler<BaseParameters.StringEventArgs>? ReportStatus;
#else
public EventHandler<string>? ReportStatus;
#endif
/// <summary>
/// Queue of items that need to be logged
/// </summary>
private ProcessingQueue<string> outputQueue;
private ProcessingQueue<string>? outputQueue;
/// <summary>
/// Event handler for data returned from a process
/// </summary>
private void OutputToLog(object proc, string args) => outputQueue.Enqueue(args);
#if NET20 || NET35 || NET40
private void OutputToLog(object? proc, BaseParameters.StringEventArgs args) => outputQueue?.Enqueue(args.Value);
#else
private void OutputToLog(object? proc, string args) => outputQueue?.Enqueue(args);
#endif
/// <summary>
/// Process the outputs in the queue
/// </summary>
private void ProcessOutputs(string nextOutput) => ReportStatus.Invoke(this, nextOutput);
#if NET20 || NET35 || NET40
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, new BaseParameters.StringEventArgs { Value = nextOutput });
#else
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, nextOutput);
#endif
#endregion
@@ -93,112 +106,65 @@ namespace MPF.Library
/// <param name="type"></param>
/// <param name="internalProgram"></param>
/// <param name="parameters"></param>
public DumpEnvironment(Core.Data.Options options,
public DumpEnvironment(Data.Options options,
string outputPath,
Drive drive,
Drive? drive,
RedumpSystem? system,
MediaType? type,
InternalProgram? internalProgram,
string parameters)
string? parameters)
{
// Set options object
this.Options = options;
Options = options;
// Output paths
this.OutputPath = InfoTool.NormalizeOutputPaths(outputPath);
OutputPath = InfoTool.NormalizeOutputPaths(outputPath, false);
// UI information
this.Drive = drive;
this.System = system ?? options.DefaultSystem;
this.Type = type ?? MediaType.NONE;
this.InternalProgram = internalProgram ?? options.InternalProgram;
Drive = drive;
System = system ?? options.DefaultSystem;
Type = type ?? MediaType.NONE;
InternalProgram = internalProgram ?? options.InternalProgram;
// 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;
try
{
// Normalize the output path
string outputPath = InfoTool.NormalizeOutputPaths(this.OutputPath);
// Replace all instances in the output directory
string outputDirectory = Path.GetDirectoryName(outputPath);
outputDirectory = outputDirectory.Replace(".", "_");
// Replace all instances in the output filename
string outputFilename = Path.GetFileNameWithoutExtension(outputPath);
outputFilename = outputFilename.Replace(".", "_");
// Get the extension for recreating the path
string outputExtension = Path.GetExtension(outputPath).TrimStart('.');
// Rebuild the output path
if (!string.IsNullOrWhiteSpace(outputExtension))
this.OutputPath = Path.Combine(outputDirectory, $"{outputFilename}.{outputExtension}");
else
this.OutputPath = Path.Combine(outputDirectory, outputFilename);
// Assign the path to the filename as well for dumping
((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = this.OutputPath;
}
catch { }
}
/// <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)
public void SetParameters(string? parameters)
{
switch (this.InternalProgram)
Parameters = InternalProgram switch
{
// Dumping support
case InternalProgram.Aaru:
this.Parameters = new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath };
break;
case InternalProgram.DiscImageCreator:
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
break;
case InternalProgram.Redumper:
this.Parameters = new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath };
break;
InternalProgram.Aaru => new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath },
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath },
InternalProgram.Redumper => new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath },
// Verification support only
case InternalProgram.CleanRip:
this.Parameters = new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null };
break;
InternalProgram.CleanRip => new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null },
InternalProgram.DCDumper => null, // TODO: Create correct parameter type when supported
InternalProgram.PS3CFW => new Modules.PS3CFW.Parameters(parameters) { ExecutablePath = null },
InternalProgram.UmdImageCreator => new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null },
InternalProgram.XboxBackupCreator => new Modules.XboxBackupCreator.Parameters(parameters) { ExecutablePath = null },
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;
// If no dumping program found, set to null
InternalProgram.NONE => null,
// This should never happen, but it needs a fallback
default:
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
break;
}
_ => new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath },
};
// Set system and type
this.Parameters.System = this.System;
this.Parameters.Type = this.Type;
if (Parameters != null)
{
Parameters.System = System;
Parameters.Type = Type;
}
}
/// <summary>
@@ -206,7 +172,7 @@ namespace MPF.Library
/// </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)
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)
@@ -216,28 +182,21 @@ namespace MPF.Library
return null;
// Set the proper parameters
switch (this.InternalProgram)
Parameters = InternalProgram switch
{
case InternalProgram.Aaru:
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
break;
InternalProgram.Aaru => new Modules.Aaru.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
InternalProgram.Redumper => new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
case InternalProgram.DiscImageCreator:
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
break;
case InternalProgram.Redumper:
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
break;
// If no dumping program found, set to null
InternalProgram.NONE => null,
// This should never happen, but it needs a fallback
default:
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
break;
}
_ => new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
};
// Generate and return the param string
return Parameters.GenerateParameters();
return Parameters?.GenerateParameters();
}
return null;
@@ -250,26 +209,34 @@ namespace MPF.Library
/// <summary>
/// Cancel an in-progress dumping process
/// </summary>
public void CancelDumping() => Parameters.KillInternalProgram();
public void CancelDumping() => Parameters?.KillInternalProgram();
/// <summary>
/// Eject the disc using DiscImageCreator
/// </summary>
public async Task<string> EjectDisc() =>
public async Task<string?> EjectDisc() =>
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
/// <summary>
/// Reset the current drive using DiscImageCreator
/// </summary>
public async Task<string> ResetDrive() =>
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)
#if NET40
public Result Run(IProgress<Result>? progress = null)
#else
public async Task<Result> Run(IProgress<Result>? progress = null)
#endif
{
// If we don't have parameters
if (Parameters == null)
return Result.Failure("Error! Current configuration is not supported!");
// Check that we have the basics for dumping
Result result = IsValidForDump();
if (!result)
@@ -279,24 +246,29 @@ namespace MPF.Library
if (!Options.ToolsInSeparateWindow)
{
outputQueue = new ProcessingQueue<string>(ProcessOutputs);
Parameters.ReportStatus += OutputToLog;
if (Parameters.ReportStatus != null)
Parameters.ReportStatus += OutputToLog;
}
// Execute internal tool
progress?.Report(Result.Success($"Executing {this.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
progress?.Report(Result.Success($"{this.InternalProgram} has finished!"));
progress?.Report(Result.Success($"Executing {InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
// Execute additional tools
progress?.Report(Result.Success("Running any additional tools... see log for output!"));
result = await Task.Run(() => ExecuteAdditionalTools());
progress?.Report(result);
var directoryName = Path.GetDirectoryName(OutputPath);
if (!string.IsNullOrEmpty(directoryName))
Directory.CreateDirectory(directoryName);
#if NET40
var executeTask = Task.Factory.StartNew(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
executeTask.Wait();
#else
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
#endif
progress?.Report(Result.Success($"{InternalProgram} has finished!"));
// Remove event handler if needed
if (!Options.ToolsInSeparateWindow)
{
outputQueue.Dispose();
outputQueue?.Dispose();
Parameters.ReportStatus -= OutputToLog;
}
@@ -309,50 +281,63 @@ namespace MPF.Library
/// <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>
/// <param name="seedInfo">A seed SubmissionInfo object that contains user data</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)
IProgress<Result>? resultProgress = null,
IProgress<BinaryObjectScanner.ProtectionProgress>? protectionProgress = null,
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null,
SubmissionInfo? seedInfo = null)
{
if (Parameters == null)
return Result.Failure("Error! Current configuration is not supported!");
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
// Get the output directory and filename separately
string outputDirectory = Path.GetDirectoryName(this.OutputPath);
string outputFilename = Path.GetFileName(this.OutputPath);
var outputDirectory = Path.GetDirectoryName(OutputPath);
var outputFilename = Path.GetFileName(OutputPath);
// Check to make sure that the output had all the correct files
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, this.Parameters, false);
(bool foundFiles, List<string> missingFiles) = Parameters.FoundAllFiles(outputDirectory, outputFilename, false);
if (!foundFiles)
{
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
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.OutputPath,
this.Drive,
this.System,
this.Type,
this.Options,
this.Parameters,
var submissionInfo = await SubmissionInfoTool.ExtractOutputInformation(
OutputPath,
Drive,
System,
Type,
Options,
Parameters,
resultProgress,
protectionProgress);
resultProgress?.Report(Result.Success("Extracting information complete!"));
// Inject seed submission info data, if necessary
if (seedInfo != null)
{
resultProgress?.Report(Result.Success("Injecting user-supplied information..."));
Builder.InjectSubmissionInformation(submissionInfo, seedInfo);
resultProgress?.Report(Result.Success("Information injection complete!"));
}
// Eject the disc automatically if configured to
if (Options.EjectAfterDump == true)
{
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive.Letter}"));
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive?.Name}"));
await EjectDisc();
}
// Reset the drive automatically if configured to
if (this.InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
if (InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
{
resultProgress?.Report(Result.Success($"Resetting drive {Drive.Letter}"));
resultProgress?.Report(Result.Success($"Resetting drive {Drive?.Name}"));
await ResetDrive();
}
@@ -372,41 +357,47 @@ namespace MPF.Library
// Process special fields for site codes
resultProgress?.Report(Result.Success("Processing site codes..."));
InfoTool.ProcessSpecialFields(submissionInfo);
Formatter.ProcessSpecialFields(submissionInfo);
resultProgress?.Report(Result.Success("Processing complete!"));
// Format the information for the text output
resultProgress?.Report(Result.Success("Formatting information..."));
(List<string> formattedValues, string formatResult) = InfoTool.FormatOutputData(submissionInfo, this.Options);
(var formattedValues, var formatResult) = Formatter.FormatOutputData(submissionInfo, Options.EnableRedumpCompatibility);
if (formattedValues == null)
resultProgress?.Report(Result.Success(formatResult));
else
resultProgress?.Report(Result.Failure(formatResult));
else
resultProgress?.Report(Result.Success(formatResult));
// Get the filename suffix for auto-generated files
var filenameSuffix = Options.AddFilenameSuffix ? Path.GetFileNameWithoutExtension(outputFilename) : null;
// Write the text output
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, formattedValues);
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, formattedValues);
if (txtSuccess)
resultProgress?.Report(Result.Success(txtResult));
else
resultProgress?.Report(Result.Failure(txtResult));
// Write the copy protection output
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
if (submissionInfo?.CopyProtection?.FullProtections != null && submissionInfo.CopyProtection.FullProtections.Any())
{
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, submissionInfo);
if (scanSuccess)
resultProgress?.Report(Result.Success("Writing complete!"));
else
resultProgress?.Report(Result.Failure("Writing could not complete!"));
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
{
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo, Options.HideDriveLetters);
if (scanSuccess)
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{(Options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, submissionInfo, Options.IncludeArtifacts);
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, submissionInfo, Options.IncludeArtifacts);
if (jsonSuccess)
resultProgress?.Report(Result.Success("Writing complete!"));
else
@@ -417,13 +408,35 @@ namespace MPF.Library
if (Options.CompressLogFiles)
{
resultProgress?.Report(Result.Success("Compressing log files..."));
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, outputFilename, this.Parameters);
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, Parameters);
if (compressSuccess)
resultProgress?.Report(Result.Success(compressResult));
else
resultProgress?.Report(Result.Failure(compressResult));
}
// Delete unnecessary files, if required
if (Options.DeleteUnnecessaryFiles)
{
resultProgress?.Report(Result.Success("Deleting unnecessary files..."));
(bool deleteSuccess, string deleteResult) = InfoTool.DeleteUnnecessaryFiles(outputDirectory, outputFilename, Parameters);
if (deleteSuccess)
resultProgress?.Report(Result.Success(deleteResult));
else
resultProgress?.Report(Result.Failure(deleteResult));
}
// Create PS3 IRD, if required
if (Options.CreateIRDAfterDumping && System == RedumpSystem.SonyPlayStation3 && Type == MediaType.BluRay)
{
resultProgress?.Report(Result.Success("Creating IRD... please wait!"));
(bool deleteSuccess, string deleteResult) = await InfoTool.WriteIRD(OutputPath, submissionInfo?.Extras?.DiscKey, submissionInfo?.Extras?.DiscID, submissionInfo?.Extras?.PIC, submissionInfo?.SizeAndChecksums?.Layerbreak, submissionInfo?.SizeAndChecksums?.CRC32);
if (deleteSuccess)
resultProgress?.Report(Result.Success(deleteResult));
else
resultProgress?.Report(Result.Failure(deleteResult));
}
resultProgress?.Report(Result.Success("Submission information process complete!"));
return Result.Success();
}
@@ -434,7 +447,11 @@ namespace MPF.Library
/// <returns>True if the configuration is valid, false otherwise</returns>
internal bool ParametersValid()
{
bool parametersValid = Parameters.IsValid();
// Missing drive means it can never be valid
if (Drive == null)
return false;
bool parametersValid = Parameters?.IsValid() ?? false;
bool floppyValid = !(Drive.InternalDriveType == InternalDriveType.Floppy ^ Type == MediaType.FloppyDisk);
// TODO: HardDisk being in the Removable category is a hack, fix this later
@@ -444,28 +461,26 @@ namespace MPF.Library
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)
private static async Task<string> ExecuteInternalProgram(BaseParameters parameters)
{
Process childProcess;
#if NET40
string output = await Task.Factory.StartNew(() =>
#else
string output = await Task.Run(() =>
#endif
{
childProcess = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = parameters.ExecutablePath,
Arguments = parameters.GenerateParameters(),
FileName = parameters.ExecutablePath!,
Arguments = parameters.GenerateParameters()!,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
@@ -494,14 +509,14 @@ namespace MPF.Library
private Result IsValidForDump()
{
// Validate that everything is good
if (!ParametersValid())
if (Parameters == null || !ParametersValid())
return Result.Failure("Error! Current configuration is not supported!");
// Fix the output paths, just in case
this.OutputPath = InfoTool.NormalizeOutputPaths(this.OutputPath);
OutputPath = InfoTool.NormalizeOutputPaths(OutputPath, false);
// Validate that the output path isn't on the dumping drive
if (this.OutputPath[0] == Drive.Letter)
if (Drive?.Name != null && OutputPath.StartsWith(Drive.Name))
return Result.Failure("Error! Cannot output to same drive that is being dumped!");
// Validate that the required program exists
@@ -509,8 +524,8 @@ namespace MPF.Library
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)
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath!);
if (Drive?.Name != null && fullExecutablePath.StartsWith(Drive.Name))
return Result.Failure("Error! Cannot dump same drive that executable resides on!");
// Validate that the current configuration is supported
@@ -524,7 +539,7 @@ namespace MPF.Library
private bool RequiredProgramsExist()
{
// Validate that the path is configured
if (string.IsNullOrWhiteSpace(Options.DiscImageCreatorPath))
if (string.IsNullOrEmpty(Options.DiscImageCreatorPath))
return false;
// Validate that the required program exists
@@ -539,14 +554,14 @@ namespace MPF.Library
/// </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)
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)
if (Drive == null || Drive.InternalDriveType != InternalDriveType.Optical)
return null;
CancelDumping();
@@ -554,7 +569,7 @@ namespace MPF.Library
var parameters = new Modules.DiscImageCreator.Parameters(string.Empty)
{
BaseCommand = command,
DriveLetter = Drive.Letter.ToString(),
DrivePath = Drive.Name,
ExecutablePath = Options.DiscImageCreatorPath,
};

View File

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

View File

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

View File

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

2026
MPF.Core/InfoTool.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,52 +1,63 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.9</VersionPrefix>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<Description>Common code for all MPF implementations</Description>
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<PropertyGroup>
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
</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.Management.Infrastructure" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.Models" Version="1.1.2" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="SabreTools.Serialization" Version="1.1.2" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>
<!-- Support for old .NET versions -->
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="MinAsyncBridge" Version="0.12.4" />
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="IndexRange" Version="1.0.3" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="Microsoft.Net.Http" Version="2.2.29" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.12" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="LibIRD" Version="0.9.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="psxt001z.Library" Version="0.21.0-rc1" />
<PackageReference Include="SabreTools.Hashing" Version="1.2.0" />
<PackageReference Include="SabreTools.Models" Version="1.4.8" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
<PackageReference Include="SabreTools.Serialization" Version="1.6.5" />
</ItemGroup>
</Project>

View File

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

View File

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

View File

@@ -0,0 +1,405 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using SabreTools.Hashing;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.CleanRip
{
/// <summary>
/// Represents a generic set of CleanRip parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.CleanRip;
#endregion
/// <inheritdoc/>
public Parameters(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}-dumpinfo.txt"))
missingFiles.Add($"{basePath}-dumpinfo.txt");
if (!File.Exists($"{basePath}.bca"))
missingFiles.Add($"{basePath}.bca");
}
break;
default:
missingFiles.Add("Media and system combination not supported for CleanRip");
break;
}
return (!missingFiles.Any(), missingFiles);
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
{
// Ensure that required sections exist
info = Builder.EnsureAllSections(info);
// TODO: Determine if there's a CleanRip version anywhere
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
// Get the Datafile information
var datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
// Get the individual hash data, as per internal
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
{
info.SizeAndChecksums!.Size = size;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
// Dual-layer discs have the same size and layerbreak
if (size == 8511160320)
info.SizeAndChecksums.Layerbreak = 2084960;
}
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (File.Exists(basePath + ".bca"))
info.Extras!.BCA = GetBCA(basePath + ".bca");
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out var gcVersion, out var gcName, out var gcSerial))
{
info.CommonDiscInfo!.Region = gcRegion ?? info.CommonDiscInfo.Region;
info.VersionAndEditions!.Version = gcVersion ?? info.VersionAndEditions.Version;
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalName] = gcName ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = gcSerial ?? string.Empty;
}
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= [];
if (File.Exists(basePath + ".bca"))
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty;
if (File.Exists(basePath + "-dumpinfo.txt"))
info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt")) ?? string.Empty;
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
var logFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (File.Exists($"{basePath}-dumpinfo.txt"))
logFiles.Add($"{basePath}-dumpinfo.txt");
if (File.Exists($"{basePath}.bca"))
logFiles.Add($"{basePath}.bca");
break;
}
return logFiles;
}
#endregion
#region Information Extraction Methods
/// <summary>
/// Get a formatted datfile from the cleanrip output, if possible
/// </summary>
/// <param name="iso">Path to ISO file</param>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <returns></returns>
private static Datafile? GenerateCleanripDatafile(string iso, string dumpinfo)
{
// If the files don't exist, we can't get info from it
if (!File.Exists(iso) || !File.Exists(dumpinfo))
return null;
long size = new FileInfo(iso).Length;
string crc = string.Empty;
string md5 = string.Empty;
string sha1 = string.Empty;
try
{
// Make sure this file is a dumpinfo
using var sr = File.OpenText(dumpinfo);
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
return null;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrEmpty(line))
continue;
else if (line!.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line.Substring(5);
else if (line.StartsWith("SHA-1"))
sha1 = line.Substring(7);
}
// Ensure all checksums were found in log
if (crc == string.Empty || md5 == string.Empty || sha1 == string.Empty)
{
if (HashTool.GetStandardHashes(iso, out long isoSize, out string? isoCRC, out string? isoMD5, out string? isoSHA1))
{
crc = isoCRC ?? crc;
md5 = isoMD5 ?? md5;
sha1 = isoSHA1 ?? sha1;
}
}
return new Datafile
{
Games =
[
new()
{
Roms =
[
new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1 },
]
}
]
};
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get the hex contents of the BCA file
/// </summary>
/// <param name="bcaPath">Path to the BCA file associated with the dump</param>
/// <returns>BCA data as a hex string if possible, null on error</returns>
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
private static string? GetBCA(string bcaPath)
{
// If the file doesn't exist, we can't get the info
if (!File.Exists(bcaPath))
return null;
try
{
var hex = GetFullFile(bcaPath, true);
if (hex == null)
return null;
return Regex.Replace(hex, ".{32}", "$0\n");
}
catch
{
// We don't care what the error was right now
return null;
}
}
/// <summary>
/// Get a formatted datfile from the cleanrip output, if possible
/// </summary>
/// <param name="iso">Path to ISO file</param>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <returns></returns>
private static string? GetCleanripDatfile(string iso, string dumpinfo)
{
// If the files don't exist, we can't get info from it
if (!File.Exists(iso) || !File.Exists(dumpinfo))
return null;
long size = new FileInfo(iso).Length;
string crc = string.Empty;
string md5 = string.Empty;
string sha1 = string.Empty;
try
{
// Make sure this file is a dumpinfo
using var sr = File.OpenText(dumpinfo);
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
return null;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrEmpty(line))
continue;
else if (line!.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line.Substring(5);
else if (line.StartsWith("SHA-1"))
sha1 = line.Substring(7);
}
return $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get the extracted GC and Wii version
/// </summary>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <param name="region">Output region, if possible</param>
/// <param name="version">Output internal version of the game</param>
/// <param name="name">Output internal name of the game</param>
/// <param name="serial">Output internal serial of the game</param>
/// <returns></returns>
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string? version, out string? name, out string? serial)
{
region = null; version = null; name = null; serial = null;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dumpinfo))
return false;
try
{
// Make sure this file is a dumpinfo
using var sr = File.OpenText(dumpinfo);
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
return false;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrEmpty(line))
{
continue;
}
else if (line!.StartsWith("Version"))
{
version = line.Substring("Version: ".Length);
}
else if (line.StartsWith("Internal Name"))
{
name = line.Substring("Internal Name: ".Length);
}
else if (line.StartsWith("Filename"))
{
serial = line.Substring("Filename: ".Length);
if (serial.EndsWith("-disc2"))
serial = serial.Replace("-disc2", string.Empty);
// char gameType = serial[0];
// string gameid = serial[1] + serial[2];
// string version = serial[4] + serial[5]
switch (serial[3])
{
case 'A':
region = Region.World;
break;
case 'D':
region = Region.Germany;
break;
case 'E':
region = Region.UnitedStatesOfAmerica;
break;
case 'F':
region = Region.France;
break;
case 'I':
region = Region.Italy;
break;
case 'J':
region = Region.Japan;
break;
case 'K':
region = Region.SouthKorea;
break;
case 'L':
region = Region.Europe; // Japanese import to Europe
break;
case 'M':
region = Region.Europe; // American import to Europe
break;
case 'N':
region = Region.UnitedStatesOfAmerica; // Japanese import to USA
break;
case 'P':
region = Region.Europe;
break;
case 'R':
region = Region.RussianFederation;
break;
case 'S':
region = Region.Spain;
break;
case 'Q':
region = Region.SouthKorea; // Korea with Japanese language
break;
case 'T':
region = Region.SouthKorea; // Korea with English language
break;
case 'X':
region = null; // Not a real region code
break;
}
}
}
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
#endregion
}
}

View File

@@ -1,72 +1,72 @@
using System.Xml.Serialization;
namespace MPF.Modules
namespace MPF.Core.Modules
{
[XmlRoot("datafile")]
public class Datafile
{
[XmlElement("header")]
public Header Header;
public Header? Header;
[XmlElement("game")]
public Game[] Games;
public Game[]? Games;
}
public class Header
{
[XmlElement("name")]
public string Name;
public string? Name;
[XmlElement("description")]
public string Description;
public string? Description;
[XmlElement("version")]
public string Version;
public string? Version;
[XmlElement("date")]
public string Date;
public string? Date;
[XmlElement("author")]
public string Author;
public string? Author;
[XmlElement("homepage")]
public string Homepage;
public string? Homepage;
[XmlElement("url")]
public string Url;
public string? Url;
}
public class Game
{
[XmlAttribute("name")]
public string Name;
public string? Name;
[XmlElement("category")]
public string Category;
public string? Category;
[XmlElement("description")]
public string Description;
public string? Description;
[XmlElement("rom")]
public Rom[] Roms;
public Rom[]? Roms;
}
public class Rom
{
[XmlAttribute("name")]
public string Name;
public string? Name;
[XmlAttribute("size")]
public string Size;
public string? Size;
[XmlAttribute("crc")]
public string Crc;
public string? Crc;
[XmlAttribute("md5")]
public string Md5;
public string? Md5;
[XmlAttribute("sha1")]
public string Sha1;
public string? Sha1;
// TODO: Add extended hashes here
}

View File

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

View File

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

View File

@@ -0,0 +1,237 @@
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using SabreTools.Hashing;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.PS3CFW
{
/// <summary>
/// Represents a generic set of PlayStation 3 Custom Firmware parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.PS3CFW;
#endregion
/// <inheritdoc/>
public Parameters(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
if (this.Type != MediaType.BluRay || this.System != RedumpSystem.SonyPlayStation3)
{
missingFiles.Add("Media and system combination not supported for PS3 CFW");
}
else
{
string? getKeyBasePath = GetCFWBasePath(basePath);
if (!File.Exists($"{getKeyBasePath}.getkey.log"))
missingFiles.Add($"{getKeyBasePath}.getkey.log");
if (!File.Exists($"{getKeyBasePath}.disc.pic"))
missingFiles.Add($"{getKeyBasePath}.disc.pic");
}
return (missingFiles.Count == 0, missingFiles);
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
{
// Ensure that required sections exist
info = Builder.EnsureAllSections(info);
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
// Get the Datafile information
Datafile? datafile = GeneratePS3CFWDatafile(basePath + ".iso");
// Fill in the hash data
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
// Get the individual hash data, as per internal
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
{
info.SizeAndChecksums!.Size = size;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
}
// Get the PVD from the ISO
if (GetPVD(basePath + ".iso", out string? pvd))
info.Extras!.PVD = pvd;
// Try get the serial, version, and firmware version if a drive is provided
if (drive != null)
{
info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
string? firmwareVersion = InfoTool.GetPlayStation3FirmwareVersion(drive?.Name);
if (firmwareVersion != null)
info.CommonDiscInfo!.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}";
}
// Try to determine the name of the GetKey file(s)
string? getKeyBasePath = GetCFWBasePath(basePath);
// If GenerateSubmissionInfo is run, .getkey.log existence should already be checked
if (!File.Exists(getKeyBasePath + ".getkey.log"))
return;
// Get dumping date from GetKey log date
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(getKeyBasePath + ".getkey.log")?.ToString("yyyy-MM-dd HH:mm:ss");
// TODO: Put info about abnormal PIC info beyond 132 bytes in comments?
if (File.Exists(getKeyBasePath + ".disc.pic"))
info.Extras!.PIC = GetPIC(getKeyBasePath + ".disc.pic", 264);
// Parse Disc Key, Disc ID, and PIC from the .getkey.log file
if (Utilities.Tools.ParseGetKeyLog(getKeyBasePath + ".getkey.log", out string? key, out string? id, out string? pic))
{
if (key != null)
info.Extras!.DiscKey = key.ToUpperInvariant();
if (id != null)
info.Extras!.DiscID = id.ToUpperInvariant().Substring(0, 24) + "XXXXXXXX";
if (string.IsNullOrEmpty(info.Extras!.PIC) && !string.IsNullOrEmpty(pic))
{
pic = Regex.Replace(pic, ".{32}", "$0\n");
info.Extras.PIC = pic;
}
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= [];
if (File.Exists(getKeyBasePath + ".disc.pic"))
info.Artifacts["discpic"] = GetBase64(GetFullFile(getKeyBasePath + ".disc.pic", binary: true)) ?? string.Empty;
if (File.Exists(getKeyBasePath + ".getkey.log"))
info.Artifacts["getkeylog"] = GetBase64(GetFullFile(getKeyBasePath + ".getkey.log")) ?? string.Empty;
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
var logFiles = new List<string>();
string? getKeyBasePath = GetCFWBasePath(basePath);
if (this.System != RedumpSystem.SonyPlayStation3)
return logFiles;
switch (this.Type)
{
case MediaType.BluRay:
if (File.Exists($"{getKeyBasePath}.getkey.log"))
logFiles.Add($"{getKeyBasePath}.getkey.log");
if (File.Exists($"{getKeyBasePath}.disc.pic"))
logFiles.Add($"{getKeyBasePath}.disc.pic");
break;
}
return logFiles;
}
#endregion
#region Information Extraction Methods
/// <summary>
/// Get a formatted datfile from the PS3 CFW output, if possible
/// </summary>
/// <param name="iso">Path to ISO file</param>
/// <returns></returns>
private static Datafile? GeneratePS3CFWDatafile(string iso)
{
// If the ISO file doesn't exist, we can't get info from it
if (!File.Exists(iso))
return null;
try
{
if (HashTool.GetStandardHashes(iso, out long size, out string? crc, out string? md5, out string? sha1))
{
return new Datafile
{
Games = [new Game { Roms = [new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1, }] }]
};
}
return null;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get a formatted datfile from the PS3 CFW output, if possible
/// </summary>
/// <param name="iso">Path to ISO file</param>
/// <returns>Formatted datfile, null if not valid</returns>
private static string? GetPS3CFWDatfile(string iso)
{
// If the files don't exist, we can't get info from it
if (!File.Exists(iso))
return null;
try
{
if (HashTool.GetStandardHashes(iso, out long size, out string? crc, out string? md5, out string? sha1))
return $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
return null;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
#endregion
#region Helper Functions
/// <summary>
/// Estimate the base filename of the .getkey.log file associated with the dump
/// </summary>
/// <param name="iso">Path to ISO file</param>
/// <returns>Base filename, null if not found</returns>
private string? GetCFWBasePath(string iso)
{
string? dir = Path.GetDirectoryName(iso);
dir ??= ".";
string[] files = Directory.GetFiles(dir, "*.getkey.log");
if (files.Length != 1)
return null;
return files[0].Substring(0, files[0].Length - 11);
}
#endregion
}
}

View File

@@ -1,4 +1,4 @@
namespace MPF.Modules.Redumper
namespace MPF.Core.Modules.Redumper
{
/// <summary>
/// Top-level commands for Redumper
@@ -10,14 +10,20 @@ namespace MPF.Modules.Redumper
public const string DVD = "dvd"; // Synonym for CD
public const string BluRay = "bd"; // Synonym for CD
public const string SACD = "sacd"; // Synonym for CD
public const string New = "new"; // Synonym for CD; Temporary command, to be removed later
public const string Rings = "rings";
public const string Dump = "dump";
public const string Info = "info";
public const string Protection = "protection";
public const string DumpNew = "dumpnew"; // Temporary command, to be removed later
public const string Refine = "refine";
public const string Split = "split";
public const string RefineNew = "refinenew"; // Temporary command, to be removed later
public const string Verify = "verify";
public const string DVDKey = "dvdkey";
public const string DVDIsoKey = "dvdisokey";
public const string Protection = "protection";
public const string Split = "split";
public const string Hash = "hash";
public const string Info = "info";
public const string Skeleton = "skeleton";
}
/// <summary>
@@ -28,6 +34,7 @@ namespace MPF.Modules.Redumper
// General
public const string HelpLong = "--help";
public const string HelpShort = "-h";
public const string Version = "--version";
public const string Verbose = "--verbose";
public const string Debug = "--debug";
public const string Drive = "--drive";
@@ -46,7 +53,7 @@ namespace MPF.Modules.Redumper
public const string DriveSectorOrder = "--drive-sector-order";
// Drive Specific
public const string PlextorLeadinSkip = "--plextor-leadin-skip";
public const string PlextorSkipLeadin = "--plextor-skip-leadin";
public const string PlextorLeadinRetries = "--plextor-leadin-retries";
public const string AsusSkipLeadout = "--asus-skip-leadout";
@@ -68,6 +75,11 @@ namespace MPF.Modules.Redumper
public const string LBAEnd = "--lba-end";
public const string RefineSubchannel = "--refine-subchannel";
public const string Skip = "--skip";
public const string DumpWriteOffset = "--dump-write-offset";
public const string DumpReadSize = "--dump-read-size";
public const string OverreadLeadout = "--overread-leadout";
public const string ForceUnscrambled = "--force-unscrambled";
public const string LegacySubs = "--legacy-subs";
public const string DisableCDText = "--disable-cdtext";
}
}

View File

@@ -0,0 +1,28 @@
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.Redumper
{
public static class Converters
{
#region Cross-enumeration conversions
/// <summary>
/// Get the default extension for a given disc type
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>Valid extension (with leading '.'), null on error</returns>
public static string? Extension(MediaType? type)
{
return type switch
{
MediaType.CDROM => ".bin",
MediaType.DVD
or MediaType.HDDVD
or MediaType.BluRay => ".iso",
_ => null,
};
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,335 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Core.Converters;
using MPF.Core.Data;
using SabreTools.Hashing;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.UmdImageCreator
{
/// <summary>
/// Represents a generic set of UmdImageCreator parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.UmdImageCreator;
#endregion
/// <inheritdoc/>
public Parameters(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.UMD:
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}_disc.txt"))
missingFiles.Add($"{basePath}_disc.txt");
if (!File.Exists($"{basePath}_mainError.txt"))
missingFiles.Add($"{basePath}_mainError.txt");
if (!File.Exists($"{basePath}_mainInfo.txt"))
missingFiles.Add($"{basePath}_mainInfo.txt");
if (!File.Exists($"{basePath}_volDesc.txt"))
missingFiles.Add($"{basePath}_volDesc.txt");
}
break;
default:
missingFiles.Add("Media and system combination not supported for UmdImageCreator");
break;
}
return (!missingFiles.Any(), missingFiles);
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
{
// Ensure that required sections exist
info = Builder.EnsureAllSections(info);
// TODO: Determine if there's a UMDImageCreator version anywhere
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
// Fill in the volume labels
if (GetVolumeLabels($"{basePath}_volDesc.txt", out var volLabels))
VolumeLabels = volLabels;
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.UMD:
info.Extras!.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
if (HashTool.GetStandardHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
{
// Get the Datafile information
var datafile = new Datafile
{
Games = [new Game { Roms = [new Rom { Name = string.Empty, Size = filesize.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1, }] }]
};
// Fill in the hash data
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
info.SizeAndChecksums!.Size = filesize;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
}
if (GetUMDAuxInfo(basePath + "_disc.txt", out var title, out DiscCategory? umdcat, out var umdversion, out var umdlayer, out long umdsize))
{
info.CommonDiscInfo!.Title = title ?? string.Empty;
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
info.VersionAndEditions!.Version = umdversion ?? string.Empty;
info.SizeAndChecksums!.Size = umdsize;
if (!string.IsNullOrEmpty(umdlayer))
info.SizeAndChecksums.Layerbreak = Int64.Parse(umdlayer ?? "-1");
}
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= [];
if (File.Exists($"{basePath}_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile($"{basePath}_disc.txt")) ?? string.Empty;
if (File.Exists($"{basePath}_drive.txt"))
info.Artifacts["drive"] = GetBase64(GetFullFile($"{basePath}_drive.txt")) ?? string.Empty;
if (File.Exists($"{basePath}_mainError.txt"))
info.Artifacts["mainError"] = GetBase64(GetFullFile($"{basePath}_mainError.txt")) ?? string.Empty;
if (File.Exists($"{basePath}_mainInfo.txt"))
info.Artifacts["mainInfo"] = GetBase64(GetFullFile($"{basePath}_mainInfo.txt")) ?? string.Empty;
//if (File.Exists($"{basePath}_PFI.bin"))
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")) ?? string.Empty;
if (File.Exists($"{basePath}_volDesc.txt"))
info.Artifacts["volDesc"] = GetBase64(GetFullFile($"{basePath}_volDesc.txt")) ?? string.Empty;
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
var logFiles = new List<string>();
switch (this.Type)
{
case MediaType.UMD:
if (File.Exists($"{basePath}_disc.txt"))
logFiles.Add($"{basePath}_disc.txt");
if (File.Exists($"{basePath}_drive.txt"))
logFiles.Add($"{basePath}_drive.txt");
if (File.Exists($"{basePath}_mainError.txt"))
logFiles.Add($"{basePath}_mainError.txt");
if (File.Exists($"{basePath}_mainInfo.txt"))
logFiles.Add($"{basePath}_mainInfo.txt");
if (File.Exists($"{basePath}_volDesc.txt"))
logFiles.Add($"{basePath}_volDesc.txt");
if (File.Exists($"{basePath}_PFI.bin"))
logFiles.Add($"{basePath}_PFI.bin");
break;
}
return logFiles;
}
#endregion
#region Information Extraction Methods
/// <summary>
/// Get the PVD from the input file, if possible
/// </summary>
/// <param name="mainInfo">_mainInfo.txt file location</param>
/// <returns>Newline-deliminated PVD if possible, null on error</returns>
private static string? GetPVD(string mainInfo)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(mainInfo))
return null;
try
{
// Make sure we're in the right sector
using var sr = File.OpenText(mainInfo);
while (sr.ReadLine()?.StartsWith("========== LBA[000016, 0x0000010]: Main Channel ==========") == false) ;
// Fast forward to the PVD
while (sr.ReadLine()?.StartsWith("0310") == false) ;
// Now that we're at the PVD, read each line in and concatenate
string pvd = "";
for (int i = 0; i < 6; i++)
pvd += sr.ReadLine() + "\n"; // 320-370
return pvd;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get the UMD auxiliary info from the outputted files, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
private static bool GetUMDAuxInfo(string disc, out string? title, out DiscCategory? umdcat, out string? umdversion, out string? umdlayer, out long umdsize)
{
title = null; umdcat = null; umdversion = null; umdlayer = null; umdsize = -1;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(disc))
return false;
try
{
// Loop through everything to get the first instance of each required field
using var sr = File.OpenText(disc);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line == null)
break;
if (line.StartsWith("TITLE") && title == null)
title = line.Substring("TITLE: ".Length);
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
umdversion = line.Split(' ')[1];
else if (line.StartsWith("pspUmdTypes"))
umdcat = InfoTool.GetUMDCategory(line.Split(' ')[1]);
else if (line.StartsWith("L0 length"))
umdlayer = line.Split(' ')[2];
else if (line.StartsWith("FileSize:"))
umdsize = Int64.Parse(line.Split(' ')[1]);
}
// If the L0 length is the size of the full disc, there's no layerbreak
if (Int64.TryParse(umdlayer, out long umdlayerValue) && umdlayerValue * 2048 == umdsize)
umdlayer = null;
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
/// <summary>
/// Get all Volume Identifiers
/// </summary>
/// <param name="volDesc">_volDesc.txt file location</param>
/// <returns>Volume labels (by type), or null if none present</returns>
/// <remarks>This is a copy of the code from DiscImageCreator and has extrandous checks</remarks>
private static bool GetVolumeLabels(string volDesc, out Dictionary<string, List<string>> volLabels)
{
// If the file doesn't exist, can't get the volume labels
volLabels = [];
if (!File.Exists(volDesc))
return false;
try
{
using var sr = File.OpenText(volDesc);
var line = sr.ReadLine();
string volType = "UNKNOWN";
string label;
while (line != null)
{
// Trim the line for later use
line = line.Trim();
// ISO9660 and extensions section
if (line.StartsWith("Volume Descriptor Type: "))
{
Int32.TryParse(line.Substring("Volume Descriptor Type: ".Length), out int volTypeInt);
volType = volTypeInt switch
{
// 0 => "Boot Record" // Should not not contain a Volume Identifier
1 => "ISO", // ISO9660
2 => "Joliet",
// 3 => "Volume Partition Descriptor" // Should not not contain a Volume Identifier
// 255 => "???" // Should not not contain a Volume Identifier
_ => "UNKNOWN" // Should not contain a Volume Identifier
};
}
// UDF section
else if (line.StartsWith("Primary Volume Descriptor Number:"))
{
volType = "UDF";
}
// Identifier
else if (line.StartsWith("Volume Identifier: "))
{
label = line.Substring("Volume Identifier: ".Length);
// Remove leading non-printable character (unsure why DIC outputs this)
if (Convert.ToUInt32(label[0]) == 0x7F || Convert.ToUInt32(label[0]) < 0x20)
label = label.Substring(1);
// Skip if label is blank
if (label == null || label.Length <= 0)
{
volType = "UNKNOWN";
line = sr.ReadLine();
continue;
}
if (volLabels.ContainsKey(label))
volLabels[label].Add(volType);
else
volLabels.Add(label, [volType]);
// Reset volume type
volType = "UNKNOWN";
}
line = sr.ReadLine();
}
// Return true if a volume label was found
return volLabels.Count > 0;
}
catch
{
// We don't care what the exception is right now
volLabels = [];
return false;
}
}
#endregion
}
}

View File

@@ -0,0 +1,677 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using SabreTools.Hashing;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.XboxBackupCreator
{
/// <summary>
/// Represents a generic set of Xbox Backup Creator parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.XboxBackupCreator;
#endregion
/// <inheritdoc/>
public Parameters(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD:
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
string? logPath = GetLogName(baseDir);
if (string.IsNullOrEmpty(logPath))
missingFiles.Add($"{baseDir}Log.txt");
if (!File.Exists($"{baseDir}DMI.bin"))
missingFiles.Add($"{baseDir}DMI.bin");
if (!File.Exists($"{baseDir}PFI.bin"))
missingFiles.Add($"{baseDir}PFI.bin");
if (!File.Exists($"{baseDir}SS.bin"))
missingFiles.Add($"{baseDir}SS.bin");
// Not required from XBC
//if (!File.Exists($"{basePath}.dvd"))
// missingFiles.Add($"{basePath}.dvd");
}
break;
default:
missingFiles.Add("Media and system combination not supported for XboxBackupCreator");
break;
}
return (!missingFiles.Any(), missingFiles);
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
{
// Ensure that required sections exist
info = Builder.EnsureAllSections(info);
// Get base directory
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
// Get log filename
string? logPath = GetLogName(baseDir);
if (string.IsNullOrEmpty(logPath))
return;
// XBC dump info
info.DumpingInfo!.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {GetVersion(logPath) ?? "Unknown Version"}";
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(logPath)?.ToString("yyyy-MM-dd HH:mm:ss");
info.DumpingInfo.Model = GetDrive(logPath) ?? "Unknown Drive";
// Look for read errors
if (GetReadErrors(logPath, out long readErrors))
info.CommonDiscInfo!.ErrorsCount = readErrors == -1 ? "Error retrieving error count" : readErrors.ToString();
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.DVD:
// Get Layerbreak from .dvd file if possible
if (GetLayerbreak($"{basePath}.dvd", out long layerbreak))
info.SizeAndChecksums!.Layerbreak = layerbreak;
// Hash data
if (HashTool.GetStandardHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
{
// Get the Datafile information
var datafile = new Datafile
{
Games = [new Game { Roms = [new Rom { Name = string.Empty, Size = filesize.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1, }] }]
};
// Fill in the hash data
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
info.SizeAndChecksums!.Size = filesize;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
}
switch (this.System)
{
case RedumpSystem.MicrosoftXbox:
// Parse DMI.bin
string xmidString = Tools.GetXGD1XMID($"{baseDir}DMI.bin");
var xmid = SabreTools.Serialization.Wrappers.XMID.Create(xmidString);
if (xmid != null)
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XMID] = xmidString?.TrimEnd('\0') ?? string.Empty;
info.CommonDiscInfo.Serial = xmid.Serial ?? string.Empty;
if (!options.EnableRedumpCompatibility)
info.VersionAndEditions!.Version = xmid.Version ?? string.Empty;
info.CommonDiscInfo.Region = InfoTool.GetXGDRegion(xmid.Model.RegionIdentifier);
}
break;
case RedumpSystem.MicrosoftXbox360:
// Get PVD from ISO
if (GetPVD(basePath + ".iso", out string? pvd))
info.Extras!.PVD = pvd;
// Parse Media ID
//string? mediaID = GetMediaID(logPath);
// Parse DMI.bin
string xemidString = Tools.GetXGD23XeMID($"{baseDir}DMI.bin");
var xemid = SabreTools.Serialization.Wrappers.XeMID.Create(xemidString);
if (xemid != null)
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XeMID] = xemidString?.TrimEnd('\0') ?? string.Empty;
info.CommonDiscInfo.Serial = xemid.Serial ?? string.Empty;
if (!options.EnableRedumpCompatibility)
info.VersionAndEditions!.Version = xemid.Version ?? string.Empty;
info.CommonDiscInfo.Region = InfoTool.GetXGDRegion(xemid.Model.RegionIdentifier);
}
break;
}
// Deal with SS.bin
if (File.Exists($"{baseDir}SS.bin"))
{
// Save security sector ranges
string? ranges = Tools.GetSSRanges($"{baseDir}SS.bin");
if (!string.IsNullOrEmpty(ranges))
info.Extras!.SecuritySectorRanges = ranges;
// TODO: Determine SS version?
//info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSVersion] =
// Recreate RawSS.bin
RecreateSS(logPath!, $"{baseDir}SS.bin", $"{baseDir}RawSS.bin");
// Run ss_sector_range to get repeatable SS hash
Tools.CleanSS($"{baseDir}SS.bin", $"{baseDir}SS.bin");
}
// DMI/PFI/SS CRC32 hashes
if (File.Exists($"{baseDir}DMI.bin"))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = HashTool.GetFileHash($"{baseDir}DMI.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
if (File.Exists($"{baseDir}PFI.bin"))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.PFIHash] = HashTool.GetFileHash($"{baseDir}PFI.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
if (File.Exists($"{baseDir}SS.bin"))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSHash] = HashTool.GetFileHash($"{baseDir}SS.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= [];
if (File.Exists(logPath))
info.Artifacts["log"] = GetBase64(GetFullFile(logPath!)) ?? string.Empty;
if (File.Exists($"{basePath}.dvd"))
info.Artifacts["dvd"] = GetBase64(GetFullFile($"{basePath}.dvd")) ?? string.Empty;
//if (File.Exists($"{baseDir}DMI.bin"))
// info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}DMI.bin")) ?? string.Empty;
// TODO: Include PFI artifact only if the hash doesn't match known PFI hashes
//if (File.Exists($"{baseDir}PFI.bin"))
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}PFI.bin")) ?? string.Empty;
//if (File.Exists($"{baseDir}SS.bin"))
// info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}SS.bin")) ?? string.Empty;
//if (File.Exists($"{baseDir}RawSS.bin"))
// info.Artifacts["rawss"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}RawSS.bin")) ?? string.Empty;
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
var logFiles = new List<string>();
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
switch (this.Type)
{
case MediaType.DVD:
string? logPath = GetLogName(baseDir);
if (!string.IsNullOrEmpty(logPath))
logFiles.Add(logPath!);
if (File.Exists($"{basePath}.dvd"))
logFiles.Add($"{basePath}.dvd");
if (File.Exists($"{baseDir}DMI.bin"))
logFiles.Add($"{baseDir}DMI.bin");
if (File.Exists($"{baseDir}PFI.bin"))
logFiles.Add($"{baseDir}PFI.bin");
if (File.Exists($"{baseDir}SS.bin"))
logFiles.Add($"{baseDir}SS.bin");
if (File.Exists($"{baseDir}RawSS.bin"))
logFiles.Add($"{baseDir}RawSS.bin");
break;
}
return logFiles;
}
#endregion
#region Information Extraction Methods
/// <summary>
/// Determines the file path of the XBC log
/// </summary>
/// <param name="baseDir">Base directory to search in</param>
/// <returns>Log path if found, null otherwise</returns>
private static string? GetLogName(string baseDir)
{
if (IsSuccessfulLog($"{baseDir}Log.txt"))
return $"{baseDir}Log.txt";
// Search for a renamed log file (assume there is only one)
string[] files = Directory.GetFiles(baseDir, "*.txt", SearchOption.TopDirectoryOnly);
foreach (string file in files)
{
if (IsSuccessfulLog(file))
return file;
}
return null;
}
/// <summary>
/// Checks if Log file has a successful read in it
/// </summary>
/// <param name="log">Path to log file</param>
/// <returns>True if successful log found, false otherwise</returns>
private static bool IsSuccessfulLog(string log)
{
if (!File.Exists(log))
return false;
// Successful Example:
// Read completed in 00:50:23
// Failed Example:
// Read failed
try
{
// If Version is not found, not a valid log file
if (string.IsNullOrEmpty(GetVersion(log)))
return false;
// Look for " Read completed in " in log file
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine();
if (line?.StartsWith(" Read completed in ") == true)
{
return true;
}
}
// We couldn't find a successful dump
return false;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
/// <summary>
/// Get the XBC version if possible
/// </summary>
/// <param name="log">Path to XBC log file</param>
/// <returns>Version if possible, null on error</returns>
private static string? GetVersion(string? log)
{
if (string.IsNullOrEmpty(log) || !File.Exists(log))
return null;
// Sample:
// ====================================================================
// Xbox Backup Creator v2.9 Build:0425 By Redline99
//
try
{
// Assume version is appended after first mention of Xbox Backup Creator
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("Xbox Backup Creator ") == true)
return line.Substring("Xbox Backup Creator ".Length).Trim();
}
// We couldn't detect the version
return null;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get the drive model from the log
/// </summary>
/// <param name="log">Path to XBC log file</param>
/// <returns>Drive model if found, null otherwise</returns>
private static string? GetDrive(string? log)
{
if (string.IsNullOrEmpty(log) || !File.Exists(log))
return null;
// Example:
// ========================================
// < --Security Sector Details -->
// Source Drive: SH-D162D
// ----------------------------------------
try
{
// Parse drive model from log file
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("Source Drive: ") == true)
{
return line.Substring("Source Drive: ".Length).Trim();
}
}
// We couldn't detect the drive model
return null;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get the Layerbreak value if possible
/// </summary>
/// <param name="dvd">Path to layerbreak file</param>
/// <param name="layerbreak">Layerbreak value if found</param>
/// <returns>True if successful, otherwise false</returns>
/// <returns></returns>
private static bool GetLayerbreak(string? dvd, out long layerbreak)
{
layerbreak = 0;
if (string.IsNullOrEmpty(dvd) || !File.Exists(dvd))
return false;
// Example:
// LayerBreak=1913776
// track.iso
try
{
// Parse Layerbreak value from DVD file
using var sr = File.OpenText(dvd);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("LayerBreak=") == true)
{
return long.TryParse(line.Substring("LayerBreak=".Length).Trim(), out layerbreak);
}
}
// We couldn't detect the Layerbreak
return false;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
/// <summary>
/// Get the read error count if possible
/// </summary>
/// <param name="log">Path to XBC log file</param>
/// <param name="readErrors">Read error count if found, -1 otherwise</param>
/// <returns>True if sucessful, otherwise false</returns>
private bool GetReadErrors(string? log, out long readErrors)
{
readErrors = -1;
if (string.IsNullOrEmpty(log) || !File.Exists(log))
return false;
// TODO: Logic when more than one dump is in the logs
// Example: (replace [E] with drive letter)
// Creating SplitVid backup image [E]
// ...
// Reading Game Partition
// Setting read speed to 1x
// Unrecovered read error at Partition LBA: 0
// Example: (replace track with base filename)
// Creating Layer Break File
// LayerBreak file saved as: "track.dvd"
// A total of 1 sectors were zeroed out.
// Example: (for Original Xbox)
// A total of 65,536 sectors were zeroed out.
// A total of 31 sectors with read errors were recovered.
try
{
// Parse Layerbreak value from DVD file
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("Creating Layer Break File") == true)
{
// Read error count is two lines below
line = sr.ReadLine()?.Trim();
line = sr.ReadLine()?.Trim();
if (line?.StartsWith("A total of ") == true && line?.EndsWith(" sectors were zeroed out.") == true)
{
string? errorCount = line.Substring("A total of ".Length, line.Length - 36).Replace(",", "").Trim();
bool success = long.TryParse(errorCount, out readErrors);
// Original Xbox should have 65536 read errors when dumping with XBC
if (this.System == RedumpSystem.MicrosoftXbox)
{
if (readErrors == 65536)
readErrors = 0;
else if (readErrors > 65536)
readErrors -= 65536;
}
return success;
}
}
}
// We couldn't detect the read error count
return false;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
/// <summary>
/// Get Xbox360 Media ID from XBC log file
/// </summary>
/// <param name="log">Path to XBC log file</param>
/// <returns>Media ID if Log successfully parsed, null otherwise</returns>
private string? GetMediaID(string? log)
{
if (string.IsNullOrEmpty(log) || !File.Exists(log))
return null;
if (this.System == RedumpSystem.MicrosoftXbox)
return null;
// Example:
// ----------------------------------------
// Media ID
// A76B9983D170EFF8749A892BC-8B62A812
// ----------------------------------------
try
{
// Parse Layerbreak value from DVD file
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("Media ID") == true)
{
line = sr.ReadLine()?.Trim();
return line?.Substring(25).Trim();
}
}
// We couldn't detect the Layerbreak
return null;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Recreate an SS.bin file from XBC log and write it to a file
/// </summary>
/// <param name="log">Path to XBC log</param>
/// <param name="cleanSS">Path to the clean SS file to read from</param>
/// <param name="rawSS">Path to the raw SS file to write to</param>
/// <returns>True if successful, false otherwise</returns>
private static bool RecreateSS(string log, string cleanSS, string rawSS)
{
if (!File.Exists(log) || !File.Exists(cleanSS))
return false;
byte[] ss = File.ReadAllBytes(cleanSS);
if (ss.Length != 2048)
return false;
if (!RecreateSS(log!, ss))
return false;
File.WriteAllBytes(rawSS, ss);
return true;
}
/// <summary>
/// Recreate an SS.bin byte array from an XBC log.
/// With help from https://github.com/hadzz/SS-Angle-Fixer/
/// </summary>
/// <param name="log">Path to XBC log</param>
/// <param name="ss">Byte array of SS sector</param>
/// <returns>True if successful, false otherwise</returns>
private static bool RecreateSS(string log, byte[] ss)
{
// Log file must exist
if (!File.Exists(log))
return false;
// SS must be complete sector
if (ss.Length != 2048)
return false;
// Ignore XGD1 discs
if (!Tools.GetXGDType(ss, out int xgdType))
return false;
if (xgdType == 0)
return false;
// Don't recreate an already raw SS
// (but do save to file, so return true)
if (!Tools.IsCleanSS(ss))
return true;
// Example replay table:
/*
----------------------------------------
RT CID MOD DATA Drive Response
-- -- -- ------------- -------------------
01 14 00 033100 0340FF B7D8C32A B703590100
03 BE 00 244530 24552F F4B9B528 BE46360500
01 97 00 DBBAD0 DBCACF DD7787F4 484977ED00
03 45 00 FCAF00 FCBEFF FB7A7773 AAB662FC00
05 6B 00 033100 033E7F 0A31252A 0200000200
07 46 00 244530 2452AF F8E77EBC 5B00005B00
05 36 00 DBBAD0 DBC84F F5DFA735 B50000B500
07 A1 00 FCAF00 FCBC7F 6B749DBF 0E01000E01
E0 50 00 42F4E1 00B6F7 00000000 0000000000
--------------------------------------------
*/
try
{
// Parse Replay Table from log
using var sr = File.OpenText(log);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line?.StartsWith("RT CID MOD DATA Drive Response") == true)
{
// Ignore next line
line = sr.ReadLine()?.Trim();
if (sr.EndOfStream)
return false;
byte[][] responses = new byte[4][];
// Parse the nine rows from replay table
for (int i = 0; i < 9; i++)
{
line = sr.ReadLine()?.Trim();
// Validate line
if (sr.EndOfStream || string.IsNullOrEmpty(line) || line!.Length < 44)
return false;
// Save useful angle responses
if (i >= 4 && i <= 7)
{
byte[]? angles = Tools.HexStringToByteArray(line!.Substring(34, 10));
if (angles == null || angles.Length != 5)
return false;
responses[i - 4] = angles!;
}
}
int rtOffset = 0x204;
if (xgdType == 3)
rtOffset = 0x24;
// Replace angles
for (int i = 0; i < 4; i++)
{
int offset = rtOffset + (9 * (i + 4));
for (int j = 0; j < 5; j++)
{
// Ignore the middle byte
if (j == 2)
continue;
ss[offset + j] = responses[i][j];
}
}
return true;
}
}
// We couldn't detect the replay table
return false;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
#endregion
}
}

View File

@@ -4,11 +4,11 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BinaryObjectScanner.Protection;
using BurnOutSharp;
using psxt001z;
namespace MPF.Library
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'.
namespace MPF.Core
{
public static class Protection
{
@@ -19,13 +19,14 @@ namespace MPF.Library
/// <param name="options">Options object that determines what to scan</param>
/// <param name="progress">Optional progress callback</param>
/// <returns>Set of all detected copy protections with an optional error string</returns>
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Core.Data.Options options, IProgress<ProtectionProgress> progress = null)
public static async Task<(Dictionary<string, List<string>>?, string?)> RunProtectionScanOnPath(string path, Data.Options options, IProgress<BinaryObjectScanner.ProtectionProgress>? progress = null)
{
try
{
var found = await Task.Run(() =>
#if NET40
var found = await Task.Factory.StartNew(() =>
{
var scanner = new Scanner(
var scanner = new BinaryObjectScanner.Scanner(
options.ScanArchivesForProtection,
scanContents: true, // Hardcoded value to avoid issues
scanGameEngines: false, // Hardcoded value to avoid issues
@@ -36,14 +37,37 @@ namespace MPF.Library
return scanner.GetProtections(path);
});
#else
var found = await Task.Run(() =>
{
var scanner = new BinaryObjectScanner.Scanner(
options.ScanArchivesForProtection,
scanContents: true, // Hardcoded value to avoid issues
scanGameEngines: false, // Hardcoded value to avoid issues
options.ScanPackersForProtection,
scanPaths: true, // Hardcoded value to avoid issues
options.IncludeDebugProtectionInformation,
progress);
return scanner.GetProtections(path);
});
#endif
// If nothing was returned, return
if (found == null || !found.Any())
#if NET20 || NET35
if (found == null || found.Count == 0)
#else
if (found == null || found.IsEmpty)
#endif
return (null, null);
// Filter out any empty protections
var filteredProtections = found
.Where(kvp => kvp.Value != null && kvp.Value.Any())
#if NET20 || NET35
.Where(kvp => kvp.Value != null && kvp.Value.Count > 0)
#else
.Where(kvp => kvp.Value != null && !kvp.Value.IsEmpty)
#endif
.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.OrderBy(s => s).ToList());
@@ -62,11 +86,11 @@ namespace MPF.Library
/// </summary>
/// <param name="protections">Dictionary of file to list of protection mappings</param>
/// <returns>Detected protections, if any</returns>
public static string FormatProtections(Dictionary<string, List<string>> protections)
public static string? FormatProtections(Dictionary<string, List<string>>? protections)
{
// If the filtered list is empty in some way, return
if (protections == null || !protections.Any())
return "None found";
return "None found [OMIT FROM SUBMISSION]";
// Get an ordered list of distinct found protections
var orderedDistinctProtections = protections
@@ -76,8 +100,8 @@ namespace MPF.Library
// Sanitize and join protections for writing
string protectionString = SanitizeFoundProtections(orderedDistinctProtections);
if (string.IsNullOrWhiteSpace(protectionString))
return "None found";
if (string.IsNullOrEmpty(protectionString))
return "None found [OMIT FROM SUBMISSION]";
return protectionString;
}
@@ -87,20 +111,25 @@ namespace MPF.Library
/// </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)
public static async Task<bool> GetPlayStationAntiModchipDetected(string? path)
{
return await Task.Run(() =>
// If there is no valid path
if (string.IsNullOrEmpty(path))
return false;
#if NET40
return await Task.Factory.StartNew(() =>
{
try
{
var antiModchip = new PSXAntiModchip();
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
{
try
{
byte[] fileContent = File.ReadAllBytes(file);
string protection = antiModchip.CheckContents(file, fileContent, false);
if (!string.IsNullOrWhiteSpace(protection))
var protection = antiModchip.CheckContents(file, fileContent, false);
if (!string.IsNullOrEmpty(protection))
return true;
}
catch { }
@@ -110,6 +139,33 @@ namespace MPF.Library
return false;
});
#else
return await Task.Run(() =>
{
try
{
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
#if NET20 || NET35
foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories))
#else
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
#endif
{
try
{
byte[] fileContent = File.ReadAllBytes(file);
var protection = antiModchip.CheckContents(file, fileContent, false);
if (!string.IsNullOrEmpty(protection))
return true;
}
catch { }
}
}
catch { }
return false;
});
#endif
}
/// <summary>
@@ -123,7 +179,7 @@ namespace MPF.Library
if (!File.Exists(sub))
return null;
return LibCrypt.CheckSubfile(sub);
return LibCrypt.DetectLibCrypt([sub]);
}
/// <summary>
@@ -132,12 +188,27 @@ namespace MPF.Library
/// <param name="foundProtections">Enumerable of found protections</param>
public static string SanitizeFoundProtections(IEnumerable<string> foundProtections)
{
// EXCEPTIONS
if (foundProtections.Any(p => p.StartsWith("[Exception opening file")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("[Exception opening file"));
#if NET20 || NET35 || NET40 || NET452 || NET462
var tempList = new List<string> { "Exception occurred while scanning [RESCAN NEEDED]" };
tempList.AddRange(foundProtections);
foundProtections = tempList.OrderBy(p => p);
#else
foundProtections = foundProtections
.Prepend("Exception occurred while scanning [RESCAN NEEDED]")
.OrderBy(p => p);
#endif
}
// 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"))
if (foundProtections.Any(p => Regex.IsMatch(p, @"Cactus Data Shield [0-9]{3} .+", RegexOptions.Compiled)) && foundProtections.Any(p => p == "Cactus Data Shield 200"))
foundProtections = foundProtections.Where(p => p != "Cactus Data Shield 200");
// CD-Check
@@ -167,7 +238,7 @@ namespace MPF.Library
// 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}")))
if (foundProtections.Any(p => Regex.IsMatch(p, @"JoWood X-Prot [0-9]\.[0-9]\.[0-9]\.[0-9]{2}", RegexOptions.Compiled)))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
.Where(p => p != "JoWood X-Prot v1.0-v1.3")
@@ -210,34 +281,43 @@ namespace MPF.Library
.Where(p => p != "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)");
if (foundProtections.Any(p => !p.StartsWith("SafeDisc")))
{
#if NET20 || NET35 || NET40 || NET452 || NET462
var tempList = new List<string>();
tempList.AddRange(foundProtections);
tempList.Add("Cactus Data Shield 300");
foundProtections = tempList;
#else
foundProtections = foundProtections.Append("Cactus Data Shield 300");
#endif
}
}
// SafeDisc
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}")))
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled) && !p.StartsWith("Macrovision Protection File")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => p != "SafeDisc")
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}")))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}")))
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled) && !p.StartsWith("Macrovision Protection File")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => p != "SafeDisc")
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled) && !p.StartsWith("Macrovision Protection File")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
@@ -277,7 +357,7 @@ namespace MPF.Library
// StarForce
if (foundProtections.Any(p => p.StartsWith("StarForce")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"StarForce [0-9]+\..+")))
if (foundProtections.Any(p => Regex.IsMatch(p, @"StarForce [0-9]+\..+", RegexOptions.Compiled)))
{
foundProtections = foundProtections.Where(p => p != "StarForce")
.Where(p => p != "StarForce 3-5")
@@ -312,7 +392,7 @@ namespace MPF.Library
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);
return string.Join(", ", foundProtections.ToArray());
}
}
}

View File

@@ -0,0 +1,760 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MPF.Core.Data;
using MPF.Core.Modules;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
namespace MPF.Core
{
/// <summary>
/// Class to hold all SubmissionInfo-related methods
/// </summary>
internal static class SubmissionInfoTool
{
#region Extraction and Filling
/// <summary>
/// Extract all of the possible information from a given input combination
/// </summary>
/// <param name="outputPath">Output path to write to</param>
/// <param name="drive">Drive object representing the current drive</param>
/// <param name="system">Currently selected system</param>
/// <param name="mediaType">Currently selected media type</param>
/// <param name="options">Options object representing user-defined options</param>
/// <param name="parameters">Parameters object representing what to send to the internal program</param>
/// <param name="resultProgress">Optional result progress callback</param>
/// <param name="protectionProgress">Optional protection progress callback</param>
/// <returns>SubmissionInfo populated based on outputs, null on error</returns>
public static async Task<SubmissionInfo?> ExtractOutputInformation(
string outputPath,
Drive? drive,
RedumpSystem? system,
MediaType? mediaType,
Options options,
BaseParameters? parameters,
IProgress<Result>? resultProgress = null,
IProgress<BinaryObjectScanner.ProtectionProgress>? protectionProgress = null)
{
// Ensure the current disc combination should exist
if (!system.MediaTypes().Contains(mediaType))
return null;
// Split the output path for easier use
var outputDirectory = Path.GetDirectoryName(outputPath);
string outputFilename = Path.GetFileName(outputPath);
// Check that all of the relevant files are there
(bool foundFiles, List<string> missingFiles) = parameters.FoundAllFiles(outputDirectory, outputFilename, false);
if (!foundFiles)
{
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
resultProgress?.Report(Result.Failure($"This may indicate an issue with the hardware or media, including unsupported devices.\nPlease see dumping program documentation for more details."));
return null;
}
// Sanitize the output filename to strip off any potential extension
outputFilename = Path.GetFileNameWithoutExtension(outputFilename);
// Create the SubmissionInfo object with all user-inputted values by default
string combinedBase;
if (string.IsNullOrEmpty(outputDirectory))
combinedBase = outputFilename;
else
combinedBase = Path.Combine(outputDirectory, outputFilename);
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
System = system,
Media = mediaType.ToDiscType(),
Title = options.AddPlaceholders ? Template.RequiredValue : string.Empty,
ForeignTitleNonLatin = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
DiscNumberLetter = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
DiscTitle = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
Category = null,
Region = null,
Languages = null,
Serial = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty,
Barcode = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
Contents = string.Empty,
},
VersionAndEditions = new VersionAndEditionsSection()
{
Version = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty,
OtherEditions = options.AddPlaceholders ? "(VERIFY THIS) Original" : string.Empty,
},
DumpingInfo = new DumpingInfoSection()
{
FrontendVersion = Utilities.Tools.GetCurrentVersion(),
},
};
// Ensure that required sections exist
info = Builder.EnsureAllSections(info);
// Get specific tool output handling
parameters?.GenerateSubmissionInfo(info, options, combinedBase, drive, options.IncludeArtifacts);
// Get a list of matching IDs for each line in the DAT
if (!string.IsNullOrEmpty(info.TracksAndWriteOffsets!.ClrMameProData) && options.HasRedumpLogin)
#if NET40
_ = FillFromRedump(options, info, resultProgress);
#else
_ = await FillFromRedump(options, info, resultProgress);
#endif
// If we have both ClrMamePro and Size and Checksums data, remove the ClrMamePro
if (!string.IsNullOrEmpty(info.SizeAndChecksums?.CRC32))
info.TracksAndWriteOffsets.ClrMameProData = null;
// Add the volume label to comments, if possible or necessary
string? volLabels = FormatVolumeLabels(drive?.VolumeLabel, parameters?.VolumeLabels);
if (volLabels != null)
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.VolumeLabel] = volLabels;
// Extract info based generically on MediaType
switch (mediaType)
{
case MediaType.CDROM:
case MediaType.GDROM:
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
break;
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
// If we have a single-layer disc
if (info.SizeAndChecksums!.Layerbreak == default)
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
// If we have a dual-layer disc
else if (info.SizeAndChecksums!.Layerbreak2 == default)
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
// If we have a triple-layer disc
else if (info.SizeAndChecksums!.Layerbreak3 == default)
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
// If we have a quad-layer disc
else
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer3MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer3MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer3ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
break;
case MediaType.NintendoGameCubeGameDisc:
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.Extras!.BCA ??= (options.AddPlaceholders ? Template.RequiredValue : string.Empty);
break;
case MediaType.NintendoWiiOpticalDisc:
// If we have a single-layer disc
if (info.SizeAndChecksums!.Layerbreak == default)
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
// If we have a dual-layer disc
else
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
info.Extras!.DiscKey = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.Extras.BCA = info.Extras.BCA ?? (options.AddPlaceholders ? Template.RequiredValue : string.Empty);
break;
case MediaType.UMD:
// Both single- and dual-layer discs have two "layers" for the ring
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.SizeAndChecksums!.CRC32 ??= (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty);
info.SizeAndChecksums.MD5 ??= (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty);
info.SizeAndChecksums.SHA1 ??= (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty);
info.TracksAndWriteOffsets.ClrMameProData = null;
break;
}
// Extract info based specifically on RedumpSystem
switch (system)
{
case RedumpSystem.AcornArchimedes:
info.CommonDiscInfo!.Region ??= Region.UnitedKingdom;
break;
case RedumpSystem.AppleMacintosh:
case RedumpSystem.EnhancedCD:
case RedumpSystem.IBMPCcompatible:
case RedumpSystem.PalmOS:
case RedumpSystem.PocketPC:
case RedumpSystem.RainbowDisc:
case RedumpSystem.SonyElectronicBook:
resultProgress?.Report(Result.Success("Running copy protection scan... this might take a while!"));
var (protectionString, fullProtections) = await InfoTool.GetCopyProtection(drive, options, protectionProgress);
info.CopyProtection!.Protection += protectionString;
info.CopyProtection.FullProtections = fullProtections as Dictionary<string, List<string>?> ?? [];
resultProgress?.Report(Result.Success("Copy protection scan complete!"));
if (system == RedumpSystem.EnhancedCD)
info.CommonDiscInfo!.Category ??= DiscCategory.Audio;
if (system == RedumpSystem.SonyElectronicBook)
info.CommonDiscInfo!.Category ??= DiscCategory.Multimedia;
break;
case RedumpSystem.AudioCD:
case RedumpSystem.DVDAudio:
case RedumpSystem.SuperAudioCD:
info.CommonDiscInfo!.Category ??= DiscCategory.Audio;
break;
case RedumpSystem.BandaiPlaydiaQuickInteractiveSystem:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= info.CommonDiscInfo.Region ?? Region.Japan;
break;
case RedumpSystem.BDVideo:
case RedumpSystem.DVDVideo:
case RedumpSystem.HDDVDVideo:
info.CommonDiscInfo!.Category ??= DiscCategory.Video;
info.CopyProtection!.Protection ??= options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
break;
case RedumpSystem.CommodoreAmigaCD:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.CommodoreAmigaCD32:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= Region.Europe;
break;
case RedumpSystem.CommodoreAmigaCDTV:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= Region.Europe;
break;
case RedumpSystem.FujitsuFMTownsseries:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= Region.Japan;
break;
case RedumpSystem.FujitsuFMTownsMarty:
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.HasbroVideoNow:
case RedumpSystem.HasbroVideoNowColor:
case RedumpSystem.HasbroVideoNowJr:
case RedumpSystem.VideoCD:
info.CommonDiscInfo!.Category ??= DiscCategory.Video;
break;
case RedumpSystem.HasbroVideoNowXP:
case RedumpSystem.PhotoCD:
info.CommonDiscInfo!.Category ??= DiscCategory.Multimedia;
break;
case RedumpSystem.IncredibleTechnologiesEagle:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamieAmusement:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiFireBeat:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiSystemGV:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiSystem573:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiTwinkle:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.MattelHyperScan:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.MicrosoftXboxOne:
if (drive?.Name != null)
{
string xboxOneMsxcPath = Path.Combine(drive.Name, "MSXC");
if (drive != null && Directory.Exists(xboxOneMsxcPath))
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = string.Join("\n",
Directory.GetFiles(xboxOneMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).ToArray());
}
}
break;
case RedumpSystem.MicrosoftXboxSeriesXS:
if (drive?.Name != null)
{
string xboxSeriesXMsxcPath = Path.Combine(drive.Name, "MSXC");
if (drive != null && Directory.Exists(xboxSeriesXMsxcPath))
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = string.Join("\n",
Directory.GetFiles(xboxSeriesXMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).ToArray());
}
}
break;
case RedumpSystem.NamcoSegaNintendoTriforce:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.NavisoftNaviken21:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= Region.Japan;
break;
case RedumpSystem.NECPC88series:
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.NECPC98series:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.NECPCFXPCFXGA:
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.SegaChihiro:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaDreamcast:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaNaomi:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaNaomi2:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaTitanVideo:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SharpX68000:
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.SNKNeoGeoCD:
info.CommonDiscInfo!.EXEDateBuildDate ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SonyPlayStation:
// Only check the disc if the dumping program couldn't detect
if (drive != null && info.CopyProtection!.AntiModchip == YesNo.NULL)
{
resultProgress?.Report(Result.Success("Checking for anti-modchip strings... this might take a while!"));
info.CopyProtection.AntiModchip = await InfoTool.GetAntiModchipDetected(drive) ? YesNo.Yes : YesNo.No;
resultProgress?.Report(Result.Success("Anti-modchip string scan complete!"));
}
// Special case for DIC only
if (parameters?.InternalProgram == InternalProgram.DiscImageCreator)
{
resultProgress?.Report(Result.Success("Checking for LibCrypt status... this might take a while!"));
InfoTool.GetLibCryptDetected(info, combinedBase);
resultProgress?.Report(Result.Success("LibCrypt status checking complete!"));
}
break;
case RedumpSystem.SonyPlayStation2:
info.CommonDiscInfo!.LanguageSelection ??= [];
break;
case RedumpSystem.SonyPlayStation3:
info.Extras!.DiscKey ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.Extras.DiscID ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.TomyKissSite:
info.CommonDiscInfo!.Category ??= DiscCategory.Video;
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.ZAPiTGamesGameWaveFamilyEntertainmentSystem:
info.CopyProtection!.Protection ??= options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
break;
}
// Set the category if it's not overriden
info.CommonDiscInfo!.Category ??= DiscCategory.Games;
// Comments and contents have odd handling
if (string.IsNullOrEmpty(info.CommonDiscInfo.Comments))
info.CommonDiscInfo.Comments = options.AddPlaceholders ? Template.OptionalValue : string.Empty;
if (string.IsNullOrEmpty(info.CommonDiscInfo.Contents))
info.CommonDiscInfo.Contents = options.AddPlaceholders ? Template.OptionalValue : string.Empty;
// Normalize the disc type with all current information
Validator.NormalizeDiscType(info);
return info;
}
/// <summary>
/// Fill in a SubmissionInfo object from Redump, if possible
/// </summary>
/// <param name="options">Options object representing user-defined options</param>
/// <param name="info">Existing SubmissionInfo object to fill</param>
/// <param name="resultProgress">Optional result progress callback</param>
#if NET40
public static bool FillFromRedump(Options options, SubmissionInfo info, IProgress<Result>? resultProgress = null)
#else
public async static Task<bool> FillFromRedump(Options options, SubmissionInfo info, IProgress<Result>? resultProgress = null)
#endif
{
// If no username is provided
if (string.IsNullOrEmpty(options.RedumpUsername) || string.IsNullOrEmpty(options.RedumpPassword))
return false;
// Set the current dumper based on username
info.DumpersAndStatus ??= new DumpersAndStatusSection();
info.DumpersAndStatus.Dumpers = [options.RedumpUsername!];
info.PartiallyMatchedIDs = [];
// Login to Redump
#if NETFRAMEWORK
using var wc = new RedumpWebClient();
bool? loggedIn = wc.Login(options.RedumpUsername!, options.RedumpPassword!);
#else
using var wc = new RedumpHttpClient();
bool? loggedIn = await wc.Login(options.RedumpUsername, options.RedumpPassword);
#endif
if (loggedIn == null)
{
resultProgress?.Report(Result.Failure("There was an unknown error connecting to Redump"));
return false;
}
else if (loggedIn == false)
{
// Don't log the as a failure or error
return false;
}
// Setup the full-track checks
bool allFound = true;
List<int>? fullyMatchedIDs = null;
// Loop through all of the hashdata to find matching IDs
resultProgress?.Report(Result.Success("Finding disc matches on Redump..."));
var splitData = info.TracksAndWriteOffsets?.ClrMameProData?.TrimEnd('\n')?.Split('\n');
int trackCount = splitData?.Length ?? 0;
foreach (string hashData in splitData ?? [])
{
// Catch any errant blank lines
if (string.IsNullOrEmpty(hashData))
{
trackCount--;
resultProgress?.Report(Result.Success("Blank line found, skipping!"));
continue;
}
// If the line ends in a known extra track names, skip them for checking
if (hashData.Contains("(Track 0).bin")
|| hashData.Contains("(Track 0.2).bin")
|| hashData.Contains("(Track 00).bin")
|| hashData.Contains("(Track 00.2).bin")
|| hashData.Contains("(Track A).bin")
|| hashData.Contains("(Track A.2).bin")
|| hashData.Contains("(Track AA).bin")
|| hashData.Contains("(Track AA.2).bin"))
{
trackCount--;
resultProgress?.Report(Result.Success("Extra track found, skipping!"));
continue;
}
// Get the SHA-1 hash
if (!InfoTool.GetISOHashValues(hashData, out _, out _, out _, out string? sha1))
{
resultProgress?.Report(Result.Failure($"Line could not be parsed: {hashData}"));
continue;
}
#if NET40
var validateTask = Validator.ValidateSingleTrack(wc, info, sha1);
validateTask.Wait();
(bool singleFound, var foundIds, string? result) = validateTask.Result;
#else
(bool singleFound, var foundIds, string? result) = await Validator.ValidateSingleTrack(wc, info, sha1);
#endif
if (singleFound)
resultProgress?.Report(Result.Success(result));
else
resultProgress?.Report(Result.Failure(result));
// Ensure that all tracks are found
allFound &= singleFound;
// If we found a track, only keep track of distinct found tracks
if (singleFound && foundIds != null)
{
if (fullyMatchedIDs == null)
fullyMatchedIDs = foundIds;
else
fullyMatchedIDs = fullyMatchedIDs.Intersect(foundIds).ToList();
}
// If no tracks were found, remove all fully matched IDs found so far
else
{
fullyMatchedIDs = [];
}
}
// If we don't have any matches but we have a universal hash
if (!info.PartiallyMatchedIDs.Any() && info.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) == true)
{
#if NET40
var validateTask = Validator.ValidateUniversalHash(wc, info);
validateTask.Wait();
(bool singleFound, var foundIds, string? result) = validateTask.Result;
#else
(bool singleFound, var foundIds, string? result) = await Validator.ValidateUniversalHash(wc, info);
#endif
if (singleFound)
resultProgress?.Report(Result.Success(result));
else
resultProgress?.Report(Result.Failure(result));
// Ensure that the hash is found
allFound = singleFound;
// If we found a track, only keep track of distinct found tracks
if (singleFound && foundIds != null)
{
fullyMatchedIDs = foundIds;
}
// If no tracks were found, remove all fully matched IDs found so far
else
{
fullyMatchedIDs = [];
}
}
// Make sure we only have unique IDs
info.PartiallyMatchedIDs = [.. info.PartiallyMatchedIDs.Distinct().OrderBy(id => id)];
resultProgress?.Report(Result.Success("Match finding complete! " + (fullyMatchedIDs != null && fullyMatchedIDs.Count > 0
? "Fully Matched IDs: " + string.Join(",", fullyMatchedIDs.Select(i => i.ToString()).ToArray())
: "No matches found")));
// Exit early if one failed or there are no matched IDs
if (!allFound || fullyMatchedIDs == null || fullyMatchedIDs.Count == 0)
return false;
// Find the first matched ID where the track count matches, we can grab a bunch of info from it
int totalMatchedIDsCount = fullyMatchedIDs.Count;
for (int i = 0; i < totalMatchedIDsCount; i++)
{
// Skip if the track count doesn't match
#if NET40
var validateTask = Validator.ValidateTrackCount(wc, fullyMatchedIDs[i], trackCount);
validateTask.Wait();
if (!validateTask.Result)
#else
if (!await Validator.ValidateTrackCount(wc, fullyMatchedIDs[i], trackCount))
#endif
continue;
// Fill in the fields from the existing ID
resultProgress?.Report(Result.Success($"Filling fields from existing ID {fullyMatchedIDs[i]}..."));
#if NET40
var fillTask = Task.Factory.StartNew(() => Builder.FillFromId(wc, info, fullyMatchedIDs[i], options.PullAllInformation));
fillTask.Wait();
_ = fillTask.Result;
#else
_ = await Builder.FillFromId(wc, info, fullyMatchedIDs[i], options.PullAllInformation);
#endif
resultProgress?.Report(Result.Success("Information filling complete!"));
// Set the fully matched ID to the current
info.FullyMatchedID = fullyMatchedIDs[i];
break;
}
// Clear out fully matched IDs from the partial list
if (info.FullyMatchedID.HasValue)
{
if (info.PartiallyMatchedIDs.Count == 1)
info.PartiallyMatchedIDs = null;
else
info.PartiallyMatchedIDs.Remove(info.FullyMatchedID.Value);
}
return true;
}
#endregion
#region Helper Functions
/// <summary>
/// Formats a list of volume labels and their corresponding filesystems
/// </summary>
/// <param name="labels">Dictionary of volume labels and their filesystems</param>
/// <returns>Formatted string of volume labels and their filesystems</returns>
private static string? FormatVolumeLabels(string? driveLabel, Dictionary<string, List<string>>? labels)
{
// Must have at least one label to format
if (driveLabel == null && (labels == null || labels.Count == 0))
return null;
// If no labels given, use drive label
if (labels == null || labels.Count == 0)
{
// Ignore common volume labels
if (Drive.GetRedumpSystemFromVolumeLabel(driveLabel) != null)
return null;
return driveLabel;
}
// If only one label, don't mention fs
string firstLabel = labels.First().Key;
if (labels.Count == 1 && (firstLabel == driveLabel || driveLabel == null))
{
// Ignore common volume labels
if (Drive.GetRedumpSystemFromVolumeLabel(firstLabel) != null)
return null;
return firstLabel;
}
// Otherwise, state filesystem for each label
List<string> volLabels = [];
// Begin formatted output with the label from Windows, if it is unique and not a common volume label
if (driveLabel != null && !labels.TryGetValue(driveLabel, out List<string>? value) && Drive.GetRedumpSystemFromVolumeLabel(driveLabel) == null)
volLabels.Add(driveLabel);
// Add remaining labels with their corresponding filesystems
foreach (KeyValuePair<string, List<string>> label in labels)
{
// Ignore common volume labels
if (Drive.GetRedumpSystemFromVolumeLabel(label.Key) == null)
volLabels.Add($"{label.Key} ({string.Join(", ", [.. label.Value])})");
}
// Ensure that no labels are empty
volLabels = volLabels.Where(l => !string.IsNullOrEmpty(l?.Trim())).ToList();
// Print each label separated by a comma and a space
if (volLabels.Count == 0)
return null;
return string.Join(", ", [.. volLabels]);
}
#endregion
}
}

View File

@@ -3,13 +3,13 @@ using System.Collections.Generic;
using System.Linq;
using MPF.Core.Converters;
namespace MPF.UI.Core.ComboBoxItems
namespace MPF.Core.UI.ComboBoxItems
{
/// <summary>
/// A generic combo box element
/// </summary>
/// <typeparam name="T">Enum type representing the possible values</typeparam>
public class Element<T> : IElement where T : struct, Enum
public class Element<T> : IEquatable<Element<T>>, IElement where T : struct, Enum
{
private readonly T Data;
@@ -47,5 +47,23 @@ namespace MPF.UI.Core.ComboBoxItems
.OfType<T>()
.Select(e => new Element<T>(e));
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return Equals(obj as Element<T>);
}
/// <inheritdoc/>
public bool Equals(Element<T>? other)
{
if (other == null)
return false;
return Name == other.Name;
}
/// <inheritdoc/>
public override int GetHashCode() => base.GetHashCode();
}
}

View File

@@ -1,4 +1,4 @@
namespace MPF.UI.Core.ComboBoxItems
namespace MPF.Core.UI.ComboBoxItems
{
public interface IElement
{

View File

@@ -1,18 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Core.Utilities;
using MPF.UI.Core.ComboBoxItems;
using SabreTools.RedumpLib.Data;
namespace MPF.UI.Core.ComboBoxItems
namespace MPF.Core.UI.ComboBoxItems
{
/// <summary>
/// Represents a single item in the System combo box
/// </summary>
public class RedumpSystemComboBoxItem : IElement
public class RedumpSystemComboBoxItem : IEquatable<RedumpSystemComboBoxItem>, IElement
{
private readonly object Data;
private readonly object? Data;
public RedumpSystemComboBoxItem(RedumpSystem? system) => Data = system;
public RedumpSystemComboBoxItem(SystemCategory? category) => Data = category;
@@ -81,5 +79,23 @@ namespace MPF.UI.Core.ComboBoxItems
return systemsValues;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return Equals(obj as RedumpSystemComboBoxItem);
}
/// <inheritdoc/>
public bool Equals(RedumpSystemComboBoxItem? other)
{
if (other == null)
return false;
return Value == other.Value;
}
/// <inheritdoc/>
public override int GetHashCode() => base.GetHashCode();
}
}

View File

@@ -0,0 +1,484 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BinaryObjectScanner;
using MPF.Core.Data;
using MPF.Core.UI.ComboBoxItems;
using MPF.Core.Utilities;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.UI.ViewModels
{
/// <summary>
/// Constructor
/// </summary>
public class CheckDumpViewModel : INotifyPropertyChanged
{
#region Fields
/// <summary>
/// Access to the current options
/// </summary>
public Data.Options Options
{
get => _options;
}
private readonly Data.Options _options;
/// <summary>
/// Indicates if SelectionChanged events can be executed
/// </summary>
public bool CanExecuteSelectionChanged { get; private set; } = false;
/// <inheritdoc/>
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
#region Properties
/// <summary>
/// Currently selected system value
/// </summary>
public RedumpSystem? CurrentSystem
{
get => _currentSystem;
set
{
_currentSystem = value;
TriggerPropertyChanged(nameof(CurrentSystem));
}
}
private RedumpSystem? _currentSystem;
/// <summary>
/// Indicates the status of the system type combo box
/// </summary>
public bool SystemTypeComboBoxEnabled
{
get => _systemTypeComboBoxEnabled;
set
{
_systemTypeComboBoxEnabled = value;
TriggerPropertyChanged(nameof(SystemTypeComboBoxEnabled));
}
}
private bool _systemTypeComboBoxEnabled;
/// <summary>
/// Currently selected media type value
/// </summary>
public MediaType? CurrentMediaType
{
get => _currentMediaType;
set
{
_currentMediaType = value;
TriggerPropertyChanged(nameof(CurrentMediaType));
}
}
private MediaType? _currentMediaType;
/// <summary>
/// Indicates the status of the media type combo box
/// </summary>
public bool MediaTypeComboBoxEnabled
{
get => _mediaTypeComboBoxEnabled;
set
{
_mediaTypeComboBoxEnabled = value;
TriggerPropertyChanged(nameof(MediaTypeComboBoxEnabled));
}
}
private bool _mediaTypeComboBoxEnabled;
/// <summary>
/// Currently provided input path
/// </summary>
public string? InputPath
{
get => _inputPath;
set
{
_inputPath = value;
TriggerPropertyChanged(nameof(InputPath));
}
}
private string? _inputPath;
/// <summary>
/// Indicates the status of the input path text box
/// </summary>
public bool InputPathTextBoxEnabled
{
get => _inputPathTextBoxEnabled;
set
{
_inputPathTextBoxEnabled = value;
TriggerPropertyChanged(nameof(InputPathTextBoxEnabled));
}
}
private bool _inputPathTextBoxEnabled;
/// <summary>
/// Indicates the status of the input path browse button
/// </summary>
public bool InputPathBrowseButtonEnabled
{
get => _inputPathBrowseButtonEnabled;
set
{
_inputPathBrowseButtonEnabled = value;
TriggerPropertyChanged(nameof(InputPathBrowseButtonEnabled));
}
}
private bool _inputPathBrowseButtonEnabled;
/// <summary>
/// Currently selected dumping program
/// </summary>
public InternalProgram CurrentProgram
{
get => _currentProgram;
set
{
_currentProgram = value;
TriggerPropertyChanged(nameof(CurrentProgram));
}
}
private InternalProgram _currentProgram;
/// <summary>
/// Indicates the status of the dumping program combo box
/// </summary>
public bool DumpingProgramComboBoxEnabled
{
get => _dumpingProgramComboBoxEnabled;
set
{
_dumpingProgramComboBoxEnabled = value;
TriggerPropertyChanged(nameof(DumpingProgramComboBoxEnabled));
}
}
private bool _dumpingProgramComboBoxEnabled;
/// <summary>
/// Indicates the status of the check dump button
/// </summary>
public bool CheckDumpButtonEnabled
{
get => _checkDumpButtonEnabled;
set
{
_checkDumpButtonEnabled = value;
TriggerPropertyChanged(nameof(CheckDumpButtonEnabled));
}
}
private bool _checkDumpButtonEnabled;
/// <summary>
/// Indicates the status of the cancel button
/// </summary>
public bool CancelButtonEnabled
{
get => _cancelButtonEnabled;
set
{
_cancelButtonEnabled = value;
TriggerPropertyChanged(nameof(CancelButtonEnabled));
}
}
private bool _cancelButtonEnabled;
#endregion
#region List Properties
/// <summary>
/// Current list of supported media types
/// </summary>
public List<Element<MediaType>>? MediaTypes
{
get => _mediaTypes;
set
{
_mediaTypes = value;
TriggerPropertyChanged(nameof(MediaTypes));
}
}
private List<Element<MediaType>>? _mediaTypes;
/// <summary>
/// Current list of supported system profiles
/// </summary>
public List<RedumpSystemComboBoxItem> Systems
{
get => _systems;
set
{
_systems = value;
TriggerPropertyChanged(nameof(Systems));
}
}
private List<RedumpSystemComboBoxItem> _systems;
/// <summary>
/// List of available internal programs
/// </summary>
public List<Element<InternalProgram>> InternalPrograms
{
get => _internalPrograms;
set
{
_internalPrograms = value;
TriggerPropertyChanged(nameof(InternalPrograms));
}
}
private List<Element<InternalProgram>> _internalPrograms;
#endregion
/// <summary>
/// Constructor for pure view model
/// </summary>
public CheckDumpViewModel()
{
_options = OptionsLoader.LoadFromConfig();
_internalPrograms = [];
_inputPath = string.Empty;
_systems = [];
SystemTypeComboBoxEnabled = true;
InputPathTextBoxEnabled = true;
InputPathBrowseButtonEnabled = true;
MediaTypeComboBoxEnabled = true;
DumpingProgramComboBoxEnabled = true;
CheckDumpButtonEnabled = false;
CancelButtonEnabled = true;
MediaTypes = [];
Systems = RedumpSystemComboBoxItem.GenerateElements().ToList();
InternalPrograms = [];
PopulateMediaType();
PopulateInternalPrograms();
EnableEventHandlers();
}
#region Property Updates
/// <summary>
/// Trigger a property changed event
/// </summary>
private void TriggerPropertyChanged(string propertyName)
{
// Disable event handlers temporarily
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
// If the property change event is initialized
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
#endregion
#region UI Commands
/// <summary>
/// Change the currently selected system
/// </summary>
public void ChangeSystem()
{
PopulateMediaType();
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
/// Change the currently selected media type
/// </summary>
public void ChangeMediaType()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
/// Change the currently selected dumping program
/// </summary>
public void ChangeDumpingProgram()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
/// Change the currently selected input path
/// </summary>
public void ChangeInputPath()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
#endregion
#region UI Control
/// <summary>
/// Enables all UI elements that should be enabled
/// </summary>
private void EnableUIElements()
{
SystemTypeComboBoxEnabled = true;
InputPathTextBoxEnabled = true;
InputPathBrowseButtonEnabled = true;
DumpingProgramComboBoxEnabled = true;
PopulateMediaType();
CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
CancelButtonEnabled = true;
}
/// <summary>
/// Disables all UI elements
/// </summary>
private void DisableUIElements()
{
SystemTypeComboBoxEnabled = false;
InputPathTextBoxEnabled = false;
InputPathBrowseButtonEnabled = false;
MediaTypeComboBoxEnabled = false;
DumpingProgramComboBoxEnabled = false;
CheckDumpButtonEnabled = false;
CancelButtonEnabled = false;
}
#endregion
#region Population
/// <summary>
/// Populate media type according to system type
/// </summary>
private void PopulateMediaType()
{
// Disable other UI updates
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
if (this.CurrentSystem != null)
{
var mediaTypeValues = this.CurrentSystem.MediaTypes();
int index = mediaTypeValues.FindIndex(m => m == this.CurrentMediaType);
MediaTypes = mediaTypeValues.Select(m => new Element<MediaType>(m ?? MediaType.NONE)).ToList();
this.MediaTypeComboBoxEnabled = MediaTypes.Count > 1;
this.CurrentMediaType = (index > -1 ? MediaTypes[index] : MediaTypes[0]);
}
else
{
this.MediaTypeComboBoxEnabled = false;
this.MediaTypes = null;
this.CurrentMediaType = null;
}
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
/// <summary>
/// Populate media type according to system type
/// </summary>
private void PopulateInternalPrograms()
{
// Disable other UI updates
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
// Get the current internal program
InternalProgram internalProgram = this.Options.InternalProgram;
// Create a static list of supported Check programs, not everything
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.Aaru, InternalProgram.DiscImageCreator, InternalProgram.CleanRip, InternalProgram.PS3CFW, InternalProgram.UmdImageCreator, InternalProgram.XboxBackupCreator };
InternalPrograms = internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
// Select the current default dumping program
int currentIndex = InternalPrograms.FindIndex(m => m == internalProgram);
this.CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
#endregion
#region UI Functionality
private bool ShouldEnableCheckDumpButton()
{
return this.CurrentSystem != null && this.CurrentMediaType != null && !string.IsNullOrEmpty(this.InputPath);
}
/// <summary>
/// Enable all textbox and combobox event handlers
/// </summary>
private void EnableEventHandlers()
{
CanExecuteSelectionChanged = true;
}
/// <summary>
/// Disable all textbox and combobox event handlers
/// </summary>
private void DisableEventHandlers()
{
CanExecuteSelectionChanged = false;
}
#endregion
#region MPF.Check
/// <summary>
/// Performs MPF.Check functionality
/// </summary>
/// <returns>An error message if failed, otherwise string.Empty/null</returns>
public async Task<string?> CheckDump(Func<SubmissionInfo?, (bool?, SubmissionInfo?)> processUserInfo)
{
if (string.IsNullOrEmpty(InputPath))
return "Invalid Input path";
if (!File.Exists(this.InputPath!.Trim('"')))
return "Input Path is not a valid file";
// Disable UI while Check is running
DisableUIElements();
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
// Populate an environment
var env = new DumpEnvironment(Options, Path.GetFullPath(this.InputPath.Trim('"')), null, this.CurrentSystem, this.CurrentMediaType, this.CurrentProgram, parameters: null);
// Make new Progress objects
var resultProgress = new Progress<Result>();
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
// Finally, attempt to do the output dance
var result = await env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress, processUserInfo);
// Reenable UI and event handlers, if necessary
EnableUIElements();
if (cachedCanExecuteSelectionChanged)
EnableEventHandlers();
return result.Message;
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,247 @@
using System.Collections.Generic;
using System.Linq;
using MPF.Core.Data;
using MPF.Core.UI.ComboBoxItems;
using MPF.Core.Utilities;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.UI.ViewModels
{
public class DiscInformationViewModel
{
#region Fields
/// <summary>
/// Application-level Options object
/// </summary>
public Options Options { 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 Redump-supported Regions
/// </summary>
private static readonly List<Region> RedumpRegions = new()
{
Region.Argentina,
Region.Asia,
Region.AsiaEurope,
Region.AsiaUSA,
Region.Australia,
Region.AustraliaGermany,
Region.AustraliaNewZealand,
Region.Austria,
Region.AustriaSwitzerland,
Region.Belarus,
Region.Belgium,
Region.BelgiumNetherlands,
Region.Brazil,
Region.Bulgaria,
Region.Canada,
Region.China,
Region.Croatia,
Region.Czechia,
Region.Denmark,
Region.Estonia,
Region.Europe,
Region.EuropeAsia,
Region.EuropeAustralia,
Region.EuropeCanada,
Region.EuropeGermany,
Region.Export,
Region.Finland,
Region.France,
Region.FranceSpain,
Region.Germany,
Region.GreaterChina,
Region.Greece,
Region.Hungary,
Region.Iceland,
Region.India,
Region.Ireland,
Region.Israel,
Region.Italy,
Region.Japan,
Region.JapanAsia,
Region.JapanEurope,
Region.JapanKorea,
Region.JapanUSA,
Region.SouthKorea,
Region.LatinAmerica,
Region.Lithuania,
Region.Netherlands,
Region.NewZealand,
Region.Norway,
Region.Poland,
Region.Portugal,
Region.Romania,
Region.RussianFederation,
Region.Scandinavia,
Region.Serbia,
Region.Singapore,
Region.Slovakia,
Region.SouthAfrica,
Region.Spain,
Region.SpainPortugal,
Region.Sweden,
Region.Switzerland,
Region.Taiwan,
Region.Thailand,
Region.Turkey,
Region.UnitedArabEmirates,
Region.UnitedKingdom,
Region.UKAustralia,
Region.Ukraine,
Region.UnitedStatesOfAmerica,
Region.USAAsia,
Region.USAAustralia,
Region.USABrazil,
Region.USACanada,
Region.USAEurope,
Region.USAGermany,
Region.USAJapan,
Region.USAKorea,
Region.World,
};
/// <summary>
/// List of available languages
/// </summary>
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
/// <summary>
/// List of Redump-supported Languages
/// </summary>
private static readonly List<Language> RedumpLanguages = new()
{
Language.Afrikaans,
Language.Albanian,
Language.Arabic,
Language.Armenian,
Language.Basque,
Language.Belarusian,
Language.Bulgarian,
Language.Catalan,
Language.Chinese,
Language.Croatian,
Language.Czech,
Language.Danish,
Language.Dutch,
Language.English,
Language.Estonian,
Language.Finnish,
Language.French,
Language.Gaelic,
Language.German,
Language.Greek,
Language.Hebrew,
Language.Hindi,
Language.Hungarian,
Language.Icelandic,
Language.Indonesian,
Language.Italian,
Language.Japanese,
Language.Korean,
Language.Latin,
Language.Latvian,
Language.Lithuanian,
Language.Macedonian,
Language.Norwegian,
Language.Polish,
Language.Portuguese,
Language.Panjabi,
Language.Romanian,
Language.Russian,
Language.Serbian,
Language.Slovak,
Language.Slovenian,
Language.Spanish,
Language.Swedish,
Language.Tamil,
Language.Thai,
Language.Turkish,
Language.Ukrainian,
Language.Vietnamese,
};
/// <summary>
/// List of available languages
/// </summary>
public List<Element<LanguageSelection>> LanguageSelections { get; private set; } = Element<LanguageSelection>.GenerateElements().ToList();
#endregion
/// <summary>
/// Constructor
/// </summary>
public DiscInformationViewModel(Options options, SubmissionInfo? submissionInfo)
{
Options = options;
SubmissionInfo = submissionInfo?.Clone() as SubmissionInfo ?? new SubmissionInfo();
}
#region Helpers
/// <summary>
/// Load the current contents of the base SubmissionInfo to the UI
/// </summary>
/// TODO: Convert selected list item to binding
public void Load()
{
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));
}
/// <summary>
/// Save the current contents of the UI to the base SubmissionInfo
/// </summary>
/// TODO: Convert selected list item to binding
public void Save()
{
if (SubmissionInfo.CommonDiscInfo == null)
SubmissionInfo.CommonDiscInfo = new CommonDiscInfoSection();
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
SubmissionInfo.CommonDiscInfo.Languages = [null];
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
SubmissionInfo.CommonDiscInfo.Title = InfoTool.NormalizeDiscTitle(SubmissionInfo.CommonDiscInfo.Title, SubmissionInfo.CommonDiscInfo.Languages);
}
/// <summary>
/// Repopulate the list of Languages based on Redump support
/// </summary>
public void SetRedumpLanguages()
{
this.Languages = RedumpLanguages.Select(l => new Element<Language>(l)).ToList();
}
/// <summary>
/// Repopulate the list of Regions based on Redump support
/// </summary>
public void SetRedumpRegions()
{
this.Regions = RedumpRegions.Select(r => new Element<Region>(r)).ToList();
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,164 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using MPF.Core.Data;
using MPF.Core.UI.ComboBoxItems;
using SabreTools.RedumpLib.Web;
namespace MPF.Core.UI.ViewModels
{
/// <summary>
/// Constructor
/// </summary>
public class OptionsViewModel : INotifyPropertyChanged
{
#region Fields
/// <summary>
/// Title for the window
/// </summary>
public string? Title
{
get => _title;
set
{
_title = value;
TriggerPropertyChanged(nameof(Title));
}
}
private string? _title;
/// <summary>
/// Current set of options
/// </summary>
public Options Options { get; }
/// <summary>
/// Flag for if settings were saved or not
/// </summary>
public bool SavedSettings { get; set; }
/// <inheritdoc/>
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
#region Lists
/// <summary>
/// List of available internal programs
/// </summary>
public static List<Element<InternalProgram>> InternalPrograms => PopulateInternalPrograms();
/// <summary>
/// Current list of supported Redumper read methods
/// </summary>
public static List<Element<RedumperReadMethod>> RedumperReadMethods => PopulateRedumperReadMethods();
/// <summary>
/// Current list of supported Redumper sector orders
/// </summary>
public static List<Element<RedumperSectorOrder>> RedumperSectorOrders => PopulateRedumperSectorOrders();
/// <summary>
/// Current list of supported system profiles
/// </summary>
public static List<RedumpSystemComboBoxItem> Systems => RedumpSystemComboBoxItem.GenerateElements().ToList();
#endregion
/// <summary>
/// Constructor for pure view model
/// </summary>
public OptionsViewModel()
{
Options = new Options();
}
/// <summary>
/// Constructor for in-code
/// </summary>
public OptionsViewModel(Options baseOptions)
{
Options = new Options(baseOptions);
}
#region Population
/// <summary>
/// Get a complete list of supported internal programs
/// </summary>
private static List<Element<InternalProgram>> PopulateInternalPrograms()
{
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.DiscImageCreator, InternalProgram.Aaru };
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
}
/// <summary>
/// Get a complete list of supported redumper drive read methods
/// </summary>
private static List<Element<RedumperReadMethod>> PopulateRedumperReadMethods()
{
var readMethods = new List<RedumperReadMethod> { RedumperReadMethod.NONE, RedumperReadMethod.D8, RedumperReadMethod.BE, RedumperReadMethod.BE_CDDA };
return readMethods.Select(rm => new Element<RedumperReadMethod>(rm)).ToList();
}
/// <summary>
/// Get a complete list of supported redumper drive sector orders
/// </summary>
private static List<Element<RedumperSectorOrder>> PopulateRedumperSectorOrders()
{
var sectorOrders = new List<RedumperSectorOrder> { RedumperSectorOrder.NONE, RedumperSectorOrder.DATA_C2_SUB, RedumperSectorOrder.DATA_SUB_C2, RedumperSectorOrder.DATA_SUB, RedumperSectorOrder.DATA_C2 };
return sectorOrders.Select(so => new Element<RedumperSectorOrder>(so)).ToList();
}
#endregion
#region UI Commands
/// <summary>
/// Test Redump login credentials
/// </summary>
#if NET40
public static Task<(bool?, string?)> TestRedumpLogin(string username, string password)
#else
public static async Task<(bool?, string?)> TestRedumpLogin(string username, string password)
#endif
{
#if NET40
return Task.Factory.StartNew(() => RedumpWebClient.ValidateCredentials(username, password));
#elif NETFRAMEWORK
return await Task.Run(() => RedumpWebClient.ValidateCredentials(username, password));
#else
return await RedumpHttpClient.ValidateCredentials(username, password);
#endif
}
/// <summary>
/// Reset Redumper non-redump options (Read Method, Sector Order, Drive Type)
/// </summary>
public void NonRedumpModeUnChecked()
{
Options.RedumperReadMethod = RedumperReadMethod.NONE;
Options.RedumperSectorOrder = RedumperSectorOrder.NONE;
Options.RedumperUseGenericDriveType = false;
TriggerPropertyChanged(nameof(Options));
}
#endregion
#region Property Updates
/// <summary>
/// Trigger a property changed event
/// </summary>
private void TriggerPropertyChanged(string propertyName)
{
// If the property change event is initialized
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}

View File

@@ -1,4 +1,6 @@
using System;
#if FALSE
using System;
namespace MPF.Core.Utilities
{
@@ -394,3 +396,5 @@ namespace MPF.Core.Utilities
}
}
}
#endif

View File

@@ -8,35 +8,6 @@ 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>
@@ -44,88 +15,17 @@ namespace MPF.Core.Utilities
/// <returns>True if the media has variable dumping speeds, false otherwise</returns>
public static bool DoesSupportDriveSpeed(this MediaType? type)
{
switch (type)
return type switch
{
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:
case RedumpSystem.SonyPlayStationPortable:
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.HasbroiONEducationalGamingSystem:
case RedumpSystem.HasbroVideoNow:
case RedumpSystem.HasbroVideoNowColor:
case RedumpSystem.HasbroVideoNowJr:
case RedumpSystem.HasbroVideoNowXP:
case RedumpSystem.PhilipsCDi:
case RedumpSystem.PlayStationGameSharkUpdates:
case RedumpSystem.SuperAudioCD:
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;
}
MediaType.CDROM
or MediaType.DVD
or MediaType.GDROM
or MediaType.HDDVD
or MediaType.BluRay
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => true,
_ => false,
};
}
/// <summary>
@@ -137,7 +37,7 @@ namespace MPF.Core.Utilities
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
{
if (((InternalProgram)val) == InternalProgram.NONE)
if (((InternalProgram)val!) == InternalProgram.NONE)
continue;
programs.Add($"{((InternalProgram?)val).LongName()}");

View File

@@ -14,7 +14,13 @@ namespace MPF.Core.Utilities
/// <param name="reader">TextReader representing the input</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string> handler)
#if NET20 || NET35
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#elif NET40
public static void OutputToLog(TextReader reader, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#else
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string>? handler)
#endif
{
// Initialize the required variables
char[] buffer = new char[256];
@@ -26,7 +32,15 @@ namespace MPF.Core.Utilities
while (true)
{
// Try to read the next chunk of characters
#if NET20 || NET35
read = await Task.Run(() => reader.Read(buffer, 0, buffer.Length));
#elif NET40
var readTask = Task.Factory.StartNew(() => reader.Read(buffer, 0, buffer.Length));
readTask.Wait();
read = readTask.Result;
#else
read = await reader.ReadAsync(buffer, 0, buffer.Length);
#endif
if (read == 0)
{
Thread.Sleep(10);
@@ -34,25 +48,41 @@ namespace MPF.Core.Utilities
}
// Convert the characters into a string
string line = new string(buffer, 0, read);
string line = new(buffer, 0, read);
// If we have no newline characters, store in the string builder
#if NETFRAMEWORK
if (!line.Contains("\r") && !line.Contains("\n"))
#else
if (!line.Contains('\r') && !line.Contains('\n'))
#endif
sb.Append(line);
// If we have a newline, append and log
#if NETFRAMEWORK
else if (line.Contains("\n") || line.Contains("\r\n"))
#else
else if (line.Contains('\n') || line.Contains("\r\n"))
#endif
ProcessNewLines(sb, line, baseClass, handler);
// If we have a carriage return only, append and log first and last instances
#if NETFRAMEWORK
else if (line.Contains("\r"))
#else
else if (line.Contains('\r'))
#endif
ProcessCarriageReturns(sb, line, baseClass, handler);
}
}
catch { }
finally
{
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
#else
handler?.Invoke(baseClass, sb.ToString());
#endif
}
}
@@ -63,14 +93,22 @@ namespace MPF.Core.Utilities
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
#if NET20 || NET35 || NET40
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#else
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
#endif
{
line = line.Replace("\r\n", "\n");
var split = line.Split('\n');
for (int i = 0; i < split.Length; i++)
{
// If the chunk contains a carriage return, handle it like a separate line
#if NETFRAMEWORK
if (split[i].Contains("\r"))
#else
if (split[i].Contains('\r'))
#endif
{
ProcessCarriageReturns(sb, split[i], baseClass, handler);
continue;
@@ -80,8 +118,13 @@ namespace MPF.Core.Utilities
if (i == 0)
{
sb.Append(split[i]);
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
sb = new();
#else
handler?.Invoke(baseClass, sb.ToString());
sb.Clear();
#endif
}
// For the last item, just append so it's dealt with the next time
@@ -93,7 +136,11 @@ namespace MPF.Core.Utilities
// For everything else, directly write out
else
{
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = split[i] });
#else
handler?.Invoke(baseClass, split[i]);
#endif
}
}
}
@@ -105,16 +152,25 @@ namespace MPF.Core.Utilities
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
#if NET20 || NET35 || NET40
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#else
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
#endif
{
var split = line.Split('\r');
// Append and log the first
sb.Append(split[0]);
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
sb = new();
#else
handler?.Invoke(baseClass, sb.ToString());
sb.Clear();
#endif
// Append the last
sb.Clear();
sb.Append($"\r{split[split.Length - 1]}");
}
}

View File

@@ -1,18 +1,102 @@
using System.Collections.Generic;
using System.Configuration;
using System;
using System.Collections.Generic;
using System.IO;
using MPF.Core.Converters;
using MPF.Core.Data;
using Newtonsoft.Json;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Utilities
{
public static class OptionsLoader
{
private const string ConfigurationPath = "config.json";
#region Arguments
/// <summary>
/// Process any standalone arguments for the program
/// </summary>
/// <returns>True if one of the arguments was processed, false otherwise</returns>
public static bool? ProcessStandaloneArguments(string[] args)
{
// Help options
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
return false;
// List options
if (args[0] == "-lc" || args[0] == "--listcodes")
{
Console.WriteLine("Supported Site Codes:");
foreach (string siteCode in Extensions.ListSiteCodes())
{
Console.WriteLine(siteCode);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-lm" || args[0] == "--listmedia")
{
Console.WriteLine("Supported Media Types:");
foreach (string mediaType in Extensions.ListMediaTypes())
{
Console.WriteLine(mediaType);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-lp" || args[0] == "--listprograms")
{
Console.WriteLine("Supported Programs:");
foreach (string program in EnumExtensions.ListPrograms())
{
Console.WriteLine(program);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-ls" || args[0] == "--listsystems")
{
Console.WriteLine("Supported Systems:");
foreach (string system in Extensions.ListSystems())
{
Console.WriteLine(system);
}
Console.ReadLine();
return true;
}
return false;
}
/// <summary>
/// Process common arguments for all functionality
/// </summary>
/// <returns>True if all arguments pass, false otherwise</returns>
public static (bool, MediaType, RedumpSystem?, string?) ProcessCommonArguments(string[] args)
{
// All other use requires at least 3 arguments
if (args.Length < 3)
return (false, MediaType.NONE, null, "Invalid number of arguments");
// Check the MediaType
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
if (mediaType == MediaType.NONE)
return (false, MediaType.NONE, null, $"{args[0]} is not a recognized media type");
// Check the RedumpSystem
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
if (knownSystem == null)
return (false, MediaType.NONE, null, $"{args[1]} is not a recognized system");
return (true, mediaType, knownSystem, null);
}
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
public static (Options, string, int) LoadFromArguments(string[] args, int startIndex = 0)
public static (Options, SubmissionInfo?, string?, int) LoadFromArguments(string[] args, int startIndex = 0)
{
// Create the output values with defaults
var options = new Options()
@@ -20,28 +104,45 @@ namespace MPF.Core.Utilities
RedumpUsername = null,
RedumpPassword = null,
InternalProgram = InternalProgram.NONE,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
CompressLogFiles = false,
DeleteUnnecessaryFiles = false,
};
string parsedPath = null;
// Create the submission info to return, if necessary
SubmissionInfo? info = null;
string? parsedPath = null;
// These values require multiple parts to be active
bool scan = false, protectFile = false;
bool scan = false, protectFile = false, hideDriveLetters = false;
// If we have no arguments, just return
if (args == null || args.Length == 0)
return (options, null, 0);
return (options, null, null, 0);
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
return (options, null, startIndex);
return (options, null, null, startIndex);
// Loop through the arguments and parse out values
for (; startIndex < args.Length; startIndex++)
{
// Use specific program
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
{
string internalProgram = args[startIndex].Split('=')[1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
startIndex++;
}
// Redump login
if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
{
string[] credentials = args[startIndex].Split('=')[1].Split(';');
options.RedumpUsername = credentials[0];
@@ -54,17 +155,10 @@ namespace MPF.Core.Utilities
startIndex += 2;
}
// Use specific program
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
// Pull all information (requires Redump login)
else if (args[startIndex].Equals("-a") || args[startIndex].Equals("--pull-all"))
{
string internalProgram = args[startIndex].Split('=')[1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
startIndex++;
options.PullAllInformation = true;
}
// Use a device path for physical checks
@@ -90,6 +184,31 @@ namespace MPF.Core.Utilities
protectFile = true;
}
// Hide drive letters from scan output (requires --protect-file)
else if (args[startIndex].Equals("-g") || args[startIndex].Equals("--hide-drive-letters"))
{
hideDriveLetters = true;
}
// Include seed info file
else if (args[startIndex].StartsWith("-l=") || args[startIndex].StartsWith("--load-seed="))
{
string seedInfo = args[startIndex].Split('=')[1];
info = Builder.CreateFromFile(seedInfo);
}
else if (args[startIndex] == "-l" || args[startIndex] == "--load-seed")
{
string seedInfo = args[startIndex + 1];
info = Builder.CreateFromFile(seedInfo);
startIndex++;
}
// Add filename suffix
else if (args[startIndex].Equals("-x") || args[startIndex].Equals("--suffix"))
{
options.AddFilenameSuffix = true;
}
// Output submission JSON
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
{
@@ -102,6 +221,12 @@ namespace MPF.Core.Utilities
options.CompressLogFiles = true;
}
// Delete unnecessary files files
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
{
options.DeleteUnnecessaryFiles = true;
}
// Default, we fall out
else
{
@@ -110,10 +235,11 @@ namespace MPF.Core.Utilities
}
// Now deal with the complex options
options.ScanForProtection = scan && !string.IsNullOrWhiteSpace(parsedPath);
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrWhiteSpace(parsedPath);
options.ScanForProtection = scan && !string.IsNullOrEmpty(parsedPath);
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrEmpty(parsedPath);
options.HideDriveLetters = hideDriveLetters && scan && protectFile && !string.IsNullOrEmpty(parsedPath);
return (options, parsedPath, startIndex);
return (options, info, parsedPath, startIndex);
}
/// <summary>
@@ -121,15 +247,21 @@ namespace MPF.Core.Utilities
/// </summary>
public static List<string> PrintSupportedArguments()
{
var supportedArguments = new List<string>();
supportedArguments.Add("-u, --use <program> Dumping program output type [REQUIRED]");
supportedArguments.Add("-c, --credentials <user> <pw> Redump username and password");
supportedArguments.Add("-p, --path <drivepath> Physical drive path for additional checks");
supportedArguments.Add("-s, --scan Enable copy protection scan (requires --path)");
supportedArguments.Add("-f, --protect-file Output protection to separate file (requires --scan)");
supportedArguments.Add("-j, --json Enable submission JSON output");
supportedArguments.Add("-z, --zip Enable log file compression");
var supportedArguments = new List<string>
{
"-u, --use <program> Dumping program output type [REQUIRED]",
"-c, --credentials <user> <pw> Redump username and password",
"-a, --pull-all Pull all information from Redump (requires --credentials)",
"-p, --path <drivepath> Physical drive path for additional checks",
"-s, --scan Enable copy protection scan (requires --path)",
"-f, --protect-file Output protection to separate file (requires --scan)",
"-g, --hide-drive-letters Hide drive letters from scan output (requires --protect-file)",
"-l, --load-seed <path> Load a seed submission JSON for user information",
"-x, --suffix Enable adding filename suffix",
"-j, --json Enable submission JSON output",
"-z, --zip Enable log file compression",
"-d, --delete Enable unnecessary file deletion",
};
return supportedArguments;
}
@@ -143,17 +275,17 @@ namespace MPF.Core.Utilities
/// </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)
if (!File.Exists(ConfigurationPath))
{
dict[key] = settings[key]?.Value ?? string.Empty;
_ = File.Create(ConfigurationPath);
return new Options();
}
return new Options(dict);
var serializer = JsonSerializer.Create();
var stream = File.Open(ConfigurationPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var reader = new StreamReader(stream);
var settings = serializer.Deserialize(reader, typeof(Dictionary<string, string?>)) as Dictionary<string, string?>;
return new Options(settings);
}
/// <summary>
@@ -161,16 +293,10 @@ namespace MPF.Core.Utilities
/// </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);
var serializer = JsonSerializer.Create();
var sw = new StreamWriter(ConfigurationPath) { AutoFlush = true };
var writer = new JsonTextWriter(sw) { Formatting = Formatting.Indented };
serializer.Serialize(writer, options.Settings, typeof(Dictionary<string, string>));
}
#endregion

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,46 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<AssemblyName>MPF.Library</AssemblyName>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
</Project>

View File

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

View File

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

View File

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

View File

@@ -1,37 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.Models" Version="1.1.2" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="SabreTools.Serialization" Version="1.1.2" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -1,32 +0,0 @@
using SabreTools.RedumpLib.Data;
namespace MPF.Modules.Redumper
{
public static class Converters
{
#region Cross-enumeration conversions
/// <summary>
/// Get the default extension for a given disc type
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>Valid extension (with leading '.'), null on error</returns>
public static string Extension(MediaType? type)
{
switch (type)
{
case MediaType.CDROM:
return ".bin";
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
return ".iso";
case MediaType.NONE:
default:
return null;
}
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -2,9 +2,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if NETFRAMEWORK
using IMAPI2;
#endif
using MPF.Core.Converters;
using MPF.Core.Data;
using Xunit;
@@ -18,41 +15,12 @@ namespace MPF.Test.Core.Converters
/// <summary>
/// DiscType values that map to InternalDriveType
/// </summary>
private static readonly DriveType[] _mappableDriveTypes = new DriveType[]
{
private static readonly DriveType[] _mappableDriveTypes =
[
DriveType.CDRom,
DriveType.Fixed,
DriveType.Removable,
};
#if NETFRAMEWORK
/// <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
@@ -71,63 +39,24 @@ namespace MPF.Test.Core.Converters
Assert.NotNull(actual);
}
#if NETFRAMEWORK
/// <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()
public static List<object?[]> GenerateDriveTypeMappingTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
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 });
testData.Add([driveType, false]);
else
testData.Add(new object[] { driveType, true });
testData.Add([driveType, true]);
}
return testData;
}
#if NETFRAMEWORK
/// <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
@@ -150,12 +79,12 @@ namespace MPF.Test.Core.Converters
/// Generate a test set of InternalProgram values
/// </summary>
/// <returns>MemberData-compatible list of InternalProgram values</returns>
public static List<object[]> GenerateInternalProgramTestData()
public static List<object?[]> GenerateInternalProgramTestData()
{
var testData = new List<object[]>() { new object[] { null } };
var testData = new List<object?[]>() { new object?[] { null } };
foreach (InternalProgram? internalProgram in Enum.GetValues(typeof(InternalProgram)))
{
testData.Add(new object[] { internalProgram });
testData.Add([internalProgram]);
}
return testData;

View File

@@ -1,115 +0,0 @@
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);
Assert.True(xgdInfo.Initialized);
Assert.Equal(publisher, xgdInfo.XMID.PublisherIdentifier);
Assert.Equal(gameId, xgdInfo.XMID.GameID);
Assert.Equal(version, xgdInfo.XMID.VersionNumber);
Assert.Equal(regionIdentifier, xgdInfo.XMID.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);
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);
Assert.True(xgdInfo.Initialized);
Assert.Equal(publisher, xgdInfo.XeMID.PublisherIdentifier);
Assert.Equal('2', xgdInfo.XeMID.PlatformIdentifier);
Assert.Equal(gameId, xgdInfo.XeMID.GameID);
Assert.Equal(sku, xgdInfo.XeMID.SKU);
Assert.Equal(regionIdentifier, xgdInfo.XeMID.RegionIdentifier);
Assert.Equal(baseVersion, xgdInfo.XeMID.BaseVersion);
Assert.Equal(mediaSubtype, xgdInfo.XeMID.MediaSubtypeIdentifier);
Assert.Equal(discNumber, xgdInfo.XeMID.DiscNumberIdentifier);
Assert.Equal(cert, xgdInfo.XeMID.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);
Assert.False(xgdInfo.Initialized);
}
}
}

View File

@@ -12,8 +12,8 @@ namespace MPF.Test.Core.Utilities
/// <summary>
/// MediaType values that support drive speeds
/// </summary>
private static readonly MediaType?[] _supportDriveSpeeds = new MediaType?[]
{
private static readonly MediaType?[] _supportDriveSpeeds =
[
MediaType.CDROM,
MediaType.DVD,
MediaType.GDROM,
@@ -21,13 +21,13 @@ namespace MPF.Test.Core.Utilities
MediaType.BluRay,
MediaType.NintendoGameCubeGameDisc,
MediaType.NintendoWiiOpticalDisc,
};
];
/// <summary>
/// RedumpSystem values that are considered Audio
/// </summary>
private static readonly RedumpSystem?[] _audioSystems = new RedumpSystem?[]
{
private static readonly RedumpSystem?[] _audioSystems =
[
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem,
RedumpSystem.AudioCD,
RedumpSystem.DVDAudio,
@@ -39,40 +39,41 @@ namespace MPF.Test.Core.Utilities
RedumpSystem.PlayStationGameSharkUpdates,
RedumpSystem.PhilipsCDi,
RedumpSystem.SuperAudioCD,
};
];
/// <summary>
/// RedumpSystem values that are considered markers
/// </summary>
private static readonly RedumpSystem?[] _markerSystems = new RedumpSystem?[]
{
private static readonly RedumpSystem?[] _markerSystems =
[
RedumpSystem.MarkerArcadeEnd,
RedumpSystem.MarkerComputerEnd,
RedumpSystem.MarkerDiscBasedConsoleEnd,
RedumpSystem.MarkerOtherEnd,
};
];
/// <summary>
/// RedumpSystem values that are have reversed ringcodes
/// </summary>
private static readonly RedumpSystem?[] _reverseRingcodeSystems = new RedumpSystem?[]
{
private static readonly RedumpSystem?[] _reverseRingcodeSystems =
[
RedumpSystem.SonyPlayStation2,
RedumpSystem.SonyPlayStation3,
RedumpSystem.SonyPlayStation4,
RedumpSystem.SonyPlayStation5,
RedumpSystem.SonyPlayStationPortable,
};
];
/// <summary>
/// RedumpSystem values that are considered XGD
/// </summary>
private static readonly RedumpSystem?[] _xgdSystems = new RedumpSystem?[]
{
private static readonly RedumpSystem?[] _xgdSystems =
[
RedumpSystem.MicrosoftXbox,
RedumpSystem.MicrosoftXbox360,
RedumpSystem.MicrosoftXboxOne,
RedumpSystem.MicrosoftXboxSeriesXS,
};
];
/// <summary>
/// Check that all optical media support drive speeds
@@ -143,15 +144,15 @@ namespace MPF.Test.Core.Utilities
/// 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()
public static List<object?[]> GenerateSupportDriveSpeedsTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
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 });
testData.Add([mediaType, true]);
else
testData.Add(new object[] { mediaType, false });
testData.Add([mediaType, false]);
}
return testData;
@@ -161,15 +162,15 @@ namespace MPF.Test.Core.Utilities
/// 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()
public static List<object?[]> GenerateAudioSystemsTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
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 });
testData.Add([redumpSystem, true]);
else
testData.Add(new object[] { redumpSystem, false });
testData.Add([redumpSystem, false]);
}
return testData;
@@ -179,15 +180,15 @@ namespace MPF.Test.Core.Utilities
/// 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()
public static List<object?[]> GenerateMarkerSystemsTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
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 });
testData.Add([redumpSystem, true]);
else
testData.Add(new object[] { redumpSystem, false });
testData.Add([redumpSystem, false]);
}
return testData;
@@ -197,15 +198,15 @@ namespace MPF.Test.Core.Utilities
/// 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()
public static List<object?[]> GenerateReversedRingcodeSystemsTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
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 });
testData.Add([redumpSystem, true]);
else
testData.Add(new object[] { redumpSystem, false });
testData.Add([redumpSystem, false]);
}
return testData;
@@ -215,15 +216,15 @@ namespace MPF.Test.Core.Utilities
/// 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()
public static List<object?[]> GenerateXGDSystemsTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
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 });
testData.Add([redumpSystem, true]);
else
testData.Add(new object[] { redumpSystem, false });
testData.Add([redumpSystem, false]);
}
return testData;

View File

@@ -1,6 +1,5 @@
using System.IO;
using MPF.Core;
using MPF.Core.Data;
using MPF.Library;
using SabreTools.RedumpLib.Data;
using Xunit;
@@ -15,7 +14,7 @@ namespace MPF.Test.Library
[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)
public void ParametersValidTest(string? parameters, char letter, bool isFloppy, MediaType? mediaType, bool expected)
{
var options = new Options() { InternalProgram = InternalProgram.DiscImageCreator };

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.IO;
using MPF.Library;
using MPF.Core;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using Xunit;
@@ -9,48 +10,7 @@ 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)
{
// TODO: Add tests around BDU
string actual = InfoTool.GetFixedMediaType(mediaType, null, size, layerbreak, layerbreak2, layerbreak3);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(null, null)]
[InlineData(null, "")]
[InlineData(" ", " ")]
[InlineData("super\\blah.bin", "super\\blah.bin")]
[InlineData("super\\hero\\blah.bin", "super\\hero\\blah.bin")]
@@ -58,12 +18,12 @@ namespace MPF.Test.Library
[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 outputPath, string expectedPath)
public void NormalizeOutputPathsTest(string? outputPath, string? expectedPath)
{
if (!string.IsNullOrWhiteSpace(expectedPath))
if (!string.IsNullOrEmpty(expectedPath))
expectedPath = Path.GetFullPath(expectedPath);
string actualPath = InfoTool.NormalizeOutputPaths(outputPath);
string actualPath = InfoTool.NormalizeOutputPaths(outputPath, true);
Assert.Equal(expectedPath, actualPath);
}
@@ -71,26 +31,18 @@ namespace MPF.Test.Library
public void ProcessSpecialFieldsCompleteTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = "This is a comments line\n[T:ISBN] ISBN Value",
#if NET48
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
CommentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
},
Contents = "This is a contents line\n[T:GF] Game Footage",
#if NET48
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
ContentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.Patches] = "1.04 patch",
},
@@ -98,7 +50,7 @@ namespace MPF.Test.Library
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
Formatter.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
@@ -119,13 +71,13 @@ namespace MPF.Test.Library
public void ProcessSpecialFieldsNullObjectTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
var info = new SubmissionInfo()
{
CommonDiscInfo = null,
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
Formatter.ProcessSpecialFields(info);
// Validate
Assert.Null(info.CommonDiscInfo);
@@ -135,26 +87,18 @@ namespace MPF.Test.Library
public void ProcessSpecialFieldsNullCommentsContentsTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = null,
#if NET48
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
CommentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
},
Contents = null,
#if NET48
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
ContentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.Patches] = "1.04 patch",
},
@@ -162,7 +106,7 @@ namespace MPF.Test.Library
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
Formatter.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
@@ -183,7 +127,7 @@ namespace MPF.Test.Library
public void ProcessSpecialFieldsNullDictionariesTest()
{
// Create a new SubmissionInfo object
SubmissionInfo info = new SubmissionInfo()
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
@@ -196,7 +140,7 @@ namespace MPF.Test.Library
};
// Process the special fields
InfoTool.ProcessSpecialFields(info);
Formatter.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);

View File

@@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Library;
using MPF.Core;
using Xunit;
namespace MPF.Test.Library
@@ -11,11 +11,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsActiveMARKTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"ActiveMARK",
"ActiveMARK 5",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("ActiveMARK 5", sanitized);
@@ -24,11 +24,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsCactusDataShieldTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"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);
@@ -37,11 +37,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsCDCheckTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"Anything Else Protection",
"Executable-Based CD Check",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
@@ -50,11 +50,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsCDCopsTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"CD-Cops",
"CD-Cops v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("CD-Cops v1.2.0", sanitized);
@@ -63,11 +63,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsCDKeyTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"Anything Else Protection",
"CD-Key / Serial",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
@@ -76,11 +76,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsEACdKeyTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"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);
@@ -89,11 +89,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsEADRMTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"EA DRM Protection",
"EA DRM Protection v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("EA DRM Protection v1.2.0", sanitized);
@@ -102,11 +102,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsGFWLTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"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);
@@ -115,11 +115,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsGFWLZDPPTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"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);
@@ -128,11 +128,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsImpulseReactorTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"Impulse Reactor",
"Impulse Reactor Core Module v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Impulse Reactor Core Module v1.2.0", sanitized);
@@ -145,14 +145,14 @@ namespace MPF.Test.Library
[InlineData(3)]
public void SanitizeFoundProtectionsJoWoodXProtTest(int skip)
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"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)
@@ -168,11 +168,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsOnlineRegistrationTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"Anything Else Protection",
"Executable-Based Online Registration",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
@@ -185,14 +185,14 @@ namespace MPF.Test.Library
[InlineData(3)]
public void SanitizeFoundProtectionStarForceTest(int skip)
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"StarForce 1.20.000.000",
"StarForce 5 [Protected Module]",
"StarForce 5",
"StarForce 3-5",
"StarForce",
};
];
// Safeguard for the future
if (skip >= protections.Count)
@@ -208,11 +208,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsSysiphusTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"Sysiphus",
"Sysiphus v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Sysiphus v1.2.0", sanitized);
@@ -221,11 +221,11 @@ namespace MPF.Test.Library
[Fact]
public void SanitizeFoundProtectionsXCPTest()
{
List<string> protections = new List<string>()
{
List<string> protections =
[
"XCP",
"XCP v1.2.0",
};
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("XCP v1.2.0", sanitized);

View File

@@ -1,54 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
<IsPackable>false</IsPackable>
<TargetFrameworks>net8.0</TargetFrameworks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</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>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF\MPF.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="17.6.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Include="Microsoft.CodeCoverage" Version="17.10.0-release-24177-07" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-release-24177-07" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="1.2.0" />
<PackageReference Include="xunit.assert" Version="2.5.0" />
<PackageReference Include="xunit.core" Version="2.5.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.5.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.5.0" />
<PackageReference Include="xunit.runner.console" Version="2.5.0">
<PackageReference Include="xunit.analyzers" Version="1.13.0" />
<PackageReference Include="xunit.assert" Version="2.8.0" />
<PackageReference Include="xunit.core" Version="2.8.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.8.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.8.0" />
<PackageReference Include="xunit.runner.console" Version="2.8.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using MPF.Core.Data;
using MPF.Modules.DiscImageCreator;
using MPF.Core.Modules.DiscImageCreator;
using SabreTools.RedumpLib.Data;
using Xunit;
@@ -17,10 +18,10 @@ namespace MPF.Test.Modules
[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)
public void ParametersFromSystemAndTypeTest(RedumpSystem? knownSystem, MediaType? mediaType, string? expected)
{
var options = new Options();
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
Assert.Equal(expected, actual.BaseCommand);
}
@@ -28,12 +29,12 @@ namespace MPF.Test.Modules
[InlineData(RedumpSystem.AppleMacintosh, MediaType.LaserDisc, null)] // Deliberately unsupported
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, new string[] { FlagStrings.Raw })]
public void ParametersFromOptionsSpecialDefaultTest(RedumpSystem? knownSystem, MediaType? mediaType,string[] expected)
public void ParametersFromOptionsSpecialDefaultTest(RedumpSystem? knownSystem, MediaType? mediaType, string[]? expected)
{
var options = new Options();
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
}
@@ -44,9 +45,9 @@ namespace MPF.Test.Modules
public void ParametersFromOptionsC2RereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadC2, string[] expected)
{
var options = new Options { DICRereadCount = rereadC2 };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
@@ -64,9 +65,9 @@ namespace MPF.Test.Modules
public void ParametersFromOptionsDVDRereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadDVDBD, string[] expected)
{
var options = new Options { DICDVDRereadCount = rereadDVDBD };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
@@ -88,9 +89,9 @@ namespace MPF.Test.Modules
public void ParametersFromOptionsMultiSectorReadTest(RedumpSystem? knownSystem, MediaType? mediaType, bool multiSectorRead, string[] expected)
{
var options = new Options { DICMultiSectorRead = multiSectorRead };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (expectedSet.Count != 1 && multiSectorRead)
@@ -109,9 +110,9 @@ namespace MPF.Test.Modules
public void ParametersFromOptionsParanoidModeTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoidMode, string[] expected)
{
var options = new Options { DICParanoidMode = paranoidMode };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (paranoidMode)
@@ -146,7 +147,7 @@ namespace MPF.Test.Modules
[InlineData("bd D longer\\path_test.iso 16", true)]
[InlineData("stop D", true)]
[InlineData("ls", false)]
public void ValidateParametersTest(string parameters, bool expected)
public void ValidateParametersTest(string? parameters, bool expected)
{
var actual = new Parameters(parameters);
Assert.Equal(expected, actual.IsValid());
@@ -160,9 +161,9 @@ namespace MPF.Test.Modules
[InlineData(MediaType.FloppyDisk, ".img")]
[InlineData(MediaType.Cassette, ".wav")]
[InlineData(MediaType.NONE, null)]
public void MediaTypeToExtensionTest(MediaType? mediaType, string expected)
public void MediaTypeToExtensionTest(MediaType? mediaType, string? expected)
{
string actual = Converters.Extension(mediaType);
var actual = Converters.Extension(mediaType);
Assert.Equal(expected, actual);
}
@@ -226,7 +227,7 @@ namespace MPF.Test.Modules
Assert.NotNull(parametersObject);
// Validate that the same set of parameters are generated on the output
string newParameters = parametersObject.GenerateParameters();
var newParameters = parametersObject.GenerateParameters();
Assert.NotNull(newParameters);
Assert.Equal(originalParameters, newParameters);
}
@@ -241,7 +242,7 @@ namespace MPF.Test.Modules
Assert.NotNull(parametersObject);
// Validate that the same set of parameters are generated on the output
string newParameters = parametersObject.GenerateParameters();
var newParameters = parametersObject.GenerateParameters();
Assert.NotNull(newParameters);
Assert.Equal(originalParameters, newParameters);
}
@@ -253,7 +254,7 @@ namespace MPF.Test.Modules
/// <returns>HashSet representing the strings</returns>
private static HashSet<string> GenerateUsedKeys(Parameters parameters)
{
HashSet<string> usedKeys = new HashSet<string>();
var usedKeys = new HashSet<string>();
if (parameters?.Keys == null)
return usedKeys;

View File

@@ -95,15 +95,15 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of DiscType values
/// </summary>
/// <returns>MemberData-compatible list of DiscType values</returns>
public static List<object[]> GenerateDiscTypeMappingTestData()
public static List<object?[]> GenerateDiscTypeMappingTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
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 });
testData.Add(new object?[] { discType, false });
else
testData.Add(new object[] { discType, true });
testData.Add(new object?[] { discType, true });
}
return testData;
@@ -113,12 +113,12 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of RedumpSystem values
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
public static List<object[]> GenerateRedumpSystemMappingTestData()
public static List<object?[]> GenerateRedumpSystemMappingTestData()
{
var testData = new List<object[]>() { new object[] { null } };
var testData = new List<object?[]>() { new object?[] { null } };
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
testData.Add(new object[] { redumpSystem });
testData.Add(new object?[] { redumpSystem });
}
return testData;
@@ -128,16 +128,16 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of mappable media types
/// </summary>
/// <returns>MemberData-compatible list of MediaTypes</returns>
public static List<object[]> GenerateMediaTypeMappingTestData()
public static List<object?[]> GenerateMediaTypeMappingTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
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 });
testData.Add(new object?[] { mediaType, false });
else
testData.Add(new object[] { mediaType, true });
testData.Add(new object?[] { mediaType, true });
}
return testData;
@@ -156,7 +156,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateDiscCategoryTestData))]
public void DiscCategoryLongNameTest(DiscCategory? discCategory, bool expectNull)
{
string actual = discCategory.LongName();
var actual = discCategory.LongName();
if (expectNull)
Assert.Null(actual);
@@ -168,12 +168,12 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of DiscCategory values
/// </summary>
/// <returns>MemberData-compatible list of DiscCategory values</returns>
public static List<object[]> GenerateDiscCategoryTestData()
public static List<object?[]> GenerateDiscCategoryTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
var testData = new List<object?[]>() { new object?[] { null, true } };
foreach (DiscCategory? discCategory in Enum.GetValues(typeof(DiscCategory)))
{
testData.Add(new object[] { discCategory, false });
testData.Add(new object?[] { discCategory, false });
}
return testData;
@@ -192,7 +192,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateDiscTypeTestData))]
public void DiscTypeLongNameTest(DiscType? discType, bool expectNull)
{
string actual = discType.LongName();
var actual = discType.LongName();
if (expectNull)
Assert.Null(actual);
@@ -204,15 +204,15 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of DiscType values
/// </summary>
/// <returns>MemberData-compatible list of DiscType values</returns>
public static List<object[]> GenerateDiscTypeTestData()
public static List<object?[]> GenerateDiscTypeTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
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 });
testData.Add(new object?[] { discType, true });
else
testData.Add(new object[] { discType, false });
testData.Add(new object?[] { discType, false });
}
return testData;
@@ -231,7 +231,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateLanguageTestData))]
public void LanguageLongNameTest(Language? language, bool expectNull)
{
string actual = language.LongName();
var actual = language.LongName();
if (expectNull)
Assert.Null(actual);
@@ -248,7 +248,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateLanguageTestData))]
public void LanguageShortNameTest(Language? language, bool expectNull)
{
string actual = language.ShortName();
var actual = language.ShortName();
if (expectNull)
Assert.Null(actual);
@@ -268,8 +268,8 @@ namespace MPF.Test.RedumpLib
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
string code = language.TwoLetterCode();
if (string.IsNullOrWhiteSpace(code))
var code = language.TwoLetterCode();
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
@@ -295,8 +295,8 @@ namespace MPF.Test.RedumpLib
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
string code = language.ThreeLetterCode();
if (string.IsNullOrWhiteSpace(code))
var code = language.ThreeLetterCode();
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
@@ -322,8 +322,8 @@ namespace MPF.Test.RedumpLib
int totalCount = 0;
foreach (Language? language in fullLanguages)
{
string code = language.ThreeLetterCodeAlt();
if (string.IsNullOrWhiteSpace(code))
var code = language.ThreeLetterCodeAlt();
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
@@ -341,12 +341,12 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of Language values
/// </summary>
/// <returns>MemberData-compatible list of Language values</returns>
public static List<object[]> GenerateLanguageTestData()
public static List<object?[]> GenerateLanguageTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
var testData = new List<object?[]>() { new object?[] { null, true } };
foreach (Language? language in Enum.GetValues(typeof(Language)))
{
testData.Add(new object[] { language, false });
testData.Add(new object?[] { language, false });
}
return testData;
@@ -365,7 +365,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateLanguageSelectionTestData))]
public void LanguageSelectionLongNameTest(LanguageSelection? languageSelection, bool expectNull)
{
string actual = languageSelection.LongName();
var actual = languageSelection.LongName();
if (expectNull)
Assert.Null(actual);
@@ -377,12 +377,12 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of LanguageSelection values
/// </summary>
/// <returns>MemberData-compatible list of LanguageSelection values</returns>
public static List<object[]> GenerateLanguageSelectionTestData()
public static List<object?[]> GenerateLanguageSelectionTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
var testData = new List<object?[]>() { new object?[] { null, true } };
foreach (LanguageSelection? languageSelection in Enum.GetValues(typeof(LanguageSelection)))
{
testData.Add(new object[] { languageSelection, false });
testData.Add(new object?[] { languageSelection, false });
}
return testData;
@@ -401,7 +401,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateMediaTypeTestData))]
public void MediaTypeLongNameTest(MediaType? mediaType, bool expectNull)
{
string actual = mediaType.LongName();
var actual = mediaType.LongName();
if (expectNull)
Assert.Null(actual);
@@ -418,7 +418,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateMediaTypeTestData))]
public void MediaTypeShortNameTest(MediaType? mediaType, bool expectNull)
{
string actual = mediaType.ShortName();
var actual = mediaType.ShortName();
if (expectNull)
Assert.Null(actual);
@@ -430,12 +430,12 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of MediaType values
/// </summary>
/// <returns>MemberData-compatible list of MediaType values</returns>
public static List<object[]> GenerateMediaTypeTestData()
public static List<object?[]> GenerateMediaTypeTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
var testData = new List<object?[]>() { new object?[] { null, true } };
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
{
testData.Add(new object[] { mediaType, false });
testData.Add(new object?[] { mediaType, false });
}
return testData;
@@ -454,7 +454,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateRegionTestData))]
public void RegionLongNameTest(Region? region, bool expectNull)
{
string actual = region.LongName();
var actual = region.LongName();
if (expectNull)
Assert.Null(actual);
@@ -471,7 +471,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateRegionTestData))]
public void RegionShortNameTest(Region? region, bool expectNull)
{
string actual = region.ShortName();
var actual = region.ShortName();
if (expectNull)
Assert.Null(actual);
@@ -491,8 +491,8 @@ namespace MPF.Test.RedumpLib
int totalCount = 0;
foreach (Region? region in fullRegions)
{
string code = region.ShortName();
if (string.IsNullOrWhiteSpace(code))
var code = region.ShortName();
if (string.IsNullOrEmpty(code))
continue;
// Throw if the code already exists
@@ -510,12 +510,12 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of Region values
/// </summary>
/// <returns>MemberData-compatible list of Region values</returns>
public static List<object[]> GenerateRegionTestData()
public static List<object?[]> GenerateRegionTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
var testData = new List<object?[]>() { new object?[] { null, true } };
foreach (Region? region in Enum.GetValues(typeof(Region)))
{
testData.Add(new object[] { region, false });
testData.Add(new object?[] { region, false });
}
return testData;
@@ -534,7 +534,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateSiteCodeTestData))]
public void SiteCodeLongNameTest(SiteCode? siteCode, bool expectNull)
{
string actual = siteCode.LongName();
var actual = siteCode.LongName();
if (expectNull)
Assert.Null(actual);
@@ -551,7 +551,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateSiteCodeTestData))]
public void SiteCodeShortNameTest(SiteCode? siteCode, bool expectNull)
{
string actual = siteCode.ShortName();
var actual = siteCode.ShortName();
if (expectNull)
Assert.Null(actual);
@@ -563,12 +563,12 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of SiteCode values
/// </summary>
/// <returns>MemberData-compatible list of SiteCode values</returns>
public static List<object[]> GenerateSiteCodeTestData()
public static List<object?[]> GenerateSiteCodeTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
var testData = new List<object?[]>() { new object?[] { null, true } };
foreach (SiteCode? siteCode in Enum.GetValues(typeof(SiteCode)))
{
testData.Add(new object[] { siteCode, false });
testData.Add(new object?[] { siteCode, false });
}
return testData;
@@ -587,7 +587,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateRedumpSystemTestData))]
public void RedumpSystemLongNameTest(RedumpSystem? redumpSystem, bool expectNull)
{
string actual = redumpSystem.LongName();
var actual = redumpSystem.LongName();
if (expectNull)
Assert.Null(actual);
@@ -622,16 +622,16 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of RedumpSystem values
/// </summary>
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
public static List<object[]> GenerateRedumpSystemTestData()
public static List<object?[]> GenerateRedumpSystemTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
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 (redumpSystem.IsMarker())
continue;
testData.Add(new object[] { redumpSystem, false });
testData.Add(new object?[] { redumpSystem, false });
}
return testData;
@@ -650,7 +650,7 @@ namespace MPF.Test.RedumpLib
[MemberData(nameof(GenerateSystemCategoryTestData))]
public void SystemCategoryLongNameTest(SystemCategory? systemCategory, bool expectNull)
{
string actual = systemCategory.LongName();
var actual = systemCategory.LongName();
if (expectNull)
Assert.Null(actual);
@@ -662,15 +662,15 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of SystemCategory values
/// </summary>
/// <returns>MemberData-compatible list of SystemCategory values</returns>
public static List<object[]> GenerateSystemCategoryTestData()
public static List<object?[]> GenerateSystemCategoryTestData()
{
var testData = new List<object[]>() { new object[] { null, true } };
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 });
testData.Add(new object?[] { systemCategory, true });
else
testData.Add(new object[] { systemCategory, false });
testData.Add(new object?[] { systemCategory, false });
}
return testData;
@@ -701,12 +701,12 @@ namespace MPF.Test.RedumpLib
/// Generate a test set of YesNo values
/// </summary>
/// <returns>MemberData-compatible list of YesNo values</returns>
public static List<object[]> GenerateYesNoTestData()
public static List<object?[]> GenerateYesNoTestData()
{
var testData = new List<object[]>() { new object[] { null, false } };
var testData = new List<object?[]>() { new object?[] { null, false } };
foreach (YesNo? yesNo in Enum.GetValues(typeof(YesNo)))
{
testData.Add(new object[] { yesNo, false });
testData.Add(new object?[] { yesNo, false });
}
return testData;

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using psxt001z;
using SabreTools.RedumpLib.Data;
using Xunit;
@@ -82,20 +81,12 @@ namespace MPF.Test.RedumpLib
EXEDateBuildDate = "19xx-xx-xx",
ErrorsCount = "0",
Comments = "Comment data line 1\r\nComment data line 2",
#if NET48
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
CommentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.ISBN] = "ISBN",
},
Contents = "Special contents 1\r\nSpecial contents 2",
#if NET48
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
ContentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.PlayableDemos] = "Game Demo 1",
},

View File

@@ -1,5 +1,4 @@
using System.Linq;
using MPF.Core.Data;
using MPF.Core.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
@@ -16,7 +15,7 @@ namespace MPF.Test.Data
public void GetAllowedDriveSpeedForMediaTypeTest(MediaType? mediaType, int maxExpected)
{
var actual = Interface.GetSpeedsForMediaType(mediaType);
Assert.Equal(maxExpected, actual.Last());
Assert.Equal(maxExpected, actual[actual.Count - 1]);
}
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using static MPF.Core.Data.Interface;
namespace MPF.UI.Core
{
@@ -10,18 +11,17 @@ namespace MPF.UI.Core
/// </summary>
public static class Constants
{
// Private lists of known drive speed ranges
private static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
private static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
private static IReadOnlyList<int> HDDVD { 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 SpeedsForHDDVDAsCollection { get; } = GetDoubleCollectionFromIntList(HDDVD);
public static DoubleCollection SpeedsForBDAsCollection { get; } = GetDoubleCollectionFromIntList(BD);
#if NET20 || NET35 || NET40
private static DoubleCollection GetDoubleCollectionFromIntList(IList<int> list)
#else
private static DoubleCollection GetDoubleCollectionFromIntList(IReadOnlyList<int> list)
=> new DoubleCollection(list.Select(i => Convert.ToDouble(i)).ToList());
#endif
=> new(list.Select(i => Convert.ToDouble(i)).ToList());
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Globalization;
using System.Windows.Data;
using MPF.Core.Data;
using MPF.Core.UI.ComboBoxItems;
using SabreTools.RedumpLib.Data;
namespace MPF.UI.Core
{
internal class ElementConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value switch
{
DiscCategory discCategory => new Element<DiscCategory>(discCategory),
InternalProgram internalProgram => new Element<InternalProgram>(internalProgram),
MediaType mediaType => new Element<MediaType>(mediaType),
RedumperReadMethod readMethod => new Element<RedumperReadMethod>(readMethod),
RedumperSectorOrder sectorOrder => new Element<RedumperSectorOrder>(sectorOrder),
RedumpSystem redumpSystem => new RedumpSystemComboBoxItem(redumpSystem),
Region region => new Element<Region>(region),
// Null values are treated as a system value
_ => new RedumpSystemComboBoxItem((RedumpSystem?)null),
};
}
public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// If it's an IElement but ends up null
if (value is not IElement element)
return null;
return element switch
{
Element<DiscCategory> dcElement => dcElement.Value,
Element<InternalProgram> ipElement => ipElement.Value,
Element<MediaType> mtElement => mtElement.Value,
Element<RedumperReadMethod> rmElement => rmElement.Value,
Element<RedumperSectorOrder> soElement => soElement.Value,
RedumpSystemComboBoxItem rsElement => rsElement.Value,
Element<Region> reValue => reValue.Value,
_ => null,
};
}
}
}

View File

@@ -18,7 +18,7 @@ namespace WPFCustomMessageBox
/// Global parameter to enable (true) or disable (false) the removal of the title bar icon.
/// If you are using a custom window style, the icon removal may cause issues like displaying two title bar (the default windows one and the custom one).
/// </summary>
public static bool RemoveTitleBarIcon = true;
private static readonly bool RemoveTitleBarIcon = true;
/// <summary>
/// Displays a message box that has a message and returns a result.
@@ -173,7 +173,7 @@ namespace WPFCustomMessageBox
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
public static MessageBoxResult Show(Window owner, string? messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
@@ -515,11 +515,11 @@ namespace WPFCustomMessageBox
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
private static MessageBoxResult ShowOKMessage(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
private static MessageBoxResult ShowOKMessage(Window? owner, string? messageBoxText, string caption, string? okButtonText, string? cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.OK : MessageBoxButton.OKCancel;
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
var msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
if (!string.IsNullOrEmpty(okButtonText))
msg.OkButtonText = okButtonText;
if (!string.IsNullOrEmpty(cancelButtonText))
@@ -545,11 +545,11 @@ namespace WPFCustomMessageBox
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
private static MessageBoxResult ShowYesNoMessage(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
private static MessageBoxResult ShowYesNoMessage(Window? owner, string? messageBoxText, string caption, string? yesButtonText, string? noButtonText, string? cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.YesNo : MessageBoxButton.YesNoCancel;
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
var msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
if (!string.IsNullOrEmpty(yesButtonText))
msg.YesButtonText = yesButtonText;
if (!string.IsNullOrEmpty(noButtonText))
@@ -566,14 +566,14 @@ namespace WPFCustomMessageBox
{
if (timeout.HasValue && timeout.Value <= 0)
{
throw new ArgumentOutOfRangeException("timeout", string.Format("Timeout must be greater than 0."));
throw new ArgumentOutOfRangeException(nameof(timeout), string.Format("Timeout must be greater than 0."));
}
if (timeout.HasValue)
{
//System.Threading.Timer timer = null;
//timer = new System.Threading.Timer(s => { msg.Close(); timer.Dispose(); }, null, timeout.Value, System.Threading.Timeout.Infinite);
System.Timers.Timer timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
var timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
timer.Elapsed += delegate {
Application.Current.Dispatcher.Invoke(new Action(() =>
{

View File

@@ -4,16 +4,10 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
ShowInTaskbar="False"
ResizeMode="NoResize" SizeToContent="WidthAndHeight"
TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType" UseLayoutRounding="True"
Title="" MinHeight="155" MaxWidth="470" MinWidth="154"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />

View File

@@ -1,7 +1,11 @@
using System;
using System.Drawing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using MPF.UI.Core;
#pragma warning disable IDE1006 // Naming Styles
namespace WPFCustomMessageBox
{
@@ -10,9 +14,9 @@ namespace WPFCustomMessageBox
/// </summary>
internal partial class CustomMessageBoxWindow : Window
{
private bool _removeTitleBarIcon = true;
private readonly bool _removeTitleBarIcon = true;
public string Caption
public string? Caption
{
get
{
@@ -24,75 +28,150 @@ namespace WPFCustomMessageBox
}
}
public string Message
public string? Message
{
get
{
#if NET35
return _TextBlock_Message!.Text;
#else
return TextBlock_Message.Text;
#endif
}
set
{
#if NET35
_TextBlock_Message!.Text = value;
#else
TextBlock_Message.Text = value;
#endif
}
}
public string OkButtonText
public string? OkButtonText
{
get
{
#if NET35
return _Label_Ok!.Content.ToString();
#else
return Label_Ok.Content.ToString();
#endif
}
set
{
#if NET35
_Label_Ok!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Ok.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
public string CancelButtonText
public string? CancelButtonText
{
get
{
#if NET35
return _Label_Cancel!.Content.ToString();
#else
return Label_Cancel.Content.ToString();
#endif
}
set
{
#if NET35
_Label_Cancel!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Cancel.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
public string YesButtonText
public string? YesButtonText
{
get
{
#if NET35
return _Label_Yes!.Content.ToString();
#else
return Label_Yes.Content.ToString();
#endif
}
set
{
#if NET35
_Label_Yes!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Yes.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
public string NoButtonText
public string? NoButtonText
{
get
{
#if NET35
return _Label_No!.Content.ToString();
#else
return Label_No.Content.ToString();
#endif
}
set
{
#if NET35
_Label_No!.Content = value.TryAddKeyboardAccellerator();
#else
Label_No.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
public MessageBoxResult Result { get; set; }
internal CustomMessageBoxWindow(Window owner, string message, string caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
#if NET35
private Button? _Button_Cancel => ItemHelper.FindChild<Button>(this, "Button_Cancel");
private Button? _Button_No => ItemHelper.FindChild<Button>(this, "Button_No");
private Button? _Button_OK => ItemHelper.FindChild<Button>(this, "Button_OK");
private Button? _Button_Yes => ItemHelper.FindChild<Button>(this, "Button_Yes");
private System.Windows.Controls.Image? _Image_MessageBox => ItemHelper.FindChild<System.Windows.Controls.Image>(this, "Image_MessageBox");
private Label? _Label_Cancel => ItemHelper.FindChild<Label>(this, "Label_Cancel");
private Label? _Label_No => ItemHelper.FindChild<Label>(this, "Label_No");
private Label? _Label_Ok => ItemHelper.FindChild<Label>(this, "Label_Ok");
private Label? _Label_Yes => ItemHelper.FindChild<Label>(this, "Label_Yes");
private TextBlock? _TextBlock_Message => ItemHelper.FindChild<TextBlock>(this, "TextBlock_Message");
#endif
internal CustomMessageBoxWindow(Window? owner, string? message, string? caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
{
#if NET40_OR_GREATER || NETCOREAPP
System.Windows.Media.TextOptions.SetTextFormattingMode(this, System.Windows.Media.TextFormattingMode.Display);
System.Windows.Media.TextOptions.SetTextRenderingMode(this, System.Windows.Media.TextRenderingMode.ClearType);
UseLayoutRounding = true;
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
_removeTitleBarIcon = removeTitleBarIcon;
Focusable = true;
ShowActivated = true;
ShowInTaskbar = true;
if (owner != null)
if (owner != null && owner.IsLoaded)
{
Owner = owner;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
@@ -106,7 +185,11 @@ namespace WPFCustomMessageBox
if (image.HasValue)
DisplayImage(image.Value);
else
Image_MessageBox.Visibility = System.Windows.Visibility.Collapsed;
#if NET35
_Image_MessageBox!.Visibility = Visibility.Collapsed;
#else
Image_MessageBox.Visibility = Visibility.Collapsed;
#endif
}
protected override void OnSourceInitialized(EventArgs e)
@@ -123,39 +206,75 @@ namespace WPFCustomMessageBox
{
case MessageBoxButton.OKCancel:
// Hide all but OK, Cancel
Button_OK.Visibility = System.Windows.Visibility.Visible;
Button_OK.Focus();
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
#if NET35
_Button_OK!.Visibility = Visibility.Visible;
_Button_OK.Focus();
_Button_Cancel!.Visibility = Visibility.Visible;
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
Button_No.Visibility = System.Windows.Visibility.Collapsed;
_Button_Yes!.Visibility = Visibility.Collapsed;
_Button_No!.Visibility = Visibility.Collapsed;
#else
Button_OK.Visibility = Visibility.Visible;
Button_OK.Focus();
Button_Cancel.Visibility = Visibility.Visible;
Button_Yes.Visibility = Visibility.Collapsed;
Button_No.Visibility = Visibility.Collapsed;
#endif
break;
case MessageBoxButton.YesNo:
// Hide all but Yes, No
Button_Yes.Visibility = System.Windows.Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = System.Windows.Visibility.Visible;
#if NET35
_Button_Yes!.Visibility = Visibility.Visible;
_Button_Yes.Focus();
_Button_No!.Visibility = Visibility.Visible;
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
_Button_OK!.Visibility = Visibility.Collapsed;
_Button_Cancel!.Visibility = Visibility.Collapsed;
#else
Button_Yes.Visibility = Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = Visibility.Visible;
Button_OK.Visibility = Visibility.Collapsed;
Button_Cancel.Visibility = Visibility.Collapsed;
#endif
break;
case MessageBoxButton.YesNoCancel:
// Hide only OK
Button_Yes.Visibility = System.Windows.Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = System.Windows.Visibility.Visible;
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
#if NET35
_Button_Yes!.Visibility = Visibility.Visible;
_Button_Yes.Focus();
_Button_No!.Visibility = Visibility.Visible;
_Button_Cancel!.Visibility = Visibility.Visible;
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
_Button_OK!.Visibility = Visibility.Collapsed;
#else
Button_Yes.Visibility = Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = Visibility.Visible;
Button_Cancel.Visibility = Visibility.Visible;
Button_OK.Visibility = Visibility.Collapsed;
#endif
break;
default:
// Hide all but OK
Button_OK.Visibility = System.Windows.Visibility.Visible;
#if NET35
_Button_OK!.Visibility = Visibility.Visible;
_Button_OK.Focus();
_Button_Yes!.Visibility = Visibility.Collapsed;
_Button_No!.Visibility = Visibility.Collapsed;
_Button_Cancel!.Visibility = Visibility.Collapsed;
#else
Button_OK.Visibility = Visibility.Visible;
Button_OK.Focus();
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
Button_No.Visibility = System.Windows.Visibility.Collapsed;
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
Button_Yes.Visibility = Visibility.Collapsed;
Button_No.Visibility = Visibility.Collapsed;
Button_Cancel.Visibility = Visibility.Collapsed;
#endif
break;
}
}
@@ -183,8 +302,13 @@ namespace WPFCustomMessageBox
break;
}
#if NET35
_Image_MessageBox!.Source = icon.ToImageSource();
_Image_MessageBox.Visibility = Visibility.Visible;
#else
Image_MessageBox.Source = icon.ToImageSource();
Image_MessageBox.Visibility = System.Windows.Visibility.Visible;
Image_MessageBox.Visibility = Visibility.Visible;
#endif
}
private void Button_OK_Click(object sender, RoutedEventArgs e)

View File

@@ -37,7 +37,7 @@ namespace WPFCustomMessageBox
const int SWP_NOMOVE = 0x0002;
const int SWP_NOZORDER = 0x0004;
const int SWP_FRAMECHANGED = 0x0020;
const uint WM_SETICON = 0x0080;
//const uint WM_SETICON = 0x0080;
internal static ImageSource ToImageSource(this Icon icon)
@@ -60,7 +60,7 @@ namespace WPFCustomMessageBox
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
internal static string TryAddKeyboardAccellerator(this string input)
internal static string? TryAddKeyboardAccellerator(this string? input)
{
if (input == null)
return input;
@@ -84,7 +84,7 @@ namespace WPFCustomMessageBox
IntPtr hwnd = new WindowInteropHelper(window).Handle;
// Change the extended window style to not show a window icon
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
_ = SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
// Update the window's non-client area to reflect the changes
SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}

124
MPF.UI.Core/ItemHelper.cs Normal file
View File

@@ -0,0 +1,124 @@
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
namespace MPF.UI.Core
{
internal static class ItemHelper
{
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T? FindChild<T>(DependencyObject? parent, string childName) where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T? foundChild = null;
if (parent is ItemsControl itemsControl && itemsControl.Items != null)
{
int childrenCount = itemsControl.Items.Count;
for (int i = 0; i < childrenCount; i++)
{
var child = itemsControl.Items[i] as DependencyObject;
// If the child is not of the request child type child
if (child is not T)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null)
break;
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
}
else if (parent is ContentControl contentControl && contentControl.Content != null)
{
var child = contentControl.Content as DependencyObject;
// If the child is not of the request child type child
if (child is not T)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
}
}
else
{
// child element found.
foundChild = (T)child;
}
}
else if (parent is Visual)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
if (child is not T)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null)
break;
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
}
return foundChild;
}
}
}

View File

@@ -1,24 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64</RuntimeIdentifiers>
<!-- Assembly Properties -->
<TargetFrameworks>net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0-windows;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<ImportFrameworkWinFXTargets Condition="$(TargetFramework.StartsWith(`net3`))">true</ImportFrameworkWinFXTargets>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<Title>MPF.UI.Core</Title>
<AssemblyName>MPF.UI.Core</AssemblyName>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2023</Copyright>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
<VersionPrefix>3.1.9</VersionPrefix>
<PropertyGroup>
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
<UserSecretsId>27abb4ca-bf7a-431e-932f-49153303d5ff</UserSecretsId>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Common code for all MPF UI implementations</Description>
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>
@@ -28,23 +32,18 @@
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
</ItemGroup>
<!-- Support for old .NET versions -->
<ItemGroup Condition="$(TargetFramework.StartsWith(`net3`))">
<Reference Include="PresentationBuildTasks" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationBuildTasks.dll" />
<Reference Include="PresentationCore" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll" />
<Reference Include="PresentationFramework" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll" />
<Reference Include="WindowsBase" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll" />
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationFramework.Aero" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Images\" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
</ItemGroup>
<ItemGroup>

View File

@@ -6,39 +6,39 @@ namespace MPF.UI.Core
/// <summary>
/// Represents all required mapping values for the UI
/// </summary>
public class Theme
public abstract class Theme
{
#region Application-Wide
/// <summary>
/// SolidColorBrush used to paint the active window's border.
/// </summary>
public SolidColorBrush ActiveBorderBrush { get; set; }
public SolidColorBrush? ActiveBorderBrush { get; protected set; }
/// <summary>
/// SolidColorBrush that paints the face of a three-dimensional display element.
/// </summary>
public SolidColorBrush ControlBrush { get; set; }
public SolidColorBrush? ControlBrush { get; protected set; }
/// <summary>
/// SolidColorBrush that paints text in a three-dimensional display element.
/// </summary>
public SolidColorBrush ControlTextBrush { get; set; }
public SolidColorBrush? ControlTextBrush { get; protected set; }
/// <summary>
/// SolidColorBrush that paints disabled text.
/// </summary>
public SolidColorBrush GrayTextBrush { get; set; }
public SolidColorBrush? GrayTextBrush { get; protected set; }
/// <summary>
/// SolidColorBrush that paints the background of a window's client area.
/// </summary>
public SolidColorBrush WindowBrush { get; set; }
public SolidColorBrush? WindowBrush { get; protected set; }
/// <summary>
/// SolidColorBrush that paints the text in the client area of a window.
/// </summary>
public SolidColorBrush WindowTextBrush { get; set; }
public SolidColorBrush? WindowTextBrush { get; protected set; }
#endregion
@@ -47,22 +47,22 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the Button.Disabled.Background resource
/// </summary>
public Brush Button_Disabled_Background { get; set; }
public Brush? Button_Disabled_Background { get; protected set; }
/// <summary>
/// Brush for the Button.MouseOver.Background resource
/// </summary>
public Brush Button_MouseOver_Background { get; set; }
public Brush? Button_MouseOver_Background { get; protected set; }
/// <summary>
/// Brush for the Button.Pressed.Background resource
/// </summary>
public Brush Button_Pressed_Background { get; set; }
public Brush? Button_Pressed_Background { get; protected set; }
/// <summary>
/// Brush for the Button.Static.Background resource
/// </summary>
public Brush Button_Static_Background { get; set; }
public Brush? Button_Static_Background { get; protected set; }
#endregion
@@ -71,62 +71,62 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the ComboBox.Disabled.Background resource
/// </summary>
public Brush ComboBox_Disabled_Background { get; set; }
public Brush? ComboBox_Disabled_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Disabled.Editable.Background resource
/// </summary>
public Brush ComboBox_Disabled_Editable_Background { get; set; }
public Brush? ComboBox_Disabled_Editable_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Disabled.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_Disabled_Editable_Button_Background { get; set; }
public Brush? ComboBox_Disabled_Editable_Button_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.MouseOver.Background resource
/// </summary>
public Brush ComboBox_MouseOver_Background { get; set; }
public Brush? ComboBox_MouseOver_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.MouseOver.Editable.Background resource
/// </summary>
public Brush ComboBox_MouseOver_Editable_Background { get; set; }
public Brush? ComboBox_MouseOver_Editable_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.MouseOver.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_MouseOver_Editable_Button_Background { get; set; }
public Brush? ComboBox_MouseOver_Editable_Button_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Pressed.Background resource
/// </summary>
public Brush ComboBox_Pressed_Background { get; set; }
public Brush? ComboBox_Pressed_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Pressed.Editable.Background resource
/// </summary>
public Brush ComboBox_Pressed_Editable_Background { get; set; }
public Brush? ComboBox_Pressed_Editable_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Pressed.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_Pressed_Editable_Button_Background { get; set; }
public Brush? ComboBox_Pressed_Editable_Button_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Static.Background resource
/// </summary>
public Brush ComboBox_Static_Background { get; set; }
public Brush? ComboBox_Static_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Static.Editable.Background resource
/// </summary>
public Brush ComboBox_Static_Editable_Background { get; set; }
public Brush? ComboBox_Static_Editable_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Static.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_Static_Editable_Button_Background { get; set; }
public Brush? ComboBox_Static_Editable_Button_Background { get; protected set; }
#endregion
@@ -135,7 +135,7 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the CustomMessageBox.Static.Background resource
/// </summary>
public Brush CustomMessageBox_Static_Background { get; set; }
public Brush? CustomMessageBox_Static_Background { get; protected set; }
#endregion
@@ -144,12 +144,12 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the MenuItem.SubMenu.Background resource
/// </summary>
public Brush MenuItem_SubMenu_Background { get; set; }
public Brush? MenuItem_SubMenu_Background { get; protected set; }
/// <summary>
/// Brush for the MenuItem.SubMenu.Border resource
/// </summary>
public Brush MenuItem_SubMenu_Border { get; set; }
public Brush? MenuItem_SubMenu_Border { get; protected set; }
#endregion
@@ -158,7 +158,7 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the ProgressBar.Background resource
/// </summary>
public Brush ProgressBar_Background { get; set; }
public Brush? ProgressBar_Background { get; protected set; }
#endregion
@@ -167,7 +167,7 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the ScrollViewer.ScrollBar.Background resource
/// </summary>
public Brush ScrollViewer_ScrollBar_Background { get; set; }
public Brush? ScrollViewer_ScrollBar_Background { get; protected set; }
#endregion
@@ -176,17 +176,17 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the TabItem.Selected.Background resource
/// </summary>
public Brush TabItem_Selected_Background { get; set; }
public Brush? TabItem_Selected_Background { get; protected set; }
/// <summary>
/// Brush for the TabItem.Static.Background resource
/// </summary>
public Brush TabItem_Static_Background { get; set; }
public Brush? TabItem_Static_Background { get; protected set; }
/// <summary>
/// Brush for the TabItem.Static.Border resource
/// </summary>
public Brush TabItem_Static_Border { get; set; }
public Brush? TabItem_Static_Border { get; protected set; }
#endregion
@@ -195,7 +195,7 @@ namespace MPF.UI.Core
/// <summary>
/// Brush for the TextBox.Static.Background resource
/// </summary>
public Brush TextBox_Static_Background { get; set; }
public Brush? TextBox_Static_Background { get; protected set; }
#endregion

View File

@@ -1,19 +1,241 @@
using System.Windows.Controls;
using MPF.UI.Core.ViewModels;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Threading;
using MPF.Core.Data;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.UserControls
{
public partial class LogOutput : UserControl
{
/// <summary>
/// Read-only access to the current log view model
/// Document representing the text
/// </summary>
public LogViewModel LogViewModel => DataContext as LogViewModel;
internal FlowDocument Document { get; private set; }
/// <summary>
/// Queue of items that need to be logged
/// </summary>
internal ProcessingQueue<LogLine> LogQueue { get; private set; }
/// <summary>
/// Paragraph backing the log
/// </summary>
private readonly Paragraph _paragraph;
/// <summary>
/// Cached value of the last line written
/// </summary>
private Run? lastLine = null;
#if NET35
private Button? _ClearButton => ItemHelper.FindChild<Button>(this, "ClearButton");
private RichTextBox? _Output => ItemHelper.FindChild<RichTextBox>(this, "Output");
private ScrollViewer? _OutputViewer => ItemHelper.FindChild<ScrollViewer>(this, "OutputViewer");
private Button? _SaveButton => ItemHelper.FindChild<Button>(this, "SaveButton");
#endif
public LogOutput()
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
DataContext = new LogViewModel(this);
#endif
// Update the internal state
Document = new FlowDocument()
{
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
};
_paragraph = new Paragraph();
Document.Blocks.Add(_paragraph);
// Setup the processing queue
LogQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
// Add handlers
#if NET35
_OutputViewer!.SizeChanged += OutputViewerSizeChanged;
_Output!.TextChanged += OnTextChanged;
_ClearButton!.Click += OnClearButton;
_SaveButton!.Click += OnSaveButton;
#else
OutputViewer.SizeChanged += OutputViewerSizeChanged;
Output.TextChanged += OnTextChanged;
ClearButton.Click += OnClearButton;
SaveButton.Click += OnSaveButton;
#endif
// Update the internal state
#if NET35
_Output.Document = Document;
#else
Output.Document = Document;
#endif
}
#region Logging
/// <summary>
/// Enqueue text to the log with formatting
/// </summary>
/// <param name="logLevel">LogLevel for the log</param>
/// <param name="text">Text to write to the log</param>
public void EnqueueLog(LogLevel logLevel, string text)
{
// Null text gets ignored
if (text == null)
return;
// Enqueue the text
LogQueue.Enqueue(new LogLine(text, logLevel));
}
/// <summary>
/// Log line wrapper
/// </summary>
internal readonly 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()
{
return this.LogLevel switch
{
LogLevel.SECRET => Brushes.Blue,
LogLevel.ERROR => Brushes.Red,
LogLevel.VERBOSE => Brushes.Yellow,
_ => 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>
/// Process the log lines in the queue
/// </summary>
/// <param name="nextLogLine">LogLine item to process</param>
internal void ProcessLogLine(LogLine nextLogLine)
{
// Null text gets ignored
string nextText = nextLogLine.Text;
if (nextText == null)
return;
try
{
if (nextText.StartsWith("\r"))
ReplaceLastLine(nextLogLine);
else
AppendToTextBox(nextLogLine);
}
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>
/// 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 ??= new Run();
lastLine.Text = logLine.Text;
lastLine.Foreground = logLine.GetForegroundColor();
});
}
#endregion
#region Helpers
/// <summary>
/// Clear all inlines of the paragraph
/// </summary>
private void ClearInlines() => _paragraph.Inlines.Clear();
/// <summary>
/// Save all inlines to console.log
/// </summary>
private void SaveInlines()
{
using var sw = new StreamWriter(File.OpenWrite("console.log"));
foreach (var inline in _paragraph.Inlines)
{
if (inline is Run run)
sw.Write(run.Text);
}
}
/// <summary>
/// Scroll the current view to the bottom
/// </summary>
#if NET35
public void ScrollToBottom() => _OutputViewer!.ScrollToBottom();
#else
public void ScrollToBottom() => OutputViewer.ScrollToBottom();
#endif
#endregion
#region EventHandlers
private void OnClearButton(object sender, EventArgs e)
=> ClearInlines();
private void OnSaveButton(object sender, EventArgs e)
=> SaveInlines();
private void OnTextChanged(object sender, TextChangedEventArgs e)
=> ScrollToBottom();
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e)
=> ScrollToBottom();
#endregion
}
}

View File

@@ -39,7 +39,7 @@ namespace MPF.UI.Core.UserControls
public static readonly DependencyProperty VerticalScrollBarVisibilityProperty =
DependencyProperty.Register("VerticalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(UserInput));
#endregion
#region Properties
@@ -118,7 +118,9 @@ namespace MPF.UI.Core.UserControls
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
}
}
}

View File

@@ -1,850 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.UI.Core.ComboBoxItems;
using MPF.UI.Core.Windows;
using SabreTools.RedumpLib.Data;
namespace MPF.UI.Core.ViewModels
{
public class DiscInformationViewModel
{
#region Fields
/// <summary>
/// Parent DiscInformationWindow object
/// </summary>
public DiscInformationWindow Parent { get; private set; }
/// <summary>
/// Application-level Options object
/// </summary>
public Options Options { 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 Redump-supported Regions
/// </summary>
private static readonly List<Region> RedumpRegions = new List<Region>
{
Region.Argentina,
Region.Asia,
Region.AsiaEurope,
Region.AsiaUSA,
Region.Australia,
Region.AustraliaGermany,
Region.AustraliaNewZealand,
Region.Austria,
Region.AustriaSwitzerland,
Region.Belarus,
Region.Belgium,
Region.BelgiumNetherlands,
Region.Brazil,
Region.Bulgaria,
Region.Canada,
Region.China,
Region.Croatia,
Region.Czechia,
Region.Denmark,
Region.Estonia,
Region.Europe,
Region.EuropeAsia,
Region.EuropeAustralia,
Region.EuropeCanada,
Region.EuropeGermany,
Region.Export,
Region.Finland,
Region.France,
Region.FranceSpain,
Region.Germany,
Region.GreaterChina,
Region.Greece,
Region.Hungary,
Region.Iceland,
Region.India,
Region.Ireland,
Region.Israel,
Region.Italy,
Region.Japan,
Region.JapanAsia,
Region.JapanEurope,
Region.JapanKorea,
Region.JapanUSA,
Region.SouthKorea,
Region.LatinAmerica,
Region.Lithuania,
Region.Netherlands,
Region.NewZealand,
Region.Norway,
Region.Poland,
Region.Portugal,
Region.Romania,
Region.RussianFederation,
Region.Scandinavia,
Region.Serbia,
Region.Singapore,
Region.Slovakia,
Region.SouthAfrica,
Region.Spain,
Region.SpainPortugal,
Region.Sweden,
Region.Switzerland,
Region.Taiwan,
Region.Thailand,
Region.Turkey,
Region.UnitedArabEmirates,
Region.UnitedKingdom,
Region.UKAustralia,
Region.Ukraine,
Region.UnitedStatesOfAmerica,
Region.USAAsia,
Region.USAAustralia,
Region.USABrazil,
Region.USACanada,
Region.USAEurope,
Region.USAGermany,
Region.USAJapan,
Region.USAKorea,
Region.World,
};
/// <summary>
/// List of available languages
/// </summary>
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
/// <summary>
/// List of Redump-supported Languages
/// </summary>
private static readonly List<Language> RedumpLanguages = new List<Language>
{
Language.Afrikaans,
Language.Albanian,
Language.Arabic,
Language.Armenian,
Language.Basque,
Language.Belarusian,
Language.Bulgarian,
Language.Catalan,
Language.Chinese,
Language.Croatian,
Language.Czech,
Language.Danish,
Language.Dutch,
Language.English,
Language.Estonian,
Language.Finnish,
Language.French,
Language.Gaelic,
Language.German,
Language.Greek,
Language.Hebrew,
Language.Hindi,
Language.Hungarian,
Language.Icelandic,
Language.Indonesian,
Language.Italian,
Language.Japanese,
Language.Korean,
Language.Latin,
Language.Latvian,
Language.Lithuanian,
Language.Macedonian,
Language.Norwegian,
Language.Polish,
Language.Portuguese,
Language.Panjabi,
Language.Romanian,
Language.Russian,
Language.Serbian,
Language.Slovak,
Language.Slovenian,
Language.Spanish,
Language.Swedish,
Language.Tamil,
Language.Thai,
Language.Turkish,
Language.Ukrainian,
Language.Vietnamese,
};
/// <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, Options options, SubmissionInfo submissionInfo)
{
Parent = parent;
Options = options;
SubmissionInfo = submissionInfo.Clone() as SubmissionInfo ?? new SubmissionInfo();
// Limit lists, if necessary
if (this.Options.EnableRedumpCompatibility)
{
SetRedumpRegions();
SetRedumpLanguages();
}
// 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;
// Contents
Parent.GeneralContent.Tab = true;
Parent.GamesTextBox.Tab = true;
Parent.NetYarozeGamesTextBox.Tab = true;
Parent.PlayableDemosTextBox.Tab = true;
Parent.RollingDemosTextBox.Tab = true;
Parent.TechDemosTextBox.Tab = true;
Parent.GameFootageTextBox.Tab = true;
Parent.VideosTextBox.Tab = true;
Parent.PatchesTextBox.Tab = true;
Parent.SavegamesTextBox.Tab = true;
Parent.ExtrasTextBox.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?.FullyMatchedID == null)
Parent.FullyMatchedID.Visibility = Visibility.Collapsed;
else
Parent.FullyMatchedID.Text = SubmissionInfo.FullyMatchedID.ToString();
if (SubmissionInfo?.PartiallyMatchedIDs == null)
Parent.PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
else
Parent.PartiallyMatchedIDs.Text = string.Join(", ", SubmissionInfo.PartiallyMatchedIDs);
if (SubmissionInfo?.CopyProtection?.AntiModchip == null)
Parent.AntiModchip.Visibility = Visibility.Collapsed;
else
Parent.AntiModchip.Text = SubmissionInfo.CopyProtection.AntiModchip.LongName();
if (SubmissionInfo?.TracksAndWriteOffsets?.OtherWriteOffsets == null)
Parent.DiscOffset.Visibility = Visibility.Collapsed;
else
Parent.DiscOffset.Text = SubmissionInfo.TracksAndWriteOffsets.OtherWriteOffsets;
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 (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Filename) != true)
Parent.Filename.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.Header))
Parent.Header.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.InternalName) != true)
Parent.InternalName.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?.Extras?.PVD))
Parent.PVD.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Multisession) != true)
Parent.Multisession.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.RingNonZeroDataStart) != true)
Parent.RingNonZeroDataStart.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.UniversalHash) != true)
Parent.UniversalHash.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 (this.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.CDProjektID))
Parent.CDProjektIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.CDProjektID];
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.Filename))
Parent.Filename.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalName))
Parent.InternalName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalSerialName))
Parent.InternalSerialName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Multisession))
Parent.Multisession.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PFIHash))
Parent.PFIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.RingNonZeroDataStart))
Parent.RingNonZeroDataStart.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart];
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.UniversalHash))
Parent.UniversalHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash];
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)
#if NET48
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields == null)
#if NET48
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
#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.CDProjektID] = Parent.CDProjektIDTextBox.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.Filename] = Parent.Filename.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = Parent.InternalName.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = Parent.InternalSerialName.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession] = Parent.Multisession.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = Parent.PFIHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart] = Parent.RingNonZeroDataStart.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = Parent.SSHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = Parent.SSVersion.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash] = Parent.UniversalHash.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>
/// Repopulate the list of Languages based on Redump support
/// </summary>
private void SetRedumpLanguages()
{
this.Languages = RedumpLanguages.Select(l => new Element<Language>(l)).ToList();
}
/// <summary>
/// Repopulate the list of Regions based on Redump support
/// </summary>
private void SetRedumpRegions()
{
this.Regions = RedumpRegions.Select(r => new Element<Region>(r)).ToList();
}
/// <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.HDDVDDL:
case DiscType.BD25:
case DiscType.BD33:
case DiscType.BD50:
case DiscType.BD66:
case DiscType.BD100:
case DiscType.BD128:
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

@@ -1,293 +0,0 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using MPF.Core.Data;
using MPF.UI.Core.UserControls;
namespace MPF.UI.Core.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;
#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;
logQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
}
#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, LogLevel.USER);
/// <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()
{
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</param>
private void LogInternal(string text, LogLevel logLevel)
{
// Null text gets ignored
if (text == null)
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 (nextText.StartsWith("\r"))
ReplaceLastLine(nextLogLine);
else
AppendToTextBox(nextLogLine);
}
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>
/// 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();
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,277 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using MPF.Core.Data;
using MPF.UI.Core.ComboBoxItems;
using MPF.UI.Core.Windows;
using SabreTools.RedumpLib.Web;
using WPFCustomMessageBox;
namespace MPF.UI.Core.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, Options baseOptions)
{
Parent = parent;
Options = new Options(baseOptions);
// Add handlers
Parent.AaruPathButton.Click += BrowseForPathClick;
Parent.DiscImageCreatorPathButton.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.Redumper };
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>
#if NET48
private bool? TestRedumpLogin()
#else
private async Task<bool?> TestRedumpLogin()
#endif
{
#if NET48
(bool? success, string message) = RedumpWebClient.ValidateCredentials(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
#else
(bool? success, string message) = await RedumpHttpClient.ValidateCredentials(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
#endif
if (success == true)
CustomMessageBox.Show(Parent, message, "Success", MessageBoxButton.OK, MessageBoxImage.Information);
else if (success == false)
CustomMessageBox.Show(Parent, message, "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
else
CustomMessageBox.Show(Parent, message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return success;
}
#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>
#if NET48
private void OnRedumpTestClick(object sender, EventArgs e) =>
TestRedumpLogin();
#else
private async void OnRedumpTestClick(object sender, EventArgs e) =>
_ = await TestRedumpLogin();
#endif
#endregion
}
}

View File

@@ -0,0 +1,127 @@
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.CheckDumpWindow"
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:core="clr-namespace:MPF.UI.Core"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
mc:Ignorable="d"
Title="Check Existing Dump" Width="600" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<Window.DataContext>
<viewModels:CheckDumpViewModel/>
</Window.DataContext>
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,2,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<Run FontWeight="Bold" Text="Check Existing Dump" />
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Settings">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="InputPathLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Input Path"/>
<TextBox x:Name="InputPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding InputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding InputPathTextBoxEnabled}" />
<Button x:Name="InputPathBrowseButton" Grid.Row="0" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding InputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="SystemMediaTypeLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="1" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
ItemsSource="{Binding Systems}" SelectedItem="{Binding Path=CurrentSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding SystemTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHeader}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="1" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right"
ItemsSource="{Binding MediaTypes}" SelectedItem="{Binding Path=CurrentMediaType, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding MediaTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
<Label x:Name="DumpingProgramLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Dumping Program"/>
<ComboBox x:Name="DumpingProgramComboBox" Grid.Row="2" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
ItemsSource="{Binding InternalPrograms}" SelectedItem="{Binding Path=CurrentProgram, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding DumpingProgramComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
</GroupBox>
<!-- Check Dump / Cancel -->
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
<Label/>
<!-- Empty label for padding -->
<Button Name="CheckDumpButton" Height="25" Width="80" IsDefault="True" Content="Check Dump"
IsEnabled="{Binding CheckDumpButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
IsEnabled="{Binding CancelButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Label/>
<!-- Empty label for padding -->
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</coreWindows:WindowBase>

View File

@@ -0,0 +1,291 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using MPF.Core.UI.ViewModels;
using SabreTools.RedumpLib.Data;
using WPFCustomMessageBox;
using WinForms = System.Windows.Forms;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.Windows
{
/// <summary>
/// Interaction logic for CheckDumpWindow.xaml
/// </summary>
public partial class CheckDumpWindow : WindowBase
{
/// <summary>
/// Read-only access to the current check dump view model
/// </summary>
public CheckDumpViewModel CheckDumpViewModel => DataContext as CheckDumpViewModel ?? new CheckDumpViewModel();
#if NET35
#region Settings
private ComboBox? _DumpingProgramComboBox => ItemHelper.FindChild<ComboBox>(this, "DumpingProgramComboBox");
private Button? _InputPathBrowseButton => ItemHelper.FindChild<Button>(this, "InputPathBrowseButton");
private TextBox? _InputPathTextBox => ItemHelper.FindChild<TextBox>(this, "InputPathTextBox");
private ComboBox? _MediaTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "MediaTypeComboBox");
private ComboBox? _SystemTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "SystemTypeComboBox");
#endregion
#region Controls
private System.Windows.Controls.Button? _CheckDumpButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CheckDumpButton");
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
#endregion
#endif
/// <summary>
/// Constructor
/// </summary>
public CheckDumpWindow(MainWindow parent)
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
}
/// <summary>
/// Handler for CheckDumpWindow OnContentRendered event
/// </summary>
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
// Add the click handlers to the UI
AddEventHandlers();
}
#region UI Functionality
/// <summary>
/// Add all event handlers
/// </summary>
public void AddEventHandlers()
{
// Main buttons
#if NET35
_CheckDumpButton!.Click += OnCheckDumpClick;
_CancelButton!.Click += OnCancelClick;
#else
CheckDumpButton.Click += OnCheckDumpClick;
CancelButton.Click += OnCancelClick;
#endif
// User Area Click
#if NET35
_InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
#else
InputPathBrowseButton.Click += InputPathBrowseButtonClick;
#endif
// User Area SelectionChanged
#if NET35
_SystemTypeComboBox!.SelectionChanged += SystemTypeComboBoxSelectionChanged;
_MediaTypeComboBox!.SelectionChanged += MediaTypeComboBoxSelectionChanged;
_DumpingProgramComboBox!.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
#else
SystemTypeComboBox.SelectionChanged += SystemTypeComboBoxSelectionChanged;
MediaTypeComboBox.SelectionChanged += MediaTypeComboBoxSelectionChanged;
DumpingProgramComboBox.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
#endif
// User Area TextChanged
#if NET35
_InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
#else
InputPathTextBox.TextChanged += InputPathTextBoxTextChanged;
#endif
}
/// <summary>
/// Browse for an input file path
/// </summary>
public void BrowseFile()
{
// Get the current path, if possible
string? currentPath = CheckDumpViewModel.InputPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CheckDumpViewModel.Options.DefaultOutputPath))
currentPath = CheckDumpViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "Disc Images|*.iso;*.cue;*.aaruf|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CheckDumpViewModel.InputPath = fileDialog.FileName;
}
}
/// <summary>
/// Display a user message using a CustomMessageBox
/// </summary>
/// <param name="title">Title to display to the user</param>
/// <param name="message">Message to display to the user</param>
/// <param name="optionCount">Number of options to display</param>
/// <param name="flag">true for inquiry, false otherwise</param>
/// <returns>true for positive, false for negative, null for neutral</returns>
public bool? DisplayUserMessage(string title, string message, int optionCount, bool flag)
{
// Set the correct button style
var button = optionCount switch
{
1 => MessageBoxButton.OK,
2 => MessageBoxButton.YesNo,
3 => MessageBoxButton.YesNoCancel,
// This should not happen, but default to "OK"
_ => MessageBoxButton.OK,
};
// Set the correct icon
MessageBoxImage image = flag ? MessageBoxImage.Question : MessageBoxImage.Exclamation;
// Display and get the result
MessageBoxResult result = CustomMessageBox.Show(this, message, title, button, image);
return result switch
{
MessageBoxResult.OK or MessageBoxResult.Yes => true,
MessageBoxResult.No => false,
_ => null,
};
}
/// <summary>
/// Show the disc information window
/// </summary>
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
/// <returns>Dialog open result</returns>
public (bool?, SubmissionInfo?) ShowDiscInformationWindow(SubmissionInfo? submissionInfo)
{
var discInformationWindow = new DiscInformationWindow(CheckDumpViewModel.Options, submissionInfo)
{
Focusable = true,
Owner = this,
ShowActivated = true,
ShowInTaskbar = true,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
discInformationWindow.Closed += delegate { this.Activate(); };
bool? result = discInformationWindow.ShowDialog();
// Copy back the submission info changes, if necessary
if (result == true)
submissionInfo = (discInformationWindow.DiscInformationViewModel.SubmissionInfo.Clone() as SubmissionInfo)!;
return (result, submissionInfo!);
}
#endregion
#region Event Handlers
/// <summary>
/// Handler for CheckDumpButton Click event
/// </summary>
#if NET40
private void OnCheckDumpClick(object sender, EventArgs e)
{
var checkTask = CheckDumpViewModel.CheckDump(ShowDiscInformationWindow);
checkTask.Wait();
string? errorMessage = checkTask.Result;
#else
private async void OnCheckDumpClick(object sender, EventArgs e)
{
string? errorMessage = await CheckDumpViewModel.CheckDump(ShowDiscInformationWindow);
#endif
if (string.IsNullOrEmpty(errorMessage))
{
bool? checkAgain = DisplayUserMessage("Check Complete", "The dump has been processed successfully! Would you like to check another dump?", 2, false);
if (checkAgain == false)
Close();
}
else
{
DisplayUserMessage("Check Failed", errorMessage!, 1, false);
}
}
/// <summary>
/// Handler for CancelButtom Click event
/// </summary>
private void OnCancelClick(object sender, EventArgs e)
{
Close();
}
/// <summary>
/// Handler for DumpingProgramComboBox SelectionChanged event
/// </summary>
public void DumpingProgramComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (CheckDumpViewModel.CanExecuteSelectionChanged)
CheckDumpViewModel.ChangeDumpingProgram();
}
/// <summary>
/// Handler for InputPathBrowseButton Click event
/// </summary>
public void InputPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseFile();
if (CheckDumpViewModel.CanExecuteSelectionChanged)
CheckDumpViewModel.ChangeInputPath();
}
/// <summary>
/// Handler for InputPathTextBox TextChanged event
/// </summary>
public void InputPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CheckDumpViewModel.CanExecuteSelectionChanged)
CheckDumpViewModel.ChangeInputPath();
}
/// <summary>
/// Handler for MediaTypeComboBox SelectionChanged event
/// </summary>
public void MediaTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (CheckDumpViewModel.CanExecuteSelectionChanged)
CheckDumpViewModel.ChangeMediaType();
}
/// <summary>
/// Handler for SystemTypeComboBox SelectionChanged event
/// </summary>
public void SystemTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (CheckDumpViewModel.CanExecuteSelectionChanged)
CheckDumpViewModel.ChangeSystem();
}
#endregion
}
}

View File

@@ -0,0 +1,209 @@
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.CreateIRDWindow"
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:core="clr-namespace:MPF.UI.Core"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
mc:Ignorable="d"
Title="Create PS3 IRD" Width="600" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<Window.DataContext>
<viewModels:CreateIRDViewModel/>
</Window.DataContext>
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,2,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<Run FontWeight="Bold" Text="Create PS3 IRD" />
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Input">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="InputPathLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="PS3 ISO Path"/>
<TextBox x:Name="InputPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding InputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding InputPathTextBoxEnabled}" />
<Button x:Name="InputPathBrowseButton" Grid.Row="0" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding InputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="LogPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="*.getkey.log File Path"/>
<TextBox x:Name="LogPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding LogPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding LogPathTextBoxEnabled}" />
<Button x:Name="LogPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding LogPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
</Grid>
</GroupBox>
<Expander Name="KeyExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define PS3 Disc Encryption Key">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="KeyLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Hexadecimal Key"/>
<TextBox x:Name="KeyTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding HexKey, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding HexKeyTextBoxEnabled}" />
<Label x:Name="KeyPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
ToolTip="Typically a *.key file" Content="Key File Path"/>
<TextBox x:Name="KeyPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding KeyPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding KeyPathTextBoxEnabled}" />
<Button x:Name="KeyPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding KeyPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="KeyStatusLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Key Status"/>
<Label x:Name="KeyStatusText" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" Content="{Binding KeyStatus}"/>
</Grid>
</Expander>
<Expander Name="DiscIDExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define PS3 Disc ID">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="DiscIDLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Disc ID"/>
<TextBox x:Name="DiscIDTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding DiscIDString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding DiscIDTextBoxEnabled}" />
<Label x:Name="DiscIDStatusLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="DiscID Status"/>
<Label x:Name="DiscIDStatusText" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Content="{Binding DiscIDStatus}"/>
</Grid>
</Expander>
<Expander Name="PICExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define Permanent Information &#x26; Control (PIC)">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="LayerbreakLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Layerbreak (sectors)"/>
<TextBox x:Name="LayerbreakTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding LayerbreakString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding LayerbreakTextBoxEnabled}" />
<Label x:Name="PICLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="PIC"/>
<TextBox x:Name="PICTextBox" Grid.Row="1" Grid.Column="1" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Top"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"
MinHeight="55" MaxHeight="146" AcceptsReturn="True"
Text="{Binding PICString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding PICTextBoxEnabled}" />
<Label x:Name="PICPathLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center"
ToolTip="Typically a *.physical or *_PIC.bin file" Content="PIC File Path"/>
<TextBox x:Name="PICPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding PICPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding PICPathTextBoxEnabled}" />
<Button x:Name="PICPathBrowseButton" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding PICPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="PICStatusLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="PIC Status"/>
<Label x:Name="PICStatusText" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" Content="{Binding PICStatus}"/>
</Grid>
</Expander>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center"
Text="{Binding CreateIRDStatus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</UniformGrid>
</GroupBox>
<!-- Create IRD / Cancel -->
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
<Label/>
<!-- Empty label for padding -->
<Button Name="CreateIRDButton" Height="25" Width="80" IsDefault="True" Content="Create IRD"
IsEnabled="{Binding CreateIRDButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
IsEnabled="{Binding CancelButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Label/>
<!-- Empty label for padding -->
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</coreWindows:WindowBase>

View File

@@ -0,0 +1,489 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using MPF.Core.UI.ViewModels;
using WPFCustomMessageBox;
using WinForms = System.Windows.Forms;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.Windows
{
/// <summary>
/// Interaction logic for CreateIRDWindow.xaml
/// </summary>
public partial class CreateIRDWindow : WindowBase
{
/// <summary>
/// Read-only access to the current check dump view model
/// </summary>
public CreateIRDViewModel CreateIRDViewModel => DataContext as CreateIRDViewModel ?? new CreateIRDViewModel();
#if NET35
#region Settings
private Button? _InputPathBrowseButton => ItemHelper.FindChild<Button>(this, "InputPathBrowseButton");
private TextBox? _InputPathTextBox => ItemHelper.FindChild<TextBox>(this, "InputPathTextBox");
private Button? _LogPathBrowseButton => ItemHelper.FindChild<Button>(this, "LogPathBrowseButton");
private TextBox? _LogPathTextBox => ItemHelper.FindChild<TextBox>(this, "LogPathTextBox");
private Button? _KeyPathBrowseButton => ItemHelper.FindChild<Button>(this, "KeyPathBrowseButton");
private TextBox? _KeyPathTextBox => ItemHelper.FindChild<TextBox>(this, "KeyPathTextBox");
private TextBox? _KeyTextBox => ItemHelper.FindChild<TextBox>(this, "KeyTextBox");
private TextBox? _DiscIDTextBox => ItemHelper.FindChild<TextBox>(this, "DiscIDTextBox");
private Button? _PICPathBrowseButton => ItemHelper.FindChild<Button>(this, "PICPathBrowseButton");
private TextBox? _PICPathTextBox => ItemHelper.FindChild<TextBox>(this, "PICPathTextBox");
private TextBox? _PICTextBox => ItemHelper.FindChild<TextBox>(this, "PICTextBox");
private TextBox? _LayerbreakTextBox => ItemHelper.FindChild<TextBox>(this, "LayerbreakTextBox");
#endregion
#region Controls
private System.Windows.Controls.Button? _CreateIRDButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CreateIRDButton");
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
#endregion
#region Expanders
private Expander? _KeyExpander => ItemHelper.FindChild<Expander>(this, "KeyExpander");
private Expander? _DiscIDExpander => ItemHelper.FindChild<Expander>(this, "DiscIDExpander");
private Expander? _PICExpander => ItemHelper.FindChild<Expander>(this, "PICExpander");
#endregion
#endif
/// <summary>
/// Constructor
/// </summary>
public CreateIRDWindow(MainWindow parent)
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
}
/// <summary>
/// Handler for CheckDumpWindow OnContentRendered event
/// </summary>
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
// Add the click handlers to the UI
AddEventHandlers();
}
#region UI Functionality
/// <summary>
/// Add all event handlers
/// </summary>
public void AddEventHandlers()
{
// Main buttons
#if NET35
_CreateIRDButton!.Click += OnCreateIRDClick;
_CancelButton!.Click += OnCancelClick;
#else
CreateIRDButton.Click += OnCreateIRDClick;
CancelButton.Click += OnCancelClick;
#endif
// User Area Click
#if NET35
_InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
_LogPathBrowseButton!.Click += LogPathBrowseButtonClick;
_KeyPathBrowseButton!.Click += KeyPathBrowseButtonClick;
_PICPathBrowseButton!.Click += PICPathBrowseButtonClick;
#else
InputPathBrowseButton.Click += InputPathBrowseButtonClick;
LogPathBrowseButton.Click += LogPathBrowseButtonClick;
KeyPathBrowseButton.Click += KeyPathBrowseButtonClick;
PICPathBrowseButton.Click += PICPathBrowseButtonClick;
#endif
// User Area TextChanged
#if NET35
_InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
_LogPathTextBox!.TextChanged += LogPathTextBoxTextChanged;
_KeyPathTextBox!.TextChanged += KeyPathTextBoxTextChanged;
_KeyTextBox!.TextChanged += KeyTextBoxTextChanged;
_DiscIDTextBox!.TextChanged += DiscIDTextBoxTextChanged;
_PICPathTextBox!.TextChanged += PICPathTextBoxTextChanged;
_PICTextBox!.TextChanged += PICTextBoxTextChanged;
_LayerbreakTextBox!.TextChanged += LayerbreakTextBoxTextChanged;
#else
InputPathTextBox.TextChanged += InputPathTextBoxTextChanged;
LogPathTextBox.TextChanged += LogPathTextBoxTextChanged;
KeyPathTextBox.TextChanged += KeyPathTextBoxTextChanged;
KeyTextBox.TextChanged += KeyTextBoxTextChanged;
DiscIDTextBox.TextChanged += DiscIDTextBoxTextChanged;
PICPathTextBox.TextChanged += PICPathTextBoxTextChanged;
PICTextBox.TextChanged += PICTextBoxTextChanged;
LayerbreakTextBox.TextChanged += LayerbreakTextBoxTextChanged;
#endif
}
/// <summary>
/// Browse for an input ISO file path
/// </summary>
public void BrowseISOFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.InputPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "ISO|*.iso|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.InputPath = fileDialog.FileName;
}
}
/// <summary>
/// Browse for an .getkey.log file path
/// </summary>
public void BrowseLogFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.LogPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "GetKey Log|*.getkey.log|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.LogPath = fileDialog.FileName;
}
}
/// <summary>
/// Browse for an key file path
/// </summary>
public void BrowseKeyFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.LogPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "Key|*.key|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.KeyPath = fileDialog.FileName;
}
}
/// <summary>
/// Browse for an IRD output path
/// </summary>
/// <returns>Output path if provided, else null</returns>
public string? BrowseOutputFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.InputPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = Path.Combine(CreateIRDViewModel.Options.DefaultOutputPath, "game.ird");
else if (string.IsNullOrEmpty(currentPath))
currentPath = "game.ird";
if (string.IsNullOrEmpty(currentPath))
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, "game.ird");
// Get the full path
currentPath = Path.GetFullPath(currentPath);
// Get the directory
var directory = Path.GetDirectoryName(currentPath);
// Get the filename
string filename = Path.ChangeExtension(Path.GetFileName(currentPath), ".ird");
WinForms.FileDialog fileDialog = new WinForms.SaveFileDialog
{
FileName = filename,
InitialDirectory = directory,
Filter = "IRD File|*.ird|All Files|*.*"
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
return fileDialog.FileName;
else
return null;
}
/// <summary>
/// Browse for an PIC file path
/// </summary>
public void BrowsePICFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.LogPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "PIC|*.physical;*_PIC.bin;*.PIC|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.PICPath = fileDialog.FileName;
}
}
/// <summary>
/// Display a user message using a CustomMessageBox
/// </summary>
/// <param name="title">Title to display to the user</param>
/// <param name="message">Message to display to the user</param>
/// <param name="optionCount">Number of options to display</param>
/// <param name="flag">true for inquiry, false otherwise</param>
/// <returns>true for positive, false for negative, null for neutral</returns>
public bool? DisplayUserMessage(string title, string message, int optionCount, bool flag)
{
// Set the correct button style
var button = optionCount switch
{
1 => MessageBoxButton.OK,
2 => MessageBoxButton.YesNo,
3 => MessageBoxButton.YesNoCancel,
// This should not happen, but default to "OK"
_ => MessageBoxButton.OK,
};
// Set the correct icon
MessageBoxImage image = flag ? MessageBoxImage.Question : MessageBoxImage.Exclamation;
// Display and get the result
MessageBoxResult result = CustomMessageBox.Show(this, message, title, button, image);
return result switch
{
MessageBoxResult.OK or MessageBoxResult.Yes => true,
MessageBoxResult.No => false,
_ => null,
};
}
#endregion
#region Event Handlers
/// <summary>
/// Handler for CreateIRDButton Click event
/// </summary>
private void OnCreateIRDClick(object sender, EventArgs e)
{
#if NET35
if (_KeyExpander != null) _KeyExpander.IsExpanded = false;
if (_DiscIDExpander != null) _DiscIDExpander.IsExpanded = false;
if (_PICExpander != null) _PICExpander.IsExpanded = false;
#else
KeyExpander.IsExpanded = false;
DiscIDExpander.IsExpanded = false;
PICExpander.IsExpanded = false;
#endif
string tempStatus = CreateIRDViewModel.CreateIRDStatus;
bool[] enabledFields = CreateIRDViewModel.DisableUIFields();
CreateIRDViewModel.CreateIRDStatus = "Creating IRD... Please Wait";
string? outputPath = BrowseOutputFile();
string? errorMessage = "Please provide an output path";
if (outputPath != null)
{
errorMessage = CreateIRDViewModel.CreateIRD(outputPath);
}
if (string.IsNullOrEmpty(errorMessage))
{
bool? checkAgain = DisplayUserMessage("IRD Create", "An IRD has been created successfully! Would you like to create another IRD?", 2, false);
if (checkAgain == false)
Close();
else
CreateIRDViewModel.ResetFields();
}
else
{
DisplayUserMessage("Failed to create IRD", errorMessage!, 1, false);
CreateIRDViewModel.ReenableUIFields(enabledFields);
CreateIRDViewModel.CreateIRDStatus = tempStatus;
}
}
/// <summary>
/// Handler for CancelButtom Click event
/// </summary>
private void OnCancelClick(object sender, EventArgs e)
{
Close();
}
/// <summary>
/// Handler for DiscIDTextBox TextChanged event
/// </summary>
public void DiscIDTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeDiscID();
}
/// <summary>
/// Handler for InputPathBrowseButton Click event
/// </summary>
public void InputPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseISOFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeInputPath();
}
/// <summary>
/// Handler for InputPathTextBox TextChanged event
/// </summary>
public void InputPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeInputPath();
}
/// <summary>
/// Handler for LogPathBrowseButton Click event
/// </summary>
public void LogPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseLogFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeLogPath();
}
/// <summary>
/// Handler for LogPathTextBox TextChanged event
/// </summary>
public void LogPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeLogPath();
}
/// <summary>
/// Handler for KeyPathBrowseButton Click event
/// </summary>
public void KeyPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseKeyFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeKeyPath();
}
/// <summary>
/// Handler for KeyPathTextBox TextChanged event
/// </summary>
public void KeyPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeKeyPath();
}
/// <summary>
/// Handler for KeyTextBox TextChanged event
/// </summary>
public void KeyTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeKey();
}
/// <summary>
/// Handler for PICPathBrowseButton Click event
/// </summary>
public void PICPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowsePICFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangePICPath();
}
/// <summary>
/// Handler for PICPathTextBox TextChanged event
/// </summary>
public void PICPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangePICPath();
}
/// <summary>
/// Handler for PICTextBox TextChanged event
/// </summary>
public void PICTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangePIC();
}
/// <summary>
/// Handler for LayerbreakTextBox TextChanged event
/// </summary>
public void LayerbreakTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeLayerbreak();
}
#endregion
}
}

View File

@@ -4,16 +4,18 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
xmlns:core="clr-namespace:MPF.UI.Core"
xmlns:redump="clr-namespace:SabreTools.RedumpLib.Data;assembly=SabreTools.RedumpLib"
xmlns:windows="clr-namespace:MPF.UI.Core.Windows"
mc:Ignorable="d"
Title="Disc Information" Width="515" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
</WindowChrome.WindowChrome>
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical" Width="500" MaxHeight="650">
<Grid Margin="0,10,0,0">
@@ -29,7 +31,7 @@
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock TextAlignment="Center"><Bold>Disc Information</Bold></TextBlock>
<Run FontWeight="Bold" Text="Disc Information" />
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
@@ -74,10 +76,12 @@
<StackPanel Orientation="Vertical">
<controls:UserInput Label="Title"
Text="{Binding SubmissionInfo.CommonDiscInfo.Title, Mode=TwoWay}"/>
<controls:UserInput x:Name="AlternativeTitleTextBox" Label="Alternative Title"/>
<controls:UserInput x:Name="AlternativeTitleTextBox" Label="Alternative Title"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AlternativeTitle], Mode=TwoWay}"/>
<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 x:Name="AlternativeForeignTitleTextBox" Label="Alternative Foreign Title"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AlternativeForeignTitle], Mode=TwoWay}"/>
<controls:UserInput Label="Disc Number / Letter"
Text="{Binding SubmissionInfo.CommonDiscInfo.DiscNumberLetter, Mode=TwoWay}"/>
<controls:UserInput Label="Disc Title"
@@ -90,8 +94,9 @@
</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}"
Style="{DynamicResource CustomComboBoxStyle}" />
<ComboBox x:Name="CategoryComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
ItemsSource="{Binding Categories}" SelectedItem="{Binding SubmissionInfo.CommonDiscInfo.Category, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
<Grid>
@@ -101,8 +106,9 @@
</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}"
Style="{DynamicResource CustomComboBoxStyle}" />
<ComboBox x:Name="RegionComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
ItemsSource="{Binding Regions}" SelectedItem="{Binding SubmissionInfo.CommonDiscInfo.Region, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
<Grid>
@@ -112,7 +118,8 @@
</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}"
<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>
@@ -129,7 +136,8 @@
</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}"
<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>
@@ -164,7 +172,14 @@
<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"/>
<controls:UserInput x:Name="CompatibleOSTextBox" Label="Compatible OS"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)CompatibleOS], Mode=TwoWay}"/>
<controls:UserInput x:Name="DiscKeyTextBox" Label="Disc Key" Visibility="Collapsed"
Text="{Binding Path=SubmissionInfo.Extras.DiscKey, Mode=TwoWay}"/>
<controls:UserInput x:Name="DiscIDTextBox" Label="Disc ID" Visibility="Collapsed"
Text="{Binding Path=SubmissionInfo.Extras.DiscID, Mode=TwoWay}"/>
<controls:UserInput x:Name="GenreTextBox" Label="Genre"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Genre], Mode=TwoWay}"/>
<controls:UserInput x:Name="ProtectionTextBox" Label="Protection" ToolTip="CAUTION: Only edit if you know what you are doing!"
Text="{Binding SubmissionInfo.CopyProtection.Protection, Mode=TwoWay}" TextHeight="75" TextWrapping="Wrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
@@ -175,9 +190,11 @@
</Grid.ColumnDefinitions>
<Label/>
<CheckBox Grid.Column="1" x:Name="PostgapTypeCheckBox" Content="Postgap type (Form 2)" Margin="0,4"/>
<CheckBox Grid.Column="1" x:Name="PostgapTypeCheckBox" Content="Postgap type (Form 2)" Margin="0,4"
IsChecked="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PostgapType], Mode=TwoWay}"/>
</Grid>
<controls:UserInput x:Name="SeriesTextBox" Label="Series"/>
<controls:UserInput x:Name="SeriesTextBox" Label="Series"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Series], Mode=TwoWay}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
@@ -185,7 +202,8 @@
</Grid.ColumnDefinitions>
<Label/>
<CheckBox Grid.Column="1" x:Name="VCDCheckBox" Content="V-CD" Margin="0,4"/>
<CheckBox Grid.Column="1" x:Name="VCDCheckBox" Content="V-CD" Margin="0,4"
IsChecked="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VCD], Mode=TwoWay}"/>
</Grid>
</StackPanel>
</GroupBox>
@@ -193,43 +211,78 @@
<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="CDProjektIDTextBox" Label="CD Projekt ID"/>
<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"/>
<controls:UserInput x:Name="BBFCRegistrationNumberTextBox" Label="BBFC Reg. No."
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BBFCRegistrationNumber], Mode=TwoWay}"/>
<controls:UserInput x:Name="DiscHologramIDTextBox" Label="Disc Hologram ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DiscHologramID], Mode=TwoWay}"/>
<controls:UserInput x:Name="DNASDiscIDTextBox" Label="DNAS Disc ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DNASDiscID], Mode=TwoWay}"/>
<controls:UserInput x:Name="ISBNTextBox" Label="ISBN"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ISBN], Mode=TwoWay}"/>
<controls:UserInput x:Name="ISSNTextBox" Label="ISSN"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ISSN], Mode=TwoWay}"/>
<controls:UserInput x:Name="PPNTextBox" Label="PPN"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PPN], Mode=TwoWay}"/>
<controls:UserInput x:Name="VFCCodeTextBox" Label="VFC code"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VFCCode], Mode=TwoWay}"/>
</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"/>
<controls:UserInput x:Name="AcclaimIDTextBox" Label="Acclaim ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AcclaimID], Mode=TwoWay}"/>
<controls:UserInput x:Name="ActivisionIDTextBox" Label="Activision ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ActivisionID], Mode=TwoWay}"/>
<controls:UserInput x:Name="BandaiIDTextBox" Label="Bandai ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BandaiID], Mode=TwoWay}"/>
<controls:UserInput x:Name="BethesdaIDTextBox" Label="Bandai ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BethesdaID], Mode=TwoWay}"/>
<controls:UserInput x:Name="CDProjektIDTextBox" Label="CD Projekt ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)CDProjektID], Mode=TwoWay}"/>
<controls:UserInput x:Name="EidosIDTextBox" Label="Eidos ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)EidosID], Mode=TwoWay}"/>
<controls:UserInput x:Name="ElectronicArtsIDTextBox" Label="Electronic Arts ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ElectronicArtsID], Mode=TwoWay}"/>
<controls:UserInput x:Name="FoxInteractiveIDTextBox" Label="Fox Interactive ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)FoxInteractiveID], Mode=TwoWay}"/>
<controls:UserInput x:Name="GTInteractiveIDTextBox" Label="GT Interactive ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)GTInteractiveID], Mode=TwoWay}"/>
<controls:UserInput x:Name="JASRACIDTextBox" Label="JASRAC ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)JASRACID], Mode=TwoWay}"/>
<controls:UserInput x:Name="KingRecordsIDTextBox" Label="King Records ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KingRecordsID], Mode=TwoWay}"/>
<controls:UserInput x:Name="KoeiIDTextBox" Label="Koei ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KoeiID], Mode=TwoWay}"/>
<controls:UserInput x:Name="KonamiIDTextBox" Label="Konami ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KonamiID], Mode=TwoWay}"/>
<controls:UserInput x:Name="LucasArtsIDTextBox" Label="LucasArts ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)LucasArtsID], Mode=TwoWay}"/>
<controls:UserInput x:Name="MicrosoftIDTextBox" Label="Microsoft ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)MicrosoftID], Mode=TwoWay}"/>
<controls:UserInput x:Name="NaganoIDTextBox" Label="Nagano ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NaganoID], Mode=TwoWay}"/>
<controls:UserInput x:Name="NamcoIDTextBox" Label="Namco ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NamcoID], Mode=TwoWay}"/>
<controls:UserInput x:Name="NipponIchiSoftwareIDTextBox" Label="Nippon Ichi Software ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NipponIchiSoftwareID], Mode=TwoWay}"/>
<controls:UserInput x:Name="OriginIDTextBox" Label="Origin ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)OriginID], Mode=TwoWay}"/>
<controls:UserInput x:Name="PonyCanyonIDTextBox" Label="Pony Canyon ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PonyCanyonID], Mode=TwoWay}"/>
<controls:UserInput x:Name="SegaIDTextBox" Label="Sega ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SegaID], Mode=TwoWay}"/>
<controls:UserInput x:Name="SelenIDTextBox" Label="Selen ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SelenID], Mode=TwoWay}"/>
<controls:UserInput x:Name="SierraIDTextBox" Label="Sierra ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SierraID], Mode=TwoWay}"/>
<controls:UserInput x:Name="TaitoIDTextBox" Label="Taito ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)TaitoID], Mode=TwoWay}"/>
<controls:UserInput x:Name="UbisoftIDTextBox" Label="Ubisoft ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)UbisoftID], Mode=TwoWay}"/>
<controls:UserInput x:Name="ValveIDTextBox" Label="Valve ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ValveID], Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
</StackPanel>
@@ -243,33 +296,48 @@
Text="{Binding SubmissionInfo.CommonDiscInfo.Contents, Mode=TwoWay}" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<!-- Applications -->
<controls:UserInput x:Name="ApplicationsTextBox" Label="Applications" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Applications], Mode=TwoWay}"/>
<!-- Games -->
<controls:UserInput x:Name="GamesTextBox" Label="Games" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Games], Mode=TwoWay}"/>
<controls:UserInput x:Name="NetYarozeGamesTextBox" Label="Net Yaroze Games" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)NetYarozeGames], Mode=TwoWay}"/>
<!-- Demos -->
<controls:UserInput x:Name="PlayableDemosTextBox" Label="Playable Demos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)PlayableDemos], Mode=TwoWay}"/>
<controls:UserInput x:Name="RollingDemosTextBox" Label="Rolling Demos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)RollingDemos], Mode=TwoWay}"/>
<controls:UserInput x:Name="TechDemosTextBox" Label="Tech Demos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)TechDemos], Mode=TwoWay}"/>
<!-- Video -->
<controls:UserInput x:Name="GameFootageTextBox" Label="Game Footage" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)GameFootage], Mode=TwoWay}"/>
<controls:UserInput x:Name="VideosTextBox" Label="Videos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Videos], Mode=TwoWay}"/>
<!-- Miscellaneous -->
<controls:UserInput x:Name="PatchesTextBox" Label="Patches" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Patches], Mode=TwoWay}"/>
<controls:UserInput x:Name="SavegamesTextBox" Label="Savegames" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Savegames], Mode=TwoWay}"/>
<controls:UserInput x:Name="ExtrasTextBox" Label="Extras" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Extras], Mode=TwoWay}"/>
</StackPanel>
</ScrollViewer>
</TabItem>
@@ -339,50 +407,85 @@
<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="FullyMatchedID" Label="Fully Matched ID" IsReadOnly="True"/>
<controls:UserInput x:Name="FullyMatchedID" Label="Fully Matched ID" IsReadOnly="True"
Text="{Binding SubmissionInfo.FullyMatchedID, Mode=TwoWay}"/>
<controls:UserInput x:Name="PartiallyMatchedIDs" Label="Partially Matched ID(s)" IsReadOnly="True"/>
<controls:UserInput x:Name="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"/>
<controls:UserInput x:Name="DiscOffset" Label="Disc Offset" 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="HashData" Label="Hash Data" IsReadOnly="True"
Text="{Binding SubmissionInfo.TracksAndWriteOffsets.ClrMameProData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="HashDataSize" Label="Size" IsReadOnly="True"
Text="{Binding SubmissionInfo.SizeAndChecksums.Size, Mode=TwoWay}"/>
<controls:UserInput x:Name="HashDataCRC" Label="CRC-32" IsReadOnly="True"
Text="{Binding SubmissionInfo.SizeAndChecksums.CRC32, Mode=TwoWay}"/>
<controls:UserInput x:Name="HashDataMD5" Label="MD5" IsReadOnly="True"
Text="{Binding SubmissionInfo.SizeAndChecksums.MD5, Mode=TwoWay}"/>
<controls:UserInput x:Name="HashDataSHA1" Label="SHA-1" IsReadOnly="True"
Text="{Binding SubmissionInfo.SizeAndChecksums.SHA1, Mode=TwoWay}"/>
<controls:UserInput x:Name="HashDataLayerbreak1" Label="Layerbreak 1" IsReadOnly="True"
Text="{Binding SubmissionInfo.SizeAndChecksums.Layerbreak, Mode=TwoWay}"/>
<controls:UserInput x:Name="HashDataLayerbreak2" Label="Layerbreak 2" IsReadOnly="True"
Text="{Binding SubmissionInfo.SizeAndChecksums.Layerbreak2, Mode=TwoWay}"/>
<controls:UserInput x:Name="HashDataLayerbreak3" Label="Layerbreak 3" IsReadOnly="True"
Text="{Binding SubmissionInfo.SizeAndChecksums.Layerbreak3, Mode=TwoWay}"/>
<controls:UserInput x:Name="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.AntiModchip, Mode=TwoWay}"/>
<controls:UserInput x:Name="DiscOffset" Label="Disc Offset" IsReadOnly="True"
Text="{Binding SubmissionInfo.TracksAndWriteOffsets.OtherWriteOffsets, Mode=TwoWay}"/>
<controls:UserInput x:Name="DMIHash" Label="DMI Hash" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DMIHash], Mode=TwoWay}"/>
<controls:UserInput x:Name="EDC" Label="EDC" IsReadOnly="True"
Text="{Binding SubmissionInfo.EDC.EDC, Mode=TwoWay}"/>
<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="Filename" Label="Filename" IsReadOnly="True"
TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Filename], 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="InternalName" Label="Internal Names" IsReadOnly="True"/>
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"/>
<controls:UserInput x:Name="InternalName" Label="Internal Names" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)InternalName], Mode=TwoWay}"/>
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)InternalSerialName], Mode=TwoWay}"/>
<controls:UserInput x:Name="Multisession" Label="Multisession" IsReadOnly="True" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"/>
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Multisession], Mode=TwoWay}"/>
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.LibCrypt, Mode=TwoWay}"/>
<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="PFIHash" Label="PFI Hash" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PFIHash], Mode=TwoWay}"/>
<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="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="RingNonZeroDataStart" Label="Ring Non-Zero Data Start" IsReadOnly="True"/>
<controls:UserInput x:Name="RingNonZeroDataStart" Label="Ring Non-Zero Data Start" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)RingNonZeroDataStart], Mode=TwoWay}"/>
<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="SSHash" Label="Security Sector Hash" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SSHash], Mode=TwoWay}"/>
<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="UniversalHash" Label="Universal Hash (SHA-1)" 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"/>
<controls:UserInput x:Name="SSVersion" Label="Security Sector Version" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SSVersion], Mode=TwoWay}"/>
<controls:UserInput x:Name="UniversalHash" Label="Universal Hash (SHA-1)" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)UniversalHash], Mode=TwoWay}"/>
<controls:UserInput x:Name="VolumeLabel" Label="Volume Label" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VolumeLabel], Mode=TwoWay}"/>
<controls:UserInput x:Name="XeMID" Label="XeMID" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)XeMID], Mode=TwoWay}"/>
<controls:UserInput x:Name="XMID" Label="XMID" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)XMID], Mode=TwoWay}"/>
<!-- TODO: Add track/checksum data once there's a reasonable way to do so -->
</StackPanel>

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