Compare commits

..

200 Commits
3.2.0 ... 3.2.4

Author SHA1 Message Date
Matt Nadareski
e72336a835 Bump version 2024-11-24 19:55:26 -05:00
Deterous
886a0a4f07 Add support for Redumper Xbox dumps (#766)
* Add support for Redumper Xbox dumps

* Fix doubleup of ranges variable name

* Don't use FileInfo

* Fix RemoveHeader

* Use manual Stream.CopyTo

* Capitalise DMI/PFI/SS hashes

* Final cleanup

* Final final cleanup
2024-11-24 19:50:45 -05:00
Deterous
093435e7b0 Don't add trailing spaces to BCA output (#765)
* Don't add trailing spaces to BCA output

* Fix import

* Fix var name
2024-11-23 22:32:40 -05:00
Matt Nadareski
59a427963d Fix PS3 firmware version being omitted 2024-11-23 19:59:25 -05:00
Deterous
19d6006aff Cleanup CleanSS check (#763)
* Cleanup CleanSS check

* Make ssv2 check consistent
2024-11-22 23:15:15 -05:00
Matt Nadareski
617b0ba2c5 Version gate remaining Linq statements 2024-11-22 12:44:50 -05:00
Matt Nadareski
195c83499c Clean up usings after last commit 2024-11-22 11:56:12 -05:00
Matt Nadareski
e5b02c27a8 Replace some uses of Regex.Replace 2024-11-22 11:50:07 -05:00
Deterous
c7b6b08397 Improve parameters for default output path (#762)
* Improve parameters for default output path

* ToUpperInvariant
2024-11-22 10:55:20 -05:00
Matt Nadareski
da6b2f0e24 Remove unncessary .NET Framework 4.0 gating 2024-11-21 13:29:06 -05:00
Matt Nadareski
c50c3edcc5 Don't preemptively sort protections 2024-11-21 12:08:02 -05:00
Matt Nadareski
d628b2a0dd Use List in protection formatting 2024-11-21 12:06:12 -05:00
Matt Nadareski
f6baaa6f5e Reduce unnecessary use of IEnumerable 2024-11-21 11:55:00 -05:00
Matt Nadareski
b05c82e903 Remove usages of this. from UI code 2024-11-21 11:49:14 -05:00
Matt Nadareski
65f472c4c9 Fix minor efficiency issues 2024-11-21 11:46:11 -05:00
Matt Nadareski
104dd4348b Update packages for bugfixes 2024-11-21 11:27:57 -05:00
Matt Nadareski
2337a2ed86 Use expanded version of split regex (fixes #761) 2024-11-20 11:41:28 -05:00
Deterous
2ce632f8b4 Fix PIC parser (#760) 2024-11-19 13:01:15 -05:00
Matt Nadareski
ce917f4f61 Remove usages of this. from themes 2024-11-18 15:44:43 -05:00
Matt Nadareski
d029cf4164 Add IMA extension for floppy disks 2024-11-18 15:39:43 -05:00
Matt Nadareski
74d52c2f3b Add Track 0/00/A/AA to Generic 2024-11-18 15:18:05 -05:00
Matt Nadareski
785786c3a9 Add unused "Generic" processor 2024-11-18 13:24:19 -05:00
Matt Nadareski
466d8f58b0 Add skeleton to Redumper CD by default 2024-11-18 12:22:50 -05:00
Matt Nadareski
bc690614e6 Remove usages of this. in more places 2024-11-18 12:21:13 -05:00
Deterous
d44798b27c Fix fully matching when multiple matches found (#759)
Co-authored-by: Matt Nadareski <mnadareski@outlook.com>
2024-11-17 19:33:09 -05:00
Matt Nadareski
595cd0d60f Consolidate parameter string splitting 2024-11-16 01:23:30 -05:00
Matt Nadareski
68fd5a2aa0 Be smarter about some data types 2024-11-16 01:07:47 -05:00
Matt Nadareski
6da98aa65c Add .NET 9 to target frameworks 2024-11-16 00:37:14 -05:00
Matt Nadareski
d8d149446f Be smarter about Linq usage 2024-11-12 22:18:08 -05:00
Matt Nadareski
4d0f48be10 Update Redumper to build 438 2024-11-06 16:06:22 -05:00
Matt Nadareski
8a048c8a57 Bump version 2024-11-06 15:36:56 -05:00
Matt Nadareski
d51db072fc Force rebuild of rolling 2024-11-05 15:33:45 -05:00
Matt Nadareski
468c9937da Reduce null use in BaseProcessor 2024-11-05 15:30:54 -05:00
Matt Nadareski
206ac76633 Fix missed GetOutputFiles invocation 2024-11-05 15:23:03 -05:00
Matt Nadareski
339b0d93d1 Ensure debug symbols are stripped 2024-11-05 14:13:18 -05:00
Matt Nadareski
fd5da5de74 Add conf to build matrix 2024-11-05 13:53:05 -05:00
Matt Nadareski
42146f991d Attempt to reduce nesting in GHA builds 2024-11-05 13:46:44 -05:00
Matt Nadareski
f3f05eee48 Attempt to reduce nesting in GHA builds 2024-11-05 13:45:40 -05:00
Matt Nadareski
61bf2f69aa Attempt to reduce nesting in GHA builds 2024-11-05 13:40:05 -05:00
Matt Nadareski
546aa70b69 Update BinaryObjectScanner to 3.1.16 2024-11-05 13:17:49 -05:00
Matt Nadareski
65cd1cede3 Remove last instances of ValueTuple usage 2024-11-03 23:14:22 -05:00
Matt Nadareski
dffa16139a Remove tupling from everything except CLI programs 2024-11-03 23:09:54 -05:00
Matt Nadareski
98bacf11fc Reduce tupling even more 2024-11-03 22:51:24 -05:00
Matt Nadareski
ee0ccecfb2 Replace user info func with ProcessUserInfoDelegate 2024-11-03 22:29:26 -05:00
Matt Nadareski
a0825f276b Use new ProtectionDictionary type 2024-11-03 22:15:42 -05:00
Matt Nadareski
66d61e20de Reduce call complexity for login result 2024-11-03 21:59:22 -05:00
Matt Nadareski
6455ebd434 Simplify GetMediaType return 2024-11-03 21:55:04 -05:00
Matt Nadareski
f4436fddfa Remove unused CompilerServices.Unsafe library 2024-11-03 21:06:14 -05:00
Matt Nadareski
317777cf93 Remove old .NET version of ValueTuple where possible 2024-11-03 20:40:36 -05:00
Matt Nadareski
da6dbe136e Remove unnecessary System.ValueTuple usage 2024-11-03 20:38:35 -05:00
Matt Nadareski
13d7cf8124 Remove unused IndexRange library 2024-11-03 20:16:48 -05:00
Matt Nadareski
bb8fea625b Update BinaryObjectScanner to 3.1.15 2024-11-03 20:13:38 -05:00
Matt Nadareski
15a0659867 Use rolling release, not AppVeyor, in issue templates 2024-10-22 12:39:51 -04:00
Matt Nadareski
988a5f6d2b Update RedumpLib to 1.4.4 2024-10-18 13:04:53 -04:00
Matt Nadareski
1941639473 No directory means no files 2024-10-16 12:04:39 -04:00
Matt Nadareski
d1772f743e Reduce cleverness in output file code 2024-10-16 11:45:06 -04:00
Matt Nadareski
870c2d1cec Add separate field for Regex; assorted cleanup 2024-10-16 02:48:39 -04:00
Matt Nadareski
f7f6ae1eee Ensure Regex directories are unescaped 2024-10-16 02:31:11 -04:00
Matt Nadareski
dd9e527592 Use new output file logic in processors 2024-10-16 01:57:31 -04:00
Matt Nadareski
e1122fa976 Ensure consistency in output file path checking (fixes #755) 2024-10-16 01:56:48 -04:00
Matt Nadareski
8a44fa3355 Ensure that the full base path is being used 2024-10-14 21:47:05 -04:00
Matt Nadareski
51a9e3005f Use fake filename for Redumper DAT 2024-10-14 21:23:16 -04:00
Matt Nadareski
413b6da24b Fix trimming of header output (fixes #754) 2024-10-13 11:00:07 -04:00
Matt Nadareski
172a0fb5dc Update Redumper to build 416 2024-10-12 20:13:27 -04:00
Matt Nadareski
83a189a5d3 Format CleanRip BCA wtih 2-byte blocks (fixes #743) 2024-10-10 12:02:22 -04:00
Matt Nadareski
60c27ec89b Sum track errors in Redumper (fixes #745) 2024-10-10 11:19:27 -04:00
Matt Nadareski
51733557cd Remove ReadLine in list commands 2024-10-10 11:11:54 -04:00
Matt Nadareski
93d964c603 Make .NET 8 the default in issue reports 2024-10-09 12:37:21 -04:00
Matt Nadareski
2925f2262b Change multiple offset delimiter 2024-10-08 22:04:03 -04:00
Matt Nadareski
5b211a7345 Fix faulty offset dedupe logic 2024-10-08 21:53:06 -04:00
Matt Nadareski
ed4bd24fcb Include all DIC write offsets (fixes #750) 2024-10-08 15:47:24 -04:00
Matt Nadareski
8a7761753b Update to DIC 20241001 2024-10-01 22:44:41 -04:00
Matt Nadareski
1b8cca9999 Update changelog 2024-10-01 10:55:33 -04:00
TheRogueArchivist
b75391b1c6 Fix SafeDisc filtering (#749)
* Start updating filtering for SafeDisc

This will need more work, as it is currently incomplete, unoptimized, and untested.

* Further updates to SafeDisc Filtering

Still not done, but most if not all the major edge cases should be accounted for. Mostly just needs testing to make sure I didn't accidentally break something along the way, and further polishing of code and outputs.

* Further update to SafeDisc Filtering

More cleanly covers another specific case.

* Hopefully final main additions to SafeDisc filtering

* Update SafeDisc matching for newest BOS

Fix things that broke with the BOS update, and update a few comments.
2024-10-01 10:53:58 -04:00
Matt Nadareski
e9c2fd9245 Update BinaryObjectScanner to 3.1.14 2024-09-28 13:21:37 -04:00
Matt Nadareski
fb24bbd8a5 Update to DIC 20240901 2024-09-26 10:56:50 -04:00
Matt Nadareski
4e3083c8e6 Fix date 2024-09-24 14:11:02 -04:00
Matt Nadareski
05738b7c11 Bump version 2024-09-24 13:43:18 -04:00
Matt Nadareski
f963db67b1 Update changelog 2024-09-07 01:25:28 -04:00
TurnedToast
de64631c00 Add _drive.txt file to GetOutputFiles for UmdImageCreator (#748)
* add _drive.txt file to GetOutputFiles for UmdImageCreator

* remove required from _drive

* disc -> drive
2024-09-07 01:24:49 -04:00
Matt Nadareski
c8adef78c2 Update changelog 2024-09-06 22:53:02 -04:00
TurnedToast
7b116e7a04 Ensure manufacturer files starting from 0 are zipped in redumper DVD … (#747)
* Ensure manufacturer files starting from 0 are zipped in redumper DVD processing

* Remove extraneous DVD manufacturer/physical, add needed physical for bluray
2024-09-06 22:52:20 -04:00
Matt Nadareski
fb7b6ff1be Fix typo in publisher identifiers 2024-09-05 19:47:07 -04:00
Matt Nadareski
7fb8e44c31 Forgot to assume directories don't exist 2024-08-29 01:53:49 -04:00
Matt Nadareski
239ad4c4bc Handle XGD required files 2024-08-23 21:44:54 -04:00
Matt Nadareski
9834d0ea3e Fix access permissions of output file classes 2024-08-23 21:39:50 -04:00
Matt Nadareski
a35c13bd10 Add and use CustomOutputFile 2024-08-23 21:36:04 -04:00
Matt Nadareski
5e1777a7c7 Add future XGD output files 2024-08-23 21:24:12 -04:00
Matt Nadareski
66570300df Forgot the other locations 2024-08-23 21:20:55 -04:00
Matt Nadareski
4ac1fb201e Less confusing implmentation of DatfileExists 2024-08-23 21:19:07 -04:00
Matt Nadareski
cba8daa010 Add archive override for RegexOutputFile 2024-08-23 21:08:43 -04:00
Matt Nadareski
ba24a4b21a Create and use RegexOutputFile 2024-08-23 20:58:55 -04:00
Matt Nadareski
91c6fdac82 Use simplified CheckAllOutputFilesExist 2024-08-23 20:39:30 -04:00
Matt Nadareski
416656c457 Rename new method to CheckRequiredFiles 2024-08-23 20:29:48 -04:00
Matt Nadareski
fdd75818c4 Rename new method to 2024-08-23 20:29:36 -04:00
Matt Nadareski
ac302626c2 Add new, unused CheckAllOutputFilesExist variant 2024-08-23 20:27:58 -04:00
Matt Nadareski
428f3cc547 Minor tweaks to existing code 2024-08-23 19:47:10 -04:00
Matt Nadareski
66fc36fe3c Add runtime error for improperly created artifacts 2024-08-23 17:07:24 -04:00
Matt Nadareski
9dddf1c9b6 Use new func in Redumper 2024-08-23 16:49:27 -04:00
Matt Nadareski
5dbb955d26 Pass in new func for OutputFile 2024-08-23 16:42:04 -04:00
Matt Nadareski
2f2958bdea Add unused passable func to OutputFile 2024-08-23 16:32:33 -04:00
Matt Nadareski
c91f6ebbce Fix recursive issue in AddToArchive 2024-08-23 16:27:51 -04:00
Matt Nadareski
22fdd036eb Fix new AddToArchive methods 2024-08-23 16:23:45 -04:00
Matt Nadareski
3f12c6acb9 Rearrange some BaseProcessor methods 2024-08-23 16:17:22 -04:00
Matt Nadareski
1dbae18da6 Fix broken build 2024-08-23 13:51:10 -04:00
Matt Nadareski
6370e2dd6a Fix up some file path methods 2024-08-23 13:47:08 -04:00
Matt Nadareski
0c8879bc66 Split new output file methods 2024-08-23 13:34:55 -04:00
Matt Nadareski
6be34414fe Make GenerateArtifacts return a dictionary 2024-08-22 15:07:13 -04:00
Matt Nadareski
f15fc989c8 Add artifact keys for all relevant files 2024-08-22 15:03:36 -04:00
Matt Nadareski
0fc53cb534 Define new ArtifactKey field 2024-08-22 14:46:50 -04:00
Matt Nadareski
dc0909808a Replace GenerateArtifacts with common code 2024-08-22 14:43:09 -04:00
Matt Nadareski
00401d1282 Move GetLogFilePaths to better location 2024-08-22 14:27:12 -04:00
Matt Nadareski
b9d0d5d8f6 Replace GetLogFilePaths with common code 2024-08-22 14:26:43 -04:00
Matt Nadareski
22a6b77d27 Hook up GetOutputFiles in debug way 2024-08-22 14:22:37 -04:00
Matt Nadareski
bc4fe17fab Add unused GetOutputFiles method 2024-08-22 14:13:52 -04:00
Matt Nadareski
4b4027f285 Make helper class more robust 2024-08-22 13:18:47 -04:00
Matt Nadareski
d28257b2b7 Create currently-unused helper class 2024-08-22 12:57:07 -04:00
Matt Nadareski
669ef47f32 Start preparing for better output file checks 2024-08-22 12:27:00 -04:00
Matt Nadareski
be224800bc Remove redundant drive calls 2024-08-21 00:45:06 -04:00
Matt Nadareski
8dbb589d42 Create some PlayStation helper methods 2024-08-21 00:35:54 -04:00
Matt Nadareski
7b2fd5bf35 Fix minor inconsistencies 2024-08-20 23:28:25 -04:00
Matt Nadareski
95fa651074 Move MSXC parsing to PhysicalTool 2024-08-20 23:26:42 -04:00
Matt Nadareski
a0a155eb9b Preemptively update Redumper Saturn support 2024-08-20 23:12:01 -04:00
Matt Nadareski
72339b18df Remove GD-ROM version fallback method 2024-08-20 22:57:46 -04:00
Matt Nadareski
95c9c7706d Include serial for UMD (fixes #742) 2024-08-20 21:16:49 -04:00
Matt Nadareski
135bb43cdf Use new BEE method in code 2024-08-20 14:12:39 -04:00
Matt Nadareski
cfc75ca84d Move BEE method to better location 2024-08-20 14:08:51 -04:00
Matt Nadareski
33c35b63d7 Add bus encryption enabled method 2024-08-20 14:04:51 -04:00
Matt Nadareski
851a43d46f Rename 2 XGD helper methods 2024-08-20 13:53:09 -04:00
Matt Nadareski
a88bef481d Make GD-ROM LD code nicer to read 2024-08-20 13:01:46 -04:00
Matt Nadareski
781fec2b57 Futureproof GD-ROM LD in Redumper 2024-08-20 12:51:31 -04:00
Matt Nadareski
ee96367a45 Support GD-ROM info for Redumper (fixes #741) 2024-08-20 12:30:46 -04:00
Matt Nadareski
9f9bfc0888 Hash DMI and PFI files for XGD in Redumper 2024-08-18 13:53:49 -04:00
Matt Nadareski
c6cc697320 Prepare Redumper for XGD support 2024-08-18 13:40:39 -04:00
Deterous
5e3f7f740b Fix cleaning XGD3 SS (#740) 2024-08-18 10:41:44 -04:00
Matt Nadareski
e17a8f4708 Allow separate mounted path for Linux (fixes #739) 2024-08-16 20:25:39 -04:00
Matt Nadareski
ff4771a74a Quote input paths if needed (fixes #738) 2024-08-16 19:40:51 -04:00
Matt Nadareski
426717102d Add more verbose requirement to CLI help 2024-08-16 14:44:03 -04:00
Matt Nadareski
126bae33a4 Fix some CLI issues (fixes #736, fixes #737) 2024-08-16 14:29:42 -04:00
Matt Nadareski
11b8dd44bb Fix config location in OptionsLoader 2024-08-08 13:59:21 -04:00
Deterous
cbbb8aaa8c Fix XGD3 SS ranges (#733)
* Fix XGD3 SS ranges

* Changelog
2024-08-06 22:34:33 -04:00
Matt Nadareski
9ee7cd7fd7 Move two extensions to a better location 2024-08-06 20:47:52 -04:00
Matt Nadareski
324c1fcee3 Fix build for older .NET 2024-08-06 16:59:04 -04:00
Matt Nadareski
06776a6093 Add physical drive extensions to new tool 2024-08-06 16:52:37 -04:00
Matt Nadareski
43a079bb28 Fix usings ordering in ItemHelper 2024-08-06 16:18:39 -04:00
Matt Nadareski
d45345d338 Add comments around default options object 2024-08-06 14:18:08 -04:00
Matt Nadareski
1ff0340cae Add Check flags for protection scan extras 2024-08-06 14:16:44 -04:00
Matt Nadareski
278c86f9f4 Clean up some Check options, add IRD option 2024-08-06 14:09:05 -04:00
Matt Nadareski
00089b799c Bump version 2024-08-05 12:23:58 -04:00
Matt Nadareski
0f98f03999 Update changelog 2024-07-26 10:12:22 -04:00
Deterous
22f6f39a91 Enable loading seed JSON (#731)
* Enable loading seed JSON

* Safer function call
2024-07-26 10:11:39 -04:00
Matt Nadareski
e9a9011dbd Update RedumpLib to 1.4.1 2024-07-24 11:45:12 -04:00
Matt Nadareski
40bbb3d1c8 Update changelog 2024-07-19 10:55:55 -04:00
John Veness
a48dad817b Remove old --protect-file mentions (#728)
The --protect-file option itself was removed in e0482aad78
2024-07-19 10:55:23 -04:00
Matt Nadareski
9f76dcc5fd Add include artifacts flag for check, sanitize options (fixes #726) 2024-07-19 10:20:45 -04:00
Matt Nadareski
4356561a8a Update changelog 2024-07-19 10:16:30 -04:00
John Veness
347c522d62 Change to generic wording in report (#727) 2024-07-19 10:15:59 -04:00
Matt Nadareski
998ecec261 Remove RedumpLib tests 2024-07-16 13:08:28 -04:00
Matt Nadareski
ab8e775df0 Fix broken test logic 2024-07-16 13:03:58 -04:00
Matt Nadareski
246e6b8bfd Update changelog 2024-07-16 13:01:52 -04:00
John Veness
c7f69de18f Fix logic for deducing region from PlayStation ISN (#724)
* Deduce PS2 region from Redumper serial

* Fix logic for deducing region from PlayStation ISN

* Fix logic again

* Fix logic again again

* Fix this too

* Undo Redumper change

* Reorder functions

* Fix OR/AND logic
2024-07-16 13:00:11 -04:00
John Veness
d4a98d7712 Fix URLs in github issue templates (#717) 2024-07-15 16:42:01 -04:00
Deterous
2983266e8a Fix parameters after extension change (#721) 2024-07-06 23:48:34 -04:00
Deterous
1baef4440a Don't set MediaType if parameters ambiguous (#720) 2024-07-06 22:28:04 -04:00
Matt Nadareski
7cd25dae1c Remove now-unncessary names 2024-06-26 03:26:08 -04:00
Matt Nadareski
38f9b7234b Bring Check and CLI in parity with param processing 2024-06-26 03:24:44 -04:00
Matt Nadareski
2d7ea1bed9 Fix CLI help text alignment 2024-06-25 19:31:01 -04:00
Matt Nadareski
806a69c280 Simplify custom parameters warning 2024-06-25 17:31:38 -04:00
Matt Nadareski
a92159b8cb Try out custom options classes 2024-06-25 17:29:32 -04:00
Matt Nadareski
9451629461 Add some custom CLI parameters 2024-06-25 17:11:24 -04:00
Matt Nadareski
51a1f0cc8e Move some Check-specific methods 2024-06-25 16:48:24 -04:00
Matt Nadareski
2830641b8a Move GetDefaultSpeedForMediaType to common location 2024-06-25 16:42:23 -04:00
Matt Nadareski
7c87a22dcc Fix minimum number of args checks 2024-06-25 16:37:26 -04:00
Matt Nadareski
dd5b5d4c7d Use speed for CLI from configuration 2024-06-25 16:32:41 -04:00
Deterous
bc5e73d371 Custom theme colors (#712)
* Custom theme colors

* Use Convert.ToByte
2024-06-25 16:27:38 -04:00
Matt Nadareski
f47a55b723 Ensure tracks are assigned in Aaru 2024-06-24 15:00:13 -04:00
Matt Nadareski
c4d014e480 Add CLI status output on runtime 2024-06-24 14:22:15 -04:00
Matt Nadareski
69b1d2f7ad Blindly assume the path exists 2024-06-24 12:17:57 -04:00
Matt Nadareski
7c295ca2f4 Try to make config safer for CLI 2024-06-24 11:55:28 -04:00
Matt Nadareski
5af3aad68a Dispose of stream when creating config 2024-06-24 11:43:22 -04:00
Matt Nadareski
f150483e84 Load options before anything else 2024-06-24 11:38:13 -04:00
Matt Nadareski
92ef962f42 Allow custom parameters for CLI 2024-06-24 11:22:18 -04:00
Matt Nadareski
859e53f843 Save default config values for CLI 2024-06-24 11:04:48 -04:00
Matt Nadareski
e70d70ca22 Add CLI information to README 2024-06-24 10:46:40 -04:00
Matt Nadareski
c2cf8147d3 Add CLI build status to README 2024-06-24 10:38:45 -04:00
Matt Nadareski
cf32f38c0e Add preliminary MPF.CLI 2024-06-24 10:37:05 -04:00
Matt Nadareski
1f92ff08d6 Seal all theme classes 2024-06-24 09:45:04 -04:00
Matt Nadareski
6aaf076434 Separate themes into own namespace and files 2024-06-24 09:44:06 -04:00
Deterous
6865b23aa7 Purple (#711) 2024-06-23 21:46:15 -04:00
Matt Nadareski
e1c13982bd Remove empty gitmodules 2024-06-20 15:42:56 -04:00
Matt Nadareski
9cddcc2eae Update README with new build matricies 2024-06-20 14:40:42 -04:00
Matt Nadareski
33932fad47 Enable last runtime by default 2024-06-20 14:38:06 -04:00
Matt Nadareski
470e5c69fe Add flag values to script settings 2024-06-20 14:12:16 -04:00
Matt Nadareski
3aae2990a3 Better support build matricies 2024-06-20 13:56:28 -04:00
Matt Nadareski
503933e67f Add osx-arm64 to libraries 2024-06-20 12:21:28 -04:00
Matt Nadareski
1a99fd9e71 Forgot to upload packages to release 2024-06-20 12:10:04 -04:00
Matt Nadareski
affc175bda Fix nuget package naming 2024-06-20 12:06:09 -04:00
Matt Nadareski
fe4c88d3ad Add Linux ARM64 as target by default 2024-06-20 12:00:26 -04:00
Matt Nadareski
9c830c9755 Address build warnings for packages 2024-06-20 11:54:40 -04:00
Matt Nadareski
fc300465f8 Add nuget packing for processors and contexts 2024-06-20 11:51:28 -04:00
85 changed files with 6676 additions and 5802 deletions

View File

@@ -9,8 +9,8 @@ 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](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
- Remember to try the [latest WIP build](https://github.com/SabreTools/MPF/releases/tag/rolling) to see if the feature already exists.
- Is it copy protection related? If so, report the issue [here](https://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

@@ -9,8 +9,8 @@ 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](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
- Remember to try the [latest WIP build](https://github.com/SabreTools/MPF/releases/tag/rolling) to see if the feature already exists.
- Is it copy protection related? If so, report the issue [here](https://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

@@ -9,8 +9,8 @@ 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](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
- Remember to try the [latest WIP build](https://github.com/SabreTools/MPF/releases/tag/rolling) to see if the issue has already been addressed.
- Is it copy protection related? If so, report the issue [here](https://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.
@@ -25,8 +25,7 @@ What version are you using?
**Build**
What runtime version are you using?
- [ ] .NET 6.0 running on (Operating System)
- [ ] .NET 8.0 running on (Operating System)
- [ ] .NET 9.0 running on (Operating System)
**Describe the issue**
A clear and concise description of what the bug is.

View File

@@ -11,8 +11,9 @@ jobs:
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]
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, osx-arm64]
framework: [net9.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0, net9.0]
conf: [Debug] #[Release, Debug]
steps:
- uses: actions/checkout@v4
@@ -20,28 +21,30 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
dotnet-version: 9.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' || ''}}
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8') || startsWith(matrix.framework, 'net9')) && '-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/
run: |
cd ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
zip -r ${{ github.workspace }}/${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip ./
- 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: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True

53
.github/workflows/build_cli.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: MPF CLI
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
project: [MPF.CLI]
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, osx-arm64]
framework: [net9.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0, net9.0]
conf: [Debug] #[Release, Debug]
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.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 ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8') || startsWith(matrix.framework, 'net9')) && '-p:PublishSingleFile=true' || ''}}
- name: Archive build
run: |
cd ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
zip -r ${{ github.workspace }}/${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip ./
- name: Upload build
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

61
.github/workflows/build_nupkg.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: Nuget Pack
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Pack
run: dotnet pack
- name: Upload Execution Contexts package
uses: actions/upload-artifact@v4
with:
name: 'Execution Contexts Package'
path: 'MPF.ExecutionContexts/bin/Release/*.nupkg'
- name: Upload Execution Contexts to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: 'MPF.ExecutionContexts/bin/Release/*.nupkg'
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True
- name: Upload Processors package
uses: actions/upload-artifact@v4
with:
name: 'Processors Package'
path: 'MPF.Processors/bin/Release/*.nupkg'
- name: Upload Execution Contexts to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: 'MPF.Processors/bin/Release/*.nupkg'
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

View File

@@ -12,7 +12,8 @@ jobs:
matrix:
project: [MPF.UI]
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]
framework: [net9.0-windows] #[net40, net452, net472, net48, netcoreapp3.1, net5.0-windows, net6.0-windows, net7.0-windows, net8.0-windows, net9.0-windows]
conf: [Debug] #[Release, Debug]
steps:
- uses: actions/checkout@v4
@@ -20,42 +21,44 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
dotnet-version: 9.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' || ''}}
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ matrix.conf == 'Release' && 'Release -p:DebugType=None -p:DebugSymbols=false' || 'Debug'}} --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8') || startsWith(matrix.framework, 'net9')) && '-p:PublishSingleFile=true' || ''}}
- name: Bundle DiscImageCreator
run: |
wget https://github.com/user-attachments/files/15521936/DiscImageCreator_20240601.zip
unzip -u DiscImageCreator_20240601.zip
mkdir -p MPF.UI/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Creator
mv Release_ANSI/* MPF.UI/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Creator/
wget https://github.com/user-attachments/files/17211434/DiscImageCreator_20241001.zip
unzip -u DiscImageCreator_20241001.zip
mkdir -p MPF.UI/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Creator
mv Release_ANSI/* MPF.UI/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Creator/
- name: Bundle Redumper
run: |
wget https://github.com/superg/redumper/releases/download/build_371/redumper-2024.05.27_build371-win64.zip
unzip redumper-2024.05.27_build371-win64.zip
mkdir -p MPF.UI/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper
mv redumper-2024.05.27_build371-win64/bin/redumper.exe MPF.UI/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper/
wget https://github.com/superg/redumper/releases/download/build_438/redumper-2024.11.03_build438-win64.zip
unzip redumper-2024.11.03_build438-win64.zip
mkdir -p MPF.UI/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper
mv redumper-2024.11.03_build438-win64/bin/redumper.exe MPF.UI/bin/${{ matrix.conf }}/${{ 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/
run: |
cd ${{ matrix.project }}/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
zip -r ${{ github.workspace }}/${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip ./
- 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: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_${{ matrix.conf }}.zip
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True

View File

@@ -11,7 +11,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore

0
.gitmodules vendored
View File

2
.vscode/launch.json vendored
View File

@@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/MPF.Check/bin/Debug/net8.0/MPF.Check.dll",
"program": "${workspaceFolder}/MPF.Check/bin/Debug/net9.0/MPF.Check.dll",
"args": [],
"cwd": "${workspaceFolder}/MPF.Check",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console

View File

@@ -1,3 +1,200 @@
### 3.2.4 (2024-11-24)
- Update Redumper to build 438
- Be smarter about Linq usage
- Add .NET 9 to target frameworks
- Be smarter about some data types
- Consolidate parameter string splitting
- Fix fully matching when multiple matches found
- Remove usages of `this.` in more places
- Add `skeleton` to Redumper CD by default
- Add unused "Generic" processor
- Add Track 0/00/A/AA to Generic
- Add IMA extension for floppy disks
- Remove usages of `this.` from themes
- Fix PIC parser
- Use expanded version of split regex
- Update packages for bugfixes
- Fix minor efficiency issues
- Remove usages of `this.` from UI code
- Reduce unnecessary use of IEnumerable
- Use List in protection formatting
- Don't preemptively sort protections
- Remove unncessary .NET Framework 4.0 gating
- Improve parameters for default output path
- Replace some uses of Regex.Replace
- Clean up usings after last commit
- Version gate remaining Linq statements
- Cleanup CleanSS check
- Fix PS3 firmware version being omitted
- Don't add trailing spaces to BCA output
- Add support for Redumper Xbox dumps
### 3.2.3 (2024-11-06)
- Update to DIC 20240901
- Update BinaryObjectScanner to 3.1.14
- Fix SafeDisc filtering (TheRogueArchivist)
- Update to DIC 20241001
- Include all DIC write offsets
- Fix faulty offset dedupe logic
- Change multiple offset delimiter
- Make .NET 8 the default in issue reports
- Remove ReadLine in list commands
- Sum track errors in Redumper
- Format CleanRip BCA wtih 2-byte blocks
- Update Redumper to build 416
- Fix trimming of header output
- Use fake filename for Redumper DAT
- Ensure that the full base path is being used
- Ensure consistency in output file path checking
- Use new output file logic in processors
- Ensure Regex directories are unescaped
- Add separate field for Regex; assorted cleanup
- Reduce cleverness in output file code
- No directory means no files
- Update RedumpLib to 1.4.4
- Use rolling release, not AppVeyor, in issue templates
- Update BinaryObjectScanner to 3.1.15
- Remove unused IndexRange library
- Remove unnecessary System.ValueTuple usage
- Remove old .NET version of ValueTuple where possible
- Remove unused CompilerServices.Unsafe library
- Simplify GetMediaType return
- Reduce call complexity for login result
- Use new ProtectionDictionary type
- Replace user info func with ProcessUserInfoDelegate
- Reduce tupling even more
- Remove tupling from everything except CLI programs
- Remove last instances of ValueTuple usage
- Update BinaryObjectScanner to 3.1.16
- Attempt to reduce nesting in GHA builds
- Add conf to build matrix
- Ensure debug symbols are stripped
- Fix missed GetOutputFiles invocation
- Reduce null use in BaseProcessor
- Force rebuild of rolling
### 3.2.2 (2024-09-24)
- Clean up some Check options, add IRD option
- Add Check flags for protection scan extras
- Add comments around default options object
- Fix usings ordering in ItemHelper
- Add physical drive extensions to new tool
- Fix build for older .NET
- Move two extensions to a better location
- Fix XGD3 SS ranges
- Fix config location in OptionsLoader
- Fix some CLI issues
- Add more verbose requirement to CLI help
- Quote input paths if needed
- Allow separate mounted path for Linux
- Fix cleaning XGD3 SS
- Prepare Redumper for XGD support
- Hash DMI and PFI files for XGD in Redumper
- Support GD-ROM info for Redumper
- Futureproof GD-ROM LD in Redumper
- Make GD-ROM LD code nicer to read
- Rename 2 XGD helper methods
- Add bus encryption enabled method
- Move BEE method to better location
- Use new BEE method in code
- Include serial for UMD
- Remove GD-ROM version fallback method
- Preemptively update Redumper Saturn support
- Move MSXC parsing to PhysicalTool
- Fix minor inconsistencies
- Create some PlayStation helper methods
- Remove redundant drive calls
- Start preparing for better output file checks
- Create currently-unused helper class
- Make helper class more robust
- Add unused GetOutputFiles method
- Hook up GetOutputFiles in debug way
- Replace GetLogFilePaths with common code
- Move GetLogFilePaths to better location
- Replace GenerateArtifacts with common code
- Define new ArtifactKey field
- Add artifact keys for all relevant files
- Make GenerateArtifacts return a dictionary
- Split new output file methods
- Fix up some file path methods
- Fix broken build
- Rearrange some BaseProcessor methods
- Fix new AddToArchive methods
- Fix recursive issue in AddToArchive
- Add unused passable func to OutputFile
- Pass in new func for OutputFile
- Use new func in Redumper
- Add runtime error for improperly created artifacts
- Minor tweaks to existing code
- Add new, unused CheckAllOutputFilesExist variant
- Rename new method to CheckRequiredFiles
- Use simplified CheckAllOutputFilesExist
- Create and use RegexOutputFile
- Add archive override for RegexOutputFile
- Less confusing implmentation of DatfileExists
- Forgot the other locations
- Add future XGD output files
- Add and use CustomOutputFile
- Fix access permissions of output file classes
- Handle XGD required files
- Forgot to assume directories don't exist
- Fix typo in publisher identifiers
- Ensure manufacturer files starting from 0 are zipped in redumper DVD (TurnedToast)
- Add _drive.txt file to GetOutputFiles for UmdImageCreator (TurnedToast)
### 3.2.1 (2024-08-05)
- Add nuget packing for processors and contexts
- Address build warnings for packages
- Add Linux ARM64 as target by default
- Fix nuget package naming
- Forgot to upload packages to release
- Add `osx-arm64` to libraries
- Better support build matricies
- Show script settings
- Add flag values to script settings
- Enable last runtime by default
- Update README with new build matricies
- Remove empty gitmodules
- Purple
- Separate themes into own namespace and files
- Seal all theme classes
- Add preliminary MPF.CLI
- Add CLI build status to README
- Add CLI information to README
- Save default config values for CLI
- Allow custom parameters for CLI
- Load options before anything else
- Dispose of stream when creating config
- Try to make config safer for CLI
- Blindly assume the path exists
- Add CLI status output on runtime
- Ensure tracks are assigned in Aaru
- Custom theme colors
- Use speed for CLI from configuration
- Fix minimum number of args checks
- Move GetDefaultSpeedForMediaType to common location
- Move some Check-specific methods
- Add some custom CLI parameters
- Try out custom options classes
- Simplify custom parameters warning
- Fix CLI help text alignment
- Bring Check and CLI in parity with param processing
- Remove now-unncessary names
- Don't set MediaType if parameters ambiguous
- Fix parameters after extension change
- Fix logic for deducing region from PlayStation ISN (JohnVeness)
- Fix broken test logic
- Remove RedumpLib tests
- Change to generic wording in report (JohnVeness)
- Add include artifacts flag for check, sanitize options
- Remove old --protect-file mentions (JohnVeness)
- Update RedumpLib to 1.4.1
- Enable loading seed JSON (Deterous)
### 3.2.0 (2024-06-20)
- Create currently-unused processors

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

@@ -0,0 +1,25 @@
using System;
using BinaryObjectScanner;
using MPF.Frontend;
namespace MPF.CLI
{
public static class ConsoleLogger
{
/// <summary>
/// Simple process counter to write to console
/// </summary>
public static void ProgressUpdated(object? sender, ResultEventArgs 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}");
}
}
}

55
MPF.CLI/MPF.CLI.csproj Normal file
View File

@@ -0,0 +1,55 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<NoWarn>NU1902;NU1903</NoWarn>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.2.4</VersionPrefix>
<!-- Package Properties -->
<Title>MPF CLI</Title>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>CLI frontend 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>
<!-- Support All Frameworks -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.2.3" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.5.2" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
</Project>

345
MPF.CLI/Program.cs Normal file
View File

@@ -0,0 +1,345 @@
using System;
using System.IO;
#if NET40
using System.Threading.Tasks;
#endif
using BinaryObjectScanner;
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
namespace MPF.CLI
{
public class Program
{
public static void Main(string[] args)
{
// Load options from the config file
var options = OptionsLoader.LoadFromConfig();
if (options.FirstRun)
{
// Application paths
options.AaruPath = "FILL ME IN";
options.DiscImageCreatorPath = "FILL ME IN";
options.RedumperPath = "FILL ME IN";
options.InternalProgram = InternalProgram.NONE;
// Reset first run
options.FirstRun = false;
OptionsLoader.SaveToConfig(options, saveDefault: true);
}
// Try processing the standalone arguments
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
if (standaloneProcessed != false)
{
if (standaloneProcessed == null)
DisplayHelp();
return;
}
// Try processing the common arguments
bool success = OptionsLoader.ProcessCommonArguments(args, out MediaType mediaType, out RedumpSystem? knownSystem, out var error);
if (!success)
{
DisplayHelp(error);
return;
}
// Validate the supplied credentials
bool? validated = RedumpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).GetAwaiter().GetResult();
string message = validated switch
{
true => "Redump username and password accepted!",
false => "Redump username and password denied!",
null => "An error occurred validating your credentials!",
};
if (!string.IsNullOrEmpty(message))
Console.WriteLine(message);
// Process any custom parameters
int startIndex = 2;
CommandOptions opts = LoadFromArguments(args, options, ref startIndex);
// Validate the internal program
switch (options.InternalProgram)
{
case InternalProgram.Aaru:
if (!File.Exists(options.AaruPath))
{
DisplayHelp("A path needs to be supplied for Aaru, exiting...");
return;
}
break;
case InternalProgram.DiscImageCreator:
if (!File.Exists(options.DiscImageCreatorPath))
{
DisplayHelp("A path needs to be supplied for DIC, exiting...");
return;
}
break;
case InternalProgram.Redumper:
if (!File.Exists(options.RedumperPath))
{
DisplayHelp("A path needs to be supplied for Redumper, exiting...");
return;
}
break;
default:
DisplayHelp($"{options.InternalProgram} is not a supported dumping program, exiting...");
break;
}
// Ensure we have the values we need
if (opts.CustomParams == null && (opts.DevicePath == null || opts.FilePath == null))
{
DisplayHelp("Both a device path and file path need to be supplied, exiting...");
return;
}
// Get the speed from the options
int speed = opts.DriveSpeed ?? FrontendTool.GetDefaultSpeedForMediaType(mediaType, options);
// Populate an environment
var drive = Drive.Create(null, opts.DevicePath ?? string.Empty);
var env = new DumpEnvironment(options,
opts.FilePath,
drive,
knownSystem,
mediaType,
options.InternalProgram,
parameters: null);
// Process the parameters
string? paramStr = opts.CustomParams ?? env.GetFullParameters(speed);
if (string.IsNullOrEmpty(paramStr))
{
DisplayHelp("No valid environment could be created, exiting...");
return;
}
env.SetExecutionContext(paramStr);
// Make new Progress objects
var resultProgress = new Progress<ResultEventArgs>();
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
// Invoke the dumping program
Console.WriteLine($"Invoking {options.InternalProgram} using '{paramStr}'");
var dumpResult = env.Run(resultProgress).GetAwaiter().GetResult();
Console.WriteLine(dumpResult.Message);
if (!dumpResult)
return;
// If it was not a dumping command
if (!env.IsDumpingCommand())
{
Console.WriteLine("Execution not recognized as dumping command, skipping processing...");
return;
}
// If we have a mounted path, replace the environment
if (opts.MountedPath != null && Directory.Exists(opts.MountedPath))
{
drive = Drive.Create(null, opts.MountedPath);
env = new DumpEnvironment(options,
opts.FilePath,
drive,
knownSystem,
mediaType,
internalProgram: null,
parameters: null);
}
// Finally, attempt to do the output dance
var verifyResult = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress)
.ConfigureAwait(false).GetAwaiter().GetResult();
Console.WriteLine(verifyResult.Message);
}
/// <summary>
/// Display help for MPF.CLI
/// </summary>
/// <param name="error">Error string to prefix the help text with</param>
private static void DisplayHelp(string? error = null)
{
if (error != null)
Console.WriteLine(error);
Console.WriteLine("Usage:");
Console.WriteLine("MPF.CLI <mediatype> <system> [options]");
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");
Console.WriteLine();
Console.WriteLine("CLI Options:");
Console.WriteLine("-u, --use <program> Override default dumping program");
Console.WriteLine("-d, --device <devicepath> Physical drive path (Required if no custom parameters set)");
Console.WriteLine("-m, --mounted <dirpath> Mounted filesystem path for additional checks");
Console.WriteLine("-f, --file \"<filepath>\" Output file path (Required if no custom parameters set)");
Console.WriteLine("-s, --speed <speed> Override default dumping speed");
Console.WriteLine("-c, --custom \"<params>\" Custom parameters to use");
Console.WriteLine();
Console.WriteLine("Custom dumping parameters, if used, will fully replace the default parameters.");
Console.WriteLine("All dumping parameters need to be supplied if doing this.");
Console.WriteLine("Otherwise, both a drive path and output file path are required.");
Console.WriteLine();
Console.WriteLine("Mounted filesystem path is only recommended on OSes that require block");
Console.WriteLine("device dumping, usually Linux and macOS.");
Console.WriteLine();
}
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
private static CommandOptions LoadFromArguments(string[] args, Frontend.Options options, ref int startIndex)
{
// Create return values
var opts = new CommandOptions();
// If we have no arguments, just return
if (args == null || args.Length == 0)
{
startIndex = 0;
return opts;
}
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
return opts;
// 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 = Frontend.Options.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
startIndex++;
}
// Use a device path
else if (args[startIndex].StartsWith("-d=") || args[startIndex].StartsWith("--device="))
{
opts.DevicePath = args[startIndex].Split('=')[1].Trim('"');
}
else if (args[startIndex] == "-d" || args[startIndex] == "--device")
{
opts.DevicePath = args[startIndex + 1].Trim('"');
startIndex++;
}
// Use a mounted path for physical checks
else if (args[startIndex].StartsWith("-m=") || args[startIndex].StartsWith("--mounted="))
{
opts.MountedPath = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-m" || args[startIndex] == "--mounted")
{
opts.MountedPath = args[startIndex + 1];
startIndex++;
}
// Use a file path
else if (args[startIndex].StartsWith("-f=") || args[startIndex].StartsWith("--file="))
{
opts.FilePath = args[startIndex].Split('=')[1].Trim('"');
}
else if (args[startIndex] == "-f" || args[startIndex] == "--file")
{
opts.FilePath = args[startIndex + 1].Trim('"');
startIndex++;
}
// Set an override speed
else if (args[startIndex].StartsWith("-s=") || args[startIndex].StartsWith("--speed="))
{
if (!int.TryParse(args[startIndex].Split('=')[1].Trim('"'), out int speed))
speed = -1;
opts.DriveSpeed = speed;
}
else if (args[startIndex] == "-s" || args[startIndex] == "--speed")
{
if (!int.TryParse(args[startIndex + 1].Trim('"'), out int speed))
speed = -1;
opts.DriveSpeed = speed;
startIndex++;
}
// Use a custom parameters
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--custom="))
{
opts.CustomParams = args[startIndex].Split('=')[1].Trim('"');
}
else if (args[startIndex] == "-c" || args[startIndex] == "--custom")
{
opts.CustomParams = args[startIndex + 1].Trim('"');
startIndex++;
}
// Default, we fall out
else
{
break;
}
}
return opts;
}
/// <summary>
/// Represents commandline options
/// </summary>
private class CommandOptions
{
/// <summary>
/// Path to the device to dump
/// </summary>
/// <remarks>Required if custom parameters are not set</remarks>
public string? DevicePath { get; set; } = null;
/// <summary>
/// Path to the mounted filesystem to check
/// </summary>
/// <remarks>Should only be used when the device path is not readable</remarks>
public string? MountedPath { get; set; } = null;
/// <summary>
/// Path to the output file
/// </summary>
/// <remarks>Required if custom parameters are not set</remarks>
public string? FilePath { get; set; } = null;
/// <summary>
/// Override drive speed
/// </summary>
public int? DriveSpeed { get; set; } = null;
/// <summary>
/// Custom parameters for dumping
/// </summary>
public string? CustomParams { get; set; } = null;
}
}
}

View File

@@ -1,45 +1,59 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- 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>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.2.0</VersionPrefix>
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<NoWarn>NU1902;NU1903</NoWarn>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.2.4</VersionPrefix>
<!-- 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>
<!-- 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>
<None Include="App.config" />
</ItemGroup>
<!-- Support All Frameworks -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`))">
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
</PropertyGroup>
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.13" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.2.3" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.5.2" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
</Project>

View File

@@ -1,8 +1,12 @@
using System;
using System.IO;
#if NET40
using System.Threading.Tasks;
#endif
using BinaryObjectScanner;
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
@@ -12,6 +16,34 @@ namespace MPF.Check
{
public static void Main(string[] args)
{
// Create a default options object
var options = new Frontend.Options()
{
// Internal Program
InternalProgram = InternalProgram.NONE,
// Extra Dumping Options
ScanForProtection = false,
AddPlaceholders = true,
PullAllInformation = false,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
IncludeArtifacts = false,
CompressLogFiles = false,
DeleteUnnecessaryFiles = false,
CreateIRDAfterDumping = false,
// Protection Scanning Options
ScanArchivesForProtection = true,
ScanPackersForProtection = false,
IncludeDebugProtectionInformation = false,
HideDriveLetters = false,
// Redump Login Information
RedumpUsername = null,
RedumpPassword = null,
};
// Try processing the standalone arguments
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
if (standaloneProcessed != false)
@@ -22,7 +54,7 @@ namespace MPF.Check
}
// Try processing the common arguments
(bool success, MediaType mediaType, RedumpSystem? knownSystem, var error) = OptionsLoader.ProcessCommonArguments(args);
bool success = OptionsLoader.ProcessCommonArguments(args, out MediaType mediaType, out RedumpSystem? knownSystem, out var error);
if (!success)
{
DisplayHelp(error);
@@ -30,7 +62,8 @@ namespace MPF.Check
}
// Loop through and process options
(var options, var seedInfo, var path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
int startIndex = 2;
CommandOptions opts = LoadFromArguments(args, options, ref startIndex);
if (options.InternalProgram == InternalProgram.NONE)
{
DisplayHelp("A program name needs to be provided");
@@ -44,11 +77,14 @@ namespace MPF.Check
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
// Validate the supplied credentials
#if NETFRAMEWORK
(bool? _, string? message) = RedumpWebClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty);
#else
(bool? _, string? message) = RedumpHttpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
bool? validated = RedumpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).GetAwaiter().GetResult();
string message = validated switch
{
true => "Redump username and password accepted!",
false => "Redump username and password denied!",
null => "An error occurred validating your credentials!",
};
if (!string.IsNullOrEmpty(message))
Console.WriteLine(message);
@@ -67,19 +103,14 @@ namespace MPF.Check
// Now populate an environment
Drive? drive = null;
if (!string.IsNullOrEmpty(path))
drive = Drive.Create(null, path!);
if (!string.IsNullOrEmpty(opts.DevicePath))
drive = Drive.Create(null, opts.DevicePath!);
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
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress, seedInfo: opts.Seed)
.ConfigureAwait(false).GetAwaiter().GetResult();
Console.WriteLine(result.Message);
}
}
@@ -94,7 +125,7 @@ namespace MPF.Check
Console.WriteLine(error);
Console.WriteLine("Usage:");
Console.WriteLine("MPF.Check.exe <mediatype> <system> [options] </path/to/output.cue/iso> ...");
Console.WriteLine("MPF.Check <mediatype> <system> [options] </path/to/output.cue/iso> ...");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
@@ -105,12 +136,215 @@ namespace MPF.Check
Console.WriteLine();
Console.WriteLine("Check Options:");
var supportedArguments = OptionsLoader.PrintSupportedArguments();
foreach (string argument in supportedArguments)
{
Console.WriteLine(argument);
}
Console.WriteLine("-u, --use <program> Dumping program output type [REQUIRED]");
Console.WriteLine(" --load-seed <path> Load a seed submission JSON for user information");
Console.WriteLine(" --no-placeholders Disable placeholder values in submission info");
Console.WriteLine(" --create-ird Create IRD from output files (PS3 only)");
Console.WriteLine("-c, --credentials <user> <pw> Redump username and password");
Console.WriteLine(" --pull-all Pull all information from Redump (requires --credentials)");
Console.WriteLine("-p, --path <drivepath> Physical drive path for additional checks");
Console.WriteLine("-s, --scan Enable copy protection scan (requires --path)");
Console.WriteLine(" --disable-archives Disable scanning archives (requires --scan)");
Console.WriteLine(" --enable-packers Enable scanning for packers (requires --scan)");
Console.WriteLine(" --enable-debug Enable debug protection information (requires --scan)");
Console.WriteLine(" --hide-drive-letters Hide drive letters from scan output (requires --scan)");
Console.WriteLine("-x, --suffix Enable adding filename suffix");
Console.WriteLine("-j, --json Enable submission JSON output");
Console.WriteLine(" --include-artifacts Include artifacts in JSON (requires --json)");
Console.WriteLine("-z, --zip Enable log file compression");
Console.WriteLine("-d, --delete Enable unnecessary file deletion");
Console.WriteLine();
}
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
private static CommandOptions LoadFromArguments(string[] args, Frontend.Options options, ref int startIndex)
{
// Create return values
var opts = new CommandOptions();
// These values require multiple parts to be active
bool scan = false,
enableArchives = true,
enablePackers = false,
enableDebug = false,
hideDriveLetters = false;
// If we have no arguments, just return
if (args == null || args.Length == 0)
{
startIndex = 0;
return opts;
}
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
return opts;
// 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 = Frontend.Options.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
startIndex++;
}
// Include seed info file
else if (args[startIndex].StartsWith("--load-seed="))
{
string seedInfo = args[startIndex].Split('=')[1];
opts.Seed = Builder.CreateFromFile(seedInfo);
}
else if (args[startIndex] == "--load-seed")
{
string seedInfo = args[startIndex + 1];
opts.Seed = Builder.CreateFromFile(seedInfo);
startIndex++;
}
// Disable placeholder values in submission info
else if (args[startIndex].Equals("--no-placeholders"))
{
options.AddPlaceholders = false;
}
// Create IRD from output files (PS3 only)
else if (args[startIndex].Equals("--create-ird"))
{
options.CreateIRDAfterDumping = true;
}
// Redump login
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
{
string[] credentials = args[startIndex].Split('=')[1].Split(';');
options.RedumpUsername = credentials[0];
options.RedumpPassword = credentials[1];
}
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
{
options.RedumpUsername = args[startIndex + 1];
options.RedumpPassword = args[startIndex + 2];
startIndex += 2;
}
// Pull all information (requires Redump login)
else if (args[startIndex].Equals("--pull-all"))
{
options.PullAllInformation = true;
}
// Use a device path for physical checks
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
{
opts.DevicePath = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
{
opts.DevicePath = args[startIndex + 1];
startIndex++;
}
// Scan for protection (requires device path)
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
{
scan = true;
}
// Disable scanning archives (requires --scan)
else if (args[startIndex].Equals("--disable-archives"))
{
enableArchives = false;
}
// Enable scanning for packers (requires --scan)
else if (args[startIndex].Equals("--enable-packers"))
{
enablePackers = true;
}
// Enable debug protection information (requires --scan)
else if (args[startIndex].Equals("--enable-debug"))
{
enableDebug = true;
}
// Hide drive letters from scan output (requires --scan)
else if (args[startIndex].Equals("--hide-drive-letters"))
{
hideDriveLetters = true;
}
// 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"))
{
options.OutputSubmissionJSON = true;
}
// Output submission JSON
else if (args[startIndex].Equals("--include-artifacts"))
{
options.IncludeArtifacts = true;
}
// Compress log and extraneous files
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
{
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
{
break;
}
}
// Now deal with the complex options
options.ScanForProtection = scan && !string.IsNullOrEmpty(opts.DevicePath);
options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(opts.DevicePath);
options.ScanPackersForProtection = enablePackers && scan && !string.IsNullOrEmpty(opts.DevicePath);
options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(opts.DevicePath);
options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(opts.DevicePath);
return opts;
}
/// <summary>
/// Represents commandline options
/// </summary>
private class CommandOptions
{
/// <summary>
/// Seed submission info from an input file
/// </summary>
public SubmissionInfo? Seed { get; set; } = null;
/// <summary>
/// Path to the device to scan
/// </summary>
public string? DevicePath { get; set; } = null;
}
}
}

View File

@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using SabreTools.RedumpLib.Data;
namespace MPF.ExecutionContexts.Aaru
@@ -14,10 +12,10 @@ namespace MPF.ExecutionContexts.Aaru
#region Generic Dumping Information
/// <inheritdoc/>
public override string? InputPath => InputValue;
public override string? InputPath => InputValue?.Trim('"');
/// <inheritdoc/>
public override string? OutputPath => OutputValue;
public override string? OutputPath => OutputValue?.Trim('"');
/// <inheritdoc/>
public override int? Speed
@@ -116,7 +114,12 @@ namespace MPF.ExecutionContexts.Aaru
public ExecutionContext(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public ExecutionContext(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Dictionary<string, string?> options)
public ExecutionContext(RedumpSystem? system,
MediaType? type,
string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
@@ -1082,7 +1085,11 @@ namespace MPF.ExecutionContexts.Aaru
if (string.IsNullOrEmpty(InputValue))
return null;
parameters.Add(InputValue!.TrimEnd('\\'));
if (InputValue!.Contains(" "))
parameters.Add($"\"{InputValue!.TrimEnd('\\')}\"");
else
parameters.Add(InputValue!.TrimEnd('\\'));
break;
// Two input values
@@ -1189,7 +1196,10 @@ namespace MPF.ExecutionContexts.Aaru
}
/// <inheritdoc/>
protected override void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Dictionary<string, string?> options)
protected override void SetDefaultParameters(string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options)
{
BaseCommand = $"{CommandStrings.MediaPrefixLong} {CommandStrings.MediaDump}";
@@ -1203,8 +1213,8 @@ namespace MPF.ExecutionContexts.Aaru
}
// First check to see if the combination of system and MediaType is valid
var validTypes = this.System.MediaTypes();
if (!validTypes.Contains(this.Type))
var validTypes = RedumpSystem.MediaTypes();
if (!validTypes.Contains(MediaType))
return;
// Set retry count
@@ -1227,40 +1237,40 @@ namespace MPF.ExecutionContexts.Aaru
// TODO: Look at dump-media formats and the like and see what options there are there to fill in defaults
// Now sort based on disc type
switch (this.Type)
switch (MediaType)
{
case MediaType.CDROM:
case SabreTools.RedumpLib.Data.MediaType.CDROM:
// Currently no defaults set
break;
case MediaType.DVD:
case SabreTools.RedumpLib.Data.MediaType.DVD:
this[FlagStrings.StoreEncryptedLong] = true; // TODO: Make this configurable
this[FlagStrings.TitleKeysLong] = false; // TODO: Make this configurable
this[FlagStrings.TrimLong] = true; // TODO: Make this configurable
break;
case MediaType.GDROM:
case SabreTools.RedumpLib.Data.MediaType.GDROM:
// Currently no defaults set
break;
case MediaType.HDDVD:
case SabreTools.RedumpLib.Data.MediaType.HDDVD:
this[FlagStrings.StoreEncryptedLong] = true; // TODO: Make this configurable
this[FlagStrings.TitleKeysLong] = false; // TODO: Make this configurable
this[FlagStrings.TrimLong] = true; // TODO: Make this configurable
break;
case MediaType.BluRay:
case SabreTools.RedumpLib.Data.MediaType.BluRay:
this[FlagStrings.StoreEncryptedLong] = true; // TODO: Make this configurable
this[FlagStrings.TitleKeysLong] = false; // TODO: Make this configurable
this[FlagStrings.TrimLong] = true; // TODO: Make this configurable
break;
// Special Formats
case MediaType.NintendoGameCubeGameDisc:
case SabreTools.RedumpLib.Data.MediaType.NintendoGameCubeGameDisc:
// Currently no defaults set
break;
case MediaType.NintendoWiiOpticalDisc:
case SabreTools.RedumpLib.Data.MediaType.NintendoWiiOpticalDisc:
// Currently no defaults set
break;
// Non-optical
case MediaType.FloppyDisk:
case SabreTools.RedumpLib.Data.MediaType.FloppyDisk:
// Currently no defaults set
break;
}
@@ -1276,19 +1286,14 @@ namespace MPF.ExecutionContexts.Aaru
return false;
// Now split the string into parts for easier validation
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
parameters = parameters!.Trim();
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
.Cast<Match>()
.Select(m => m.Value)
.ToList();
List<string> parts = SplitParameterString(parameters!);
// Search for pre-command flags first
int start = 0;
for (start = 0; start < parts.Count; start++)
{
// Keep a count of keys to determine if we should break out to command handling or not
int keyCount = Keys.Count();
int keyCount = Keys.Count;
// Debug
ProcessBooleanParameter(parts, FlagStrings.DebugShort, FlagStrings.DebugLong, ref start, true);
@@ -1307,7 +1312,7 @@ namespace MPF.ExecutionContexts.Aaru
ProcessBooleanParameter(parts, FlagStrings.HelpShortAlt, FlagStrings.HelpLong, ref start, true);
// If we didn't add any new flags, break out since we might be at command handling
if (keyCount == Keys.Count())
if (keyCount == Keys.Count)
break;
}
@@ -1335,7 +1340,7 @@ namespace MPF.ExecutionContexts.Aaru
string? stringValue = null;
// Keep a count of keys to determine if we should break out to filename handling or not
int keyCount = Keys.Count();
int keyCount = Keys.Count;
#region Boolean flags
@@ -1702,7 +1707,7 @@ namespace MPF.ExecutionContexts.Aaru
#endregion
// If we didn't add any new flags, break out since we might be at filename handling
if (keyCount == Keys.Count())
if (keyCount == Keys.Count)
break;
}

View File

@@ -20,7 +20,7 @@ namespace MPF.ExecutionContexts
/// Set of flags to pass to the executable
/// </summary>
protected Dictionary<string, bool?> flags = [];
protected internal IEnumerable<string> Keys => flags.Keys;
protected internal List<string> Keys => [.. flags.Keys];
/// <summary>
/// Safe access to currently set flags
@@ -82,12 +82,12 @@ namespace MPF.ExecutionContexts
/// <summary>
/// Currently represented system
/// </summary>
public RedumpSystem? System { get; set; }
public RedumpSystem? RedumpSystem { get; set; }
/// <summary>
/// Currently represented media type
/// </summary>
public MediaType? Type { get; set; }
public MediaType? MediaType { get; set; }
#endregion
@@ -111,10 +111,15 @@ namespace MPF.ExecutionContexts
/// <param name="filename">Filename to use</param>
/// <param name="driveSpeed">Drive speed to use</param>
/// <param name="options">Dictionary object containing all settings that may be used for setting parameters</param>
public BaseExecutionContext(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Dictionary<string, string?> options)
public BaseExecutionContext(RedumpSystem? system,
MediaType? type,
string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options)
{
this.System = system;
this.Type = type;
RedumpSystem = system;
MediaType = type;
SetDefaultParameters(drivePath, filename, driveSpeed, options);
}
@@ -169,7 +174,10 @@ namespace MPF.ExecutionContexts
/// <param name="filename">Filename to use</param>
/// <param name="driveSpeed">Drive speed to use</param>
/// <param name="options">Dictionary containing all settings that may be used for setting parameters</param>
protected abstract void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Dictionary<string, string?> options);
protected abstract void SetDefaultParameters(string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options);
/// <summary>
/// Scan a possible parameter string and populate whatever possible
@@ -290,6 +298,24 @@ namespace MPF.ExecutionContexts
#region Parameter Parsing
/// <summary>
/// Split a parameters string into a list while taking quotes into account
/// </summary>
/// <see href="https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes"/>
protected static List<string> SplitParameterString(string parameters)
{
// Ensure the parameter string is trimmed
parameters = parameters.Trim();
// Split the string using Regex
var matches = Regex.Matches(parameters, @"([a-zA-Z\-]*=)?[\""].+?[\""]|[^ ]+", RegexOptions.Compiled);
// Get just the values from the matches
var matchArr = new Match[matches.Count];
matches.CopyTo(matchArr, 0);
return [.. Array.ConvertAll(matchArr, m => m.Value)];
}
/// <summary>
/// Returns whether or not the selected item exists
/// </summary>
@@ -308,21 +334,13 @@ namespace MPF.ExecutionContexts
{
if (CommandSupport == null)
return false;
if (this.BaseCommand == null)
if (BaseCommand == null)
return false;
if (!CommandSupport.TryGetValue(this.BaseCommand, out var supported))
if (!CommandSupport.TryGetValue(BaseCommand, out var supported))
return false;
return supported.Contains(flag);
}
/// <summary>
/// Returns whether a string is a valid drive letter
/// </summary>
/// <param name="parameter">String value to check</param>
/// <returns>True if it's a valid drive letter, false otherwise</W>
protected static bool IsValidDriveLetter(string parameter)
=> Regex.IsMatch(parameter, @"^[A-Z]:?\\?$");
/// <summary>
/// Returns whether a string is a valid bool
/// </summary>
@@ -340,7 +358,7 @@ namespace MPF.ExecutionContexts
/// <returns>True if it's a valid byte, false otherwise</returns>
protected static bool IsValidInt8(string parameter, sbyte lowerBound = -1, sbyte upperBound = -1)
{
(string value, long _) = ExtractFactorFromValue(parameter);
string value = ExtractFactorFromValue(parameter, out _);
if (!sbyte.TryParse(value, out sbyte temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
@@ -360,7 +378,7 @@ namespace MPF.ExecutionContexts
/// <returns>True if it's a valid Int16, false otherwise</returns>
protected static bool IsValidInt16(string parameter, short lowerBound = -1, short upperBound = -1)
{
(string value, long _) = ExtractFactorFromValue(parameter);
string value = ExtractFactorFromValue(parameter, out _);
if (!short.TryParse(value, out short temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
@@ -380,7 +398,7 @@ namespace MPF.ExecutionContexts
/// <returns>True if it's a valid Int32, false otherwise</returns>
protected static bool IsValidInt32(string parameter, int lowerBound = -1, int upperBound = -1)
{
(string value, long _) = ExtractFactorFromValue(parameter);
string value = ExtractFactorFromValue(parameter, out _);
if (!int.TryParse(value, out int temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
@@ -400,7 +418,7 @@ namespace MPF.ExecutionContexts
/// <returns>True if it's a valid Int64, false otherwise</returns>
protected static bool IsValidInt64(string parameter, long lowerBound = -1, long upperBound = -1)
{
(string value, long _) = ExtractFactorFromValue(parameter);
string value = ExtractFactorFromValue(parameter, out _);
if (!long.TryParse(value, out long temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
@@ -576,7 +594,7 @@ namespace MPF.ExecutionContexts
this[longFlagString] = true;
i++;
(string value, long factor) = ExtractFactorFromValue(parts[i]);
string value = ExtractFactorFromValue(parts[i], out long factor);
if (sbyte.TryParse(value, out sbyte sByteValue))
return (sbyte)(sByteValue * factor);
string hexValue = RemoveHexIdentifier(value);
@@ -596,7 +614,7 @@ namespace MPF.ExecutionContexts
string valuePart = commandParts[1];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
string value = ExtractFactorFromValue(valuePart, out long factor);
if (sbyte.TryParse(value, out sbyte sByteValue))
return (sbyte)(sByteValue * factor);
string hexValue = RemoveHexIdentifier(value);
@@ -663,7 +681,7 @@ namespace MPF.ExecutionContexts
this[longFlagString] = true;
i++;
(string value, long factor) = ExtractFactorFromValue(parts[i]);
string value = ExtractFactorFromValue(parts[i], out long factor);
if (short.TryParse(value, out short shortValue))
return (short)(shortValue * factor);
string hexValue = RemoveHexIdentifier(value);
@@ -683,7 +701,7 @@ namespace MPF.ExecutionContexts
string valuePart = commandParts[1];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
string value = ExtractFactorFromValue(valuePart, out long factor);
if (short.TryParse(value, out short shortValue))
return (short)(shortValue * factor);
string hexValue = RemoveHexIdentifier(value);
@@ -750,7 +768,7 @@ namespace MPF.ExecutionContexts
this[longFlagString] = true;
i++;
(string value, long factor) = ExtractFactorFromValue(parts[i]);
string value = ExtractFactorFromValue(parts[i], out long factor);
if (int.TryParse(value, out int intValue))
return (int)(intValue * factor);
string hexValue = RemoveHexIdentifier(value);
@@ -770,7 +788,7 @@ namespace MPF.ExecutionContexts
string valuePart = commandParts[1];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
string value = ExtractFactorFromValue(valuePart, out long factor);
if (int.TryParse(value, out int intValue))
return (int)(intValue * factor);
string hexValue = RemoveHexIdentifier(value);
@@ -837,7 +855,7 @@ namespace MPF.ExecutionContexts
this[longFlagString] = true;
i++;
(string value, long factor) = ExtractFactorFromValue(parts[i]);
string value = ExtractFactorFromValue(parts[i], out long factor);
if (long.TryParse(value, out long longValue))
return (long)(longValue * factor);
string hexValue = RemoveHexIdentifier(value);
@@ -857,7 +875,7 @@ namespace MPF.ExecutionContexts
string valuePart = commandParts[1];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
string value = ExtractFactorFromValue(valuePart, out long factor);
if (long.TryParse(value, out long longValue))
return (long)(longValue * factor);
string hexValue = RemoveHexIdentifier(value);
@@ -998,7 +1016,7 @@ namespace MPF.ExecutionContexts
this[longFlagString] = true;
i++;
(string value, long factor) = ExtractFactorFromValue(parts[i]);
string value = ExtractFactorFromValue(parts[i], out long factor);
if (byte.TryParse(value, out byte byteValue))
return (byte)(byteValue * factor);
string hexValue = RemoveHexIdentifier(value);
@@ -1018,7 +1036,7 @@ namespace MPF.ExecutionContexts
string valuePart = commandParts[1];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
string value = ExtractFactorFromValue(valuePart, out long factor);
if (byte.TryParse(value, out byte byteValue))
return (byte)(byteValue * factor);
string hexValue = RemoveHexIdentifier(value);
@@ -1035,10 +1053,10 @@ namespace MPF.ExecutionContexts
/// </summary>
/// <param name="value">String value to treat as suffixed number</param>
/// <returns>Trimmed value and multiplication factor</returns>
private static (string trimmed, long factor) ExtractFactorFromValue(string value)
private static string ExtractFactorFromValue(string value, out long factor)
{
value = value.Trim('"');
long factor = 1;
factor = 1;
// Characters
if (value.EndsWith("c", StringComparison.Ordinal))
@@ -1089,7 +1107,7 @@ namespace MPF.ExecutionContexts
value = value.TrimEnd('G');
}
return (value, factor);
return value;
}
/// <summary>

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using SabreTools.RedumpLib.Data;
namespace MPF.ExecutionContexts.DiscImageCreator
@@ -15,10 +13,10 @@ namespace MPF.ExecutionContexts.DiscImageCreator
#region Generic Dumping Information
/// <inheritdoc/>
public override string? InputPath => DrivePath;
public override string? InputPath => DrivePath?.Trim('"');
/// <inheritdoc/>
public override string? OutputPath => Filename;
public override string? OutputPath => Filename?.Trim('"');
/// <inheritdoc/>
/// <inheritdoc/>
@@ -166,7 +164,12 @@ namespace MPF.ExecutionContexts.DiscImageCreator
public ExecutionContext(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public ExecutionContext(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Dictionary<string, string?> options)
public ExecutionContext(RedumpSystem? system,
MediaType? type,
string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
@@ -204,6 +207,7 @@ namespace MPF.ExecutionContexts.DiscImageCreator
FlagStrings.DisableBeep,
FlagStrings.DVDReread,
FlagStrings.ForceUnitAccess,
FlagStrings.Range,
FlagStrings.UseAnchorVolumeDescriptorPointer,
],
@@ -232,6 +236,7 @@ namespace MPF.ExecutionContexts.DiscImageCreator
FlagStrings.ScanSectorProtect,
FlagStrings.SeventyFour,
FlagStrings.SubchannelReadLevel,
FlagStrings.TryReadingPregap,
FlagStrings.VideoNow,
FlagStrings.VideoNowColor,
FlagStrings.VideoNowXP,
@@ -340,6 +345,7 @@ namespace MPF.ExecutionContexts.DiscImageCreator
FlagStrings.ScanSectorProtect,
FlagStrings.SeventyFour,
FlagStrings.SubchannelReadLevel,
FlagStrings.TryReadingPregap,
FlagStrings.VideoNow,
FlagStrings.VideoNowColor,
FlagStrings.VideoNowXP,
@@ -419,9 +425,16 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|| BaseCommand == CommandStrings.XGD3Swap)
{
if (DrivePath != null)
parameters.Add(DrivePath);
{
if (DrivePath.Contains(" "))
parameters.Add($"\"{DrivePath}\"");
else
parameters.Add(DrivePath);
}
else
{
return null;
}
}
// Filename
@@ -845,6 +858,13 @@ namespace MPF.ExecutionContexts.DiscImageCreator
parameters.Add(FlagStrings.Tages);
}
// Try Reading Pregap
if (IsFlagSupported(FlagStrings.TryReadingPregap))
{
if (this[FlagStrings.TryReadingPregap] == true)
parameters.Add(FlagStrings.TryReadingPregap);
}
// Use Anchor Volume Descriptor Pointer
if (IsFlagSupported(FlagStrings.UseAnchorVolumeDescriptorPointer))
{
@@ -943,17 +963,20 @@ namespace MPF.ExecutionContexts.DiscImageCreator
}
/// <inheritdoc/>
protected override void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Dictionary<string, string?> options)
protected override void SetDefaultParameters(string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options)
{
SetBaseCommand(this.System, this.Type);
SetBaseCommand(RedumpSystem, MediaType);
DrivePath = drivePath;
DriveSpeed = driveSpeed;
Filename = filename;
// First check to see if the combination of system and MediaType is valid
var validTypes = this.System.MediaTypes();
if (!validTypes.Contains(this.Type))
var validTypes = RedumpSystem.MediaTypes();
if (!validTypes.Contains(MediaType))
return;
// Set disable beep flag, if needed
@@ -979,18 +1002,18 @@ namespace MPF.ExecutionContexts.DiscImageCreator
};
// Now sort based on disc type
switch (this.Type)
switch (MediaType)
{
case MediaType.CDROM:
case SabreTools.RedumpLib.Data.MediaType.CDROM:
this[FlagStrings.C2Opcode] = true;
this[FlagStrings.MultiSectorRead] = GetBooleanSetting(options, SettingConstants.MultiSectorRead, SettingConstants.MultiSectorReadDefault);
if (this[FlagStrings.MultiSectorRead] == true)
this.MultiSectorReadValue = GetInt32Setting(options, SettingConstants.MultiSectorReadValue, SettingConstants.MultiSectorReadValueDefault);
MultiSectorReadValue = GetInt32Setting(options, SettingConstants.MultiSectorReadValue, SettingConstants.MultiSectorReadValueDefault);
switch (this.System)
switch (RedumpSystem)
{
case RedumpSystem.AppleMacintosh:
case RedumpSystem.IBMPCcompatible:
case SabreTools.RedumpLib.Data.RedumpSystem.AppleMacintosh:
case SabreTools.RedumpLib.Data.RedumpSystem.IBMPCcompatible:
this[FlagStrings.NoFixSubQSecuROM] = true;
this[FlagStrings.ScanFileProtect] = true;
this[FlagStrings.ScanSectorProtect] = GetBooleanSetting(options, SettingConstants.ParanoidMode, SettingConstants.ParanoidModeDefault);
@@ -999,48 +1022,48 @@ namespace MPF.ExecutionContexts.DiscImageCreator
SubchannelReadLevelValue = 2;
break;
case RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem:
case SabreTools.RedumpLib.Data.RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem:
this[FlagStrings.AtariJaguar] = true;
break;
case RedumpSystem.HasbroVideoNow:
case RedumpSystem.HasbroVideoNowColor:
case RedumpSystem.HasbroVideoNowJr:
case RedumpSystem.HasbroVideoNowXP:
case SabreTools.RedumpLib.Data.RedumpSystem.HasbroVideoNow:
case SabreTools.RedumpLib.Data.RedumpSystem.HasbroVideoNowColor:
case SabreTools.RedumpLib.Data.RedumpSystem.HasbroVideoNowJr:
case SabreTools.RedumpLib.Data.RedumpSystem.HasbroVideoNowXP:
this[FlagStrings.AddOffset] = true;
this.AddOffsetValue = 0; // Value needed for first run and placeholder after
AddOffsetValue = 0; // Value needed for first run and placeholder after
break;
case RedumpSystem.SonyPlayStation:
case SabreTools.RedumpLib.Data.RedumpSystem.SonyPlayStation:
this[FlagStrings.ScanAntiMod] = true;
this[FlagStrings.NoFixSubQLibCrypt] = true;
break;
}
break;
case MediaType.DVD:
case SabreTools.RedumpLib.Data.MediaType.DVD:
this[FlagStrings.CopyrightManagementInformation] = GetBooleanSetting(options, SettingConstants.UseCMIFlag, SettingConstants.UseCMIFlagDefault);
this[FlagStrings.ScanFileProtect] = GetBooleanSetting(options, SettingConstants.ParanoidMode, SettingConstants.ParanoidModeDefault);
this[FlagStrings.DVDReread] = true;
break;
case MediaType.GDROM:
case SabreTools.RedumpLib.Data.MediaType.GDROM:
this[FlagStrings.C2Opcode] = true;
break;
case MediaType.HDDVD:
case SabreTools.RedumpLib.Data.MediaType.HDDVD:
this[FlagStrings.CopyrightManagementInformation] = GetBooleanSetting(options, SettingConstants.UseCMIFlag, SettingConstants.UseCMIFlagDefault);
this[FlagStrings.DVDReread] = true;
break;
case MediaType.BluRay:
case SabreTools.RedumpLib.Data.MediaType.BluRay:
this[FlagStrings.DVDReread] = true;
break;
// Special Formats
case MediaType.NintendoGameCubeGameDisc:
case SabreTools.RedumpLib.Data.MediaType.NintendoGameCubeGameDisc:
this[FlagStrings.Raw] = true;
break;
case MediaType.NintendoWiiOpticalDisc:
case SabreTools.RedumpLib.Data.MediaType.NintendoWiiOpticalDisc:
this[FlagStrings.Raw] = true;
break;
// Non-optical
case MediaType.FloppyDisk:
case SabreTools.RedumpLib.Data.MediaType.FloppyDisk:
// Currently no defaults set
break;
}
@@ -1056,12 +1079,7 @@ namespace MPF.ExecutionContexts.DiscImageCreator
return false;
// Now split the string into parts for easier validation
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
parameters = parameters!.Trim();
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
.Cast<Match>()
.Select(m => m.Value)
.ToList();
List<string> parts = SplitParameterString(parameters!);
// Determine what the commandline should look like given the first item
BaseCommand = parts[0];
@@ -1074,10 +1092,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count < 6)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1106,10 +1122,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count < 4)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1128,10 +1142,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count != 2)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
break;
@@ -1139,10 +1151,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count < 4)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1161,10 +1171,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count < 6)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1193,10 +1201,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count < 4)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1215,10 +1221,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count != 3)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1231,21 +1235,16 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count != 2)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
break;
case CommandStrings.Eject:
if (parts.Count != 2)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
break;
@@ -1253,10 +1252,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count != 3)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1269,10 +1266,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count < 4)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1318,10 +1313,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count != 2)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
break;
@@ -1329,10 +1322,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count < 4)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1351,10 +1342,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count != 2)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
break;
@@ -1362,10 +1351,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count != 2)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
break;
@@ -1384,10 +1371,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count < 4)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1423,10 +1408,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count < 4)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1447,10 +1430,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
if (parts.Count < 4)
return false;
if (!IsValidDriveLetter(parts[1]))
return false;
else
DrivePath = parts[1];
// Blindly assume the path exists
DrivePath = parts[1];
if (IsFlagSupported(parts[2]))
return false;
@@ -1722,43 +1703,43 @@ namespace MPF.ExecutionContexts.DiscImageCreator
switch (type)
{
case MediaType.CDROM:
if (system == RedumpSystem.SuperAudioCD)
case SabreTools.RedumpLib.Data.MediaType.CDROM:
if (system == SabreTools.RedumpLib.Data.RedumpSystem.SuperAudioCD)
BaseCommand = CommandStrings.SACD;
else
BaseCommand = CommandStrings.CompactDisc;
return;
case MediaType.DVD:
if (system == RedumpSystem.MicrosoftXbox
|| system == RedumpSystem.MicrosoftXbox360)
case SabreTools.RedumpLib.Data.MediaType.DVD:
if (system == SabreTools.RedumpLib.Data.RedumpSystem.MicrosoftXbox
|| system == SabreTools.RedumpLib.Data.RedumpSystem.MicrosoftXbox360)
{
BaseCommand = CommandStrings.XBOX;
return;
}
BaseCommand = CommandStrings.DigitalVideoDisc;
return;
case MediaType.GDROM:
case SabreTools.RedumpLib.Data.MediaType.GDROM:
BaseCommand = CommandStrings.GDROM;
return;
case MediaType.HDDVD:
case SabreTools.RedumpLib.Data.MediaType.HDDVD:
BaseCommand = CommandStrings.DigitalVideoDisc;
return;
case MediaType.BluRay:
case SabreTools.RedumpLib.Data.MediaType.BluRay:
BaseCommand = CommandStrings.BluRay;
return;
case MediaType.NintendoGameCubeGameDisc:
case SabreTools.RedumpLib.Data.MediaType.NintendoGameCubeGameDisc:
BaseCommand = CommandStrings.DigitalVideoDisc;
return;
case MediaType.NintendoWiiOpticalDisc:
case SabreTools.RedumpLib.Data.MediaType.NintendoWiiOpticalDisc:
BaseCommand = CommandStrings.DigitalVideoDisc;
return;
case MediaType.FloppyDisk:
case SabreTools.RedumpLib.Data.MediaType.FloppyDisk:
BaseCommand = CommandStrings.Floppy;
return;
case MediaType.HardDisk:
case SabreTools.RedumpLib.Data.MediaType.HardDisk:
BaseCommand = CommandStrings.Disk;
return;
case MediaType.DataCartridge:
case SabreTools.RedumpLib.Data.MediaType.DataCartridge:
BaseCommand = CommandStrings.Tape;
return;

View File

@@ -37,6 +37,7 @@ namespace MPF.ExecutionContexts.DiscImageCreator
public const string SkipSector = "/sk";
public const string SubchannelReadLevel = "/s";
public const string Tages = "/t";
public const string TryReadingPregap = "/trp";
public const string UseAnchorVolumeDescriptorPointer = "/avdp";
public const string VideoNow = "/vn";
public const string VideoNowColor = "/vnc";

View File

@@ -1,45 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- 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.2.0</VersionPrefix>
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.2.4</VersionPrefix>
<WarningsNotAsErrors>NU5104</WarningsNotAsErrors>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Common code for all MPF execution contexts</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>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Common code for all MPF execution contexts</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>
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>
<ItemGroup>
<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(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="IndexRange" Version="1.0.3" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.RedumpLib" Version="1.5.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
</ItemGroup>
</Project>
</Project>

View File

@@ -18,11 +18,14 @@ namespace MPF.ExecutionContexts.Redumper
public const string RefineNew = "refinenew"; // Temporary command, to be removed later
public const string Verify = "verify";
public const string DVDKey = "dvdkey";
public const string Eject = "eject";
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";
public const string Debug = "debug";
public const string FixMSF = "fixmsf";
}
}

View File

@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using SabreTools.RedumpLib.Data;
namespace MPF.ExecutionContexts.Redumper
@@ -15,10 +13,13 @@ namespace MPF.ExecutionContexts.Redumper
#region Generic Dumping Information
/// <inheritdoc/>
public override string? InputPath => DriveValue;
public override string? InputPath => DriveValue?.Trim('"');
/// <inheritdoc/>
public override string? OutputPath => Path.Combine(ImagePathValue?.Trim('"') ?? string.Empty, ImageNameValue?.Trim('"') ?? string.Empty) + GetDefaultExtension(this.Type);
public override string? OutputPath => Path.Combine(
ImagePathValue?.Trim('"') ?? string.Empty,
ImageNameValue?.Trim('"') ?? string.Empty)
+ GetDefaultExtension(MediaType);
/// <inheritdoc/>
public override int? Speed => SpeedValue;
@@ -158,7 +159,12 @@ namespace MPF.ExecutionContexts.Redumper
public ExecutionContext(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public ExecutionContext(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Dictionary<string, string?> options)
public ExecutionContext(RedumpSystem? system,
MediaType? type,
string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
@@ -178,6 +184,7 @@ namespace MPF.ExecutionContexts.Redumper
FlagStrings.HelpShort,
FlagStrings.Version,
FlagStrings.Verbose,
FlagStrings.AutoEject,
FlagStrings.Debug,
FlagStrings.Drive,
FlagStrings.Speed,
@@ -256,6 +263,10 @@ namespace MPF.ExecutionContexts.Redumper
if (this[FlagStrings.Verbose] == true)
parameters.Add(FlagStrings.Verbose);
// Auto Eject
if (this[FlagStrings.AutoEject] == true)
parameters.Add(FlagStrings.AutoEject);
// Debug
if (this[FlagStrings.Debug] == true)
parameters.Add(FlagStrings.Debug);
@@ -264,7 +275,12 @@ namespace MPF.ExecutionContexts.Redumper
if (this[FlagStrings.Drive] == true)
{
if (DriveValue != null)
parameters.Add($"{FlagStrings.Drive}={DriveValue}");
{
if (DriveValue.Contains(" "))
parameters.Add($"{FlagStrings.Drive}=\"{DriveValue}\"");
else
parameters.Add($"{FlagStrings.Drive}={DriveValue}");
}
}
// Speed
@@ -490,14 +506,14 @@ namespace MPF.ExecutionContexts.Redumper
/// <inheritdoc/>
public override bool IsDumpingCommand()
{
return this.BaseCommand == CommandStrings.NONE
|| this.BaseCommand?.Contains(CommandStrings.CD) == true
|| this.BaseCommand?.Contains(CommandStrings.DVD) == true
|| this.BaseCommand?.Contains(CommandStrings.BluRay) == true
|| this.BaseCommand?.Contains(CommandStrings.SACD) == true
|| this.BaseCommand?.Contains(CommandStrings.New) == true
|| this.BaseCommand?.Contains(CommandStrings.Dump) == true
|| this.BaseCommand?.Contains(CommandStrings.DumpNew) == true;
return BaseCommand == CommandStrings.NONE
|| BaseCommand?.Contains(CommandStrings.CD) == true
|| BaseCommand?.Contains(CommandStrings.DVD) == true
|| BaseCommand?.Contains(CommandStrings.BluRay) == true
|| BaseCommand?.Contains(CommandStrings.SACD) == true
|| BaseCommand?.Contains(CommandStrings.New) == true
|| BaseCommand?.Contains(CommandStrings.Dump) == true
|| BaseCommand?.Contains(CommandStrings.DumpNew) == true;
}
/// <inheritdoc/>
@@ -537,34 +553,37 @@ namespace MPF.ExecutionContexts.Redumper
}
/// <inheritdoc/>
protected override void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Dictionary<string, string?> options)
protected override void SetDefaultParameters(string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options)
{
// If we don't have a CD, DVD, HD-DVD, or BD, we can't dump using redumper
if (this.Type != MediaType.CDROM
&& this.Type != MediaType.DVD
&& this.Type != MediaType.HDDVD
&& this.Type != MediaType.BluRay)
if (MediaType != SabreTools.RedumpLib.Data.MediaType.CDROM
&& MediaType != SabreTools.RedumpLib.Data.MediaType.DVD
&& MediaType != SabreTools.RedumpLib.Data.MediaType.HDDVD
&& MediaType != SabreTools.RedumpLib.Data.MediaType.BluRay)
{
return;
}
BaseCommand = CommandStrings.NONE;
switch (this.Type)
switch (MediaType)
{
case MediaType.CDROM:
ModeValues = this.System switch
case SabreTools.RedumpLib.Data.MediaType.CDROM:
ModeValues = RedumpSystem switch
{
RedumpSystem.SuperAudioCD => [CommandStrings.SACD],
_ => [CommandStrings.CD],
SabreTools.RedumpLib.Data.RedumpSystem.SuperAudioCD => [CommandStrings.SACD],
_ => [CommandStrings.CD, CommandStrings.Skeleton],
};
break;
case MediaType.DVD:
case SabreTools.RedumpLib.Data.MediaType.DVD:
ModeValues = [CommandStrings.DVD];
break;
case MediaType.HDDVD: // TODO: Keep in sync if another command string shows up
case SabreTools.RedumpLib.Data.MediaType.HDDVD: // TODO: Keep in sync if another command string shows up
ModeValues = [CommandStrings.DVD];
break;
case MediaType.BluRay:
case SabreTools.RedumpLib.Data.MediaType.BluRay:
ModeValues = [CommandStrings.BluRay];
break;
default:
@@ -643,12 +662,7 @@ namespace MPF.ExecutionContexts.Redumper
return false;
// Now split the string into parts for easier validation
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
parameters = parameters!.Trim();
List<string> parts = Regex.Matches(parameters, @"([a-zA-Z\-]*=)?[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
.Cast<Match>()
.Select(m => m.Value)
.ToList();
List<string> parts = SplitParameterString(parameters!);
// Setup the modes
ModeValues = [];
@@ -675,12 +689,15 @@ namespace MPF.ExecutionContexts.Redumper
case CommandStrings.RefineNew: // Temporary command, to be removed later
case CommandStrings.Verify:
case CommandStrings.DVDKey:
case CommandStrings.Eject:
case CommandStrings.DVDIsoKey:
case CommandStrings.Protection:
case CommandStrings.Split:
case CommandStrings.Hash:
case CommandStrings.Info:
case CommandStrings.Skeleton:
case CommandStrings.Debug:
//case CommandStrings.FixMSF:
ModeValues.Add(part);
break;
@@ -890,8 +907,8 @@ namespace MPF.ExecutionContexts.Redumper
}
// If the image name was not set, set it with a default value
if (string.IsNullOrEmpty(this.ImageNameValue))
this.ImageNameValue = "track";
if (string.IsNullOrEmpty(ImageNameValue))
ImageNameValue = "track";
return true;
}

View File

@@ -10,6 +10,7 @@ namespace MPF.ExecutionContexts.Redumper
public const string HelpShort = "-h";
public const string Version = "--version";
public const string Verbose = "--verbose";
public const string AutoEject = "--auto-eject";
public const string Debug = "--debug";
public const string Drive = "--drive";
public const string Speed = "--speed";

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace MPF.Frontend.ComboBoxItems
{
@@ -40,11 +39,10 @@ namespace MPF.Frontend.ComboBoxItems
/// Generate all elements associated with the data enum type
/// </summary>
/// <returns></returns>
public static IEnumerable<Element<T>> GenerateElements()
public static List<Element<T>> GenerateElements()
{
return Enum.GetValues(typeof(T))
.OfType<T>()
.Select(e => new Element<T>(e));
var enumArr = (T[])Enum.GetValues(typeof(T));
return [.. Array.ConvertAll(enumArr, e => new Element<T>(e))];
}
/// <inheritdoc/>

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
#if NET35_OR_GREATER || NETCOREAPP
using System.Linq;
#endif
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend.ComboBoxItems
@@ -50,13 +52,26 @@ namespace MPF.Frontend.ComboBoxItems
/// Generate all elements for the known system combo box
/// </summary>
/// <returns></returns>
public static IEnumerable<RedumpSystemComboBoxItem> GenerateElements()
public static List<RedumpSystemComboBoxItem> GenerateElements()
{
var knownSystems = Enum.GetValues(typeof(RedumpSystem))
.OfType<RedumpSystem?>()
.Where(s => !s.IsMarker() && s.GetCategory() != SystemCategory.NONE)
.ToList();
var enumArr = (RedumpSystem[])Enum.GetValues(typeof(RedumpSystem));
var nullableArr = Array.ConvertAll(enumArr, s => (RedumpSystem?)s);
var knownSystems = Array.FindAll(nullableArr,
s => !s.IsMarker() && s.GetCategory() != SystemCategory.NONE);
#if NET20
// The resulting dictionary does not have ordered value lists
var mapping = new Dictionary<SystemCategory, List<RedumpSystem?>>();
foreach (var knownSystem in knownSystems)
{
var category = knownSystem.GetCategory();
if (!mapping.ContainsKey(category))
mapping[category] = [];
mapping[category].Add(knownSystem);
}
#else
// The resulting dictionary has ordered value lists
Dictionary<SystemCategory, List<RedumpSystem?>> mapping = knownSystems
.GroupBy(s => s.GetCategory())
.ToDictionary(
@@ -65,6 +80,7 @@ namespace MPF.Frontend.ComboBoxItems
.OrderBy(s => s.LongName())
.ToList()
);
#endif
var systemsValues = new List<RedumpSystemComboBoxItem>
{

View File

@@ -1,16 +1,10 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
#if NET462_OR_GREATER || NETCOREAPP
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Generic;
#endif
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SabreTools.IO;
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend
@@ -142,26 +136,31 @@ namespace MPF.Frontend
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
{
var drives = GetDriveList(ignoreFixedDrives);
drives = [.. drives.OrderBy(i => i == null ? "\0" : i.Name)];
return drives;
drives.Sort((d1, d2) =>
{
string d1Name = d1?.Name == null ? "\0" : d1.Name;
string d2Name = d2?.Name == null ? "\0" : d2.Name;
return d1Name.CompareTo(d2Name);
});
return [.. drives];
}
/// <summary>
/// Get the current media type from drive letter
/// </summary>
/// <param name="system"></param>
/// <returns></returns>
public (MediaType?, string?) GetMediaType(RedumpSystem? system)
/// <param name="system">Currently selected system</param>
/// <returns>The detected media type, if possible</returns>
public MediaType? GetMediaType(RedumpSystem? system)
{
// Take care of the non-optical stuff first
switch (InternalDriveType)
{
case Frontend.InternalDriveType.Floppy:
return (MediaType.FloppyDisk, null);
return MediaType.FloppyDisk;
case Frontend.InternalDriveType.HardDisk:
return (MediaType.HardDisk, null);
return MediaType.HardDisk;
case Frontend.InternalDriveType.Removable:
return (MediaType.FlashDrive, null);
return MediaType.FlashDrive;
}
// Some systems should default to certain media types
@@ -174,18 +173,18 @@ namespace MPF.Frontend
case RedumpSystem.SegaSaturn:
case RedumpSystem.SonyPlayStation:
case RedumpSystem.VideoCD:
return (MediaType.CDROM, null);
return MediaType.CDROM;
// DVD
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
case RedumpSystem.MicrosoftXbox:
case RedumpSystem.MicrosoftXbox360:
return (MediaType.DVD, null);
return MediaType.DVD;
// HD-DVD
case RedumpSystem.HDDVDVideo:
return (MediaType.HDDVD, null);
return MediaType.HDDVD;
// Blu-ray
case RedumpSystem.BDVideo:
@@ -194,34 +193,34 @@ namespace MPF.Frontend
case RedumpSystem.SonyPlayStation3:
case RedumpSystem.SonyPlayStation4:
case RedumpSystem.SonyPlayStation5:
return (MediaType.BluRay, null);
return MediaType.BluRay;
// GameCube
case RedumpSystem.NintendoGameCube:
return (MediaType.NintendoGameCubeGameDisc, null);
return MediaType.NintendoGameCubeGameDisc;
// Wii
case RedumpSystem.NintendoWii:
return (MediaType.NintendoWiiOpticalDisc, null);
return MediaType.NintendoWiiOpticalDisc;
// WiiU
case RedumpSystem.NintendoWiiU:
return (MediaType.NintendoWiiUOpticalDisc, null);
return MediaType.NintendoWiiUOpticalDisc;
// PSP
case RedumpSystem.SonyPlayStationPortable:
return (MediaType.UMD, null);
return MediaType.UMD;
}
// Handle optical media by size and filesystem
if (TotalSize >= 0 && TotalSize <= 800_000_000 && (DriveFormat == "CDFS" || DriveFormat == "UDF"))
return (MediaType.CDROM, null);
return MediaType.CDROM;
else if (TotalSize > 800_000_000 && TotalSize <= 8_540_000_000 && (DriveFormat == "CDFS" || DriveFormat == "UDF"))
return (MediaType.DVD, null);
return MediaType.DVD;
else if (TotalSize > 8_540_000_000)
return (MediaType.BluRay, null);
return MediaType.BluRay;
return (null, "Could not determine media type!");
return null;
}
/// <summary>
@@ -229,554 +228,12 @@ namespace MPF.Frontend
/// </summary>
public void RefreshDrive()
{
var driveInfo = DriveInfo.GetDrives().FirstOrDefault(d => d?.Name == Name);
var driveInfo = Array.Find(DriveInfo.GetDrives(), d => d?.Name == Name);
PopulateFromDriveInfo(driveInfo);
}
#endregion
#region Information Extraction
/// <summary>
/// Get the EXE name from a PlayStation disc, if possible
/// </summary>
/// <returns>Executable name on success, null otherwise</returns>
public string? GetPlayStationExecutableName()
{
// If there's no drive path, we can't get exe name
if (string.IsNullOrEmpty(Name))
return null;
// If the folder no longer exists, we can't get exe name
if (!Directory.Exists(Name))
return null;
// Get the two paths that we will need to check
string psxExePath = Path.Combine(Name, "PSX.EXE");
string systemCnfPath = Path.Combine(Name, "SYSTEM.CNF");
// Read the CNF file as an INI file
var systemCnf = new IniFile(systemCnfPath);
string? bootValue = string.Empty;
// PlayStation uses "BOOT" as the key
if (systemCnf.ContainsKey("BOOT"))
bootValue = systemCnf["BOOT"];
// PlayStation 2 uses "BOOT2" as the key
if (systemCnf.ContainsKey("BOOT2"))
bootValue = systemCnf["BOOT2"];
// If we had any boot value, parse it and get the executable name
if (!string.IsNullOrEmpty(bootValue))
{
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)", RegexOptions.Compiled);
if (match.Groups.Count > 1)
{
string? serial = match.Groups[1].Value;
// Some games may have the EXE in a subfolder
serial = Path.GetFileName(serial);
return serial;
}
}
// If the SYSTEM.CNF value can't be found, try PSX.EXE
if (File.Exists(psxExePath))
return "PSX.EXE";
// If neither can be found, we return null
return null;
}
/// <summary>
/// Get the EXE date from a PlayStation disc, if possible
/// </summary>
/// <param name="serial">Internal disc serial, if possible</param>
/// <param name="region">Output region, if possible</param>
/// <param name="date">Output EXE date in "yyyy-mm-dd" format if possible, null on error</param>
/// <returns>True if information could be determined, false otherwise</returns>
public bool GetPlayStationExecutableInfo(out string? serial, out Region? region, out string? date)
{
serial = null; region = null; date = null;
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(Name))
return false;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(Name))
return false;
// Get the executable name
string? exeName = GetPlayStationExecutableName();
// If no executable found, we can't do this part
if (exeName == null)
return false;
// EXE name may have a trailing `;` after
// EXE name should always be in all caps
exeName = exeName
.Split(';')[0]
.ToUpperInvariant();
// Serial is most of the EXE name normalized
serial = exeName
.Replace('_', '-')
.Replace(".", string.Empty);
// Get the region, if possible
region = GetPlayStationRegion(exeName);
// Now that we have the EXE name, try to get the fileinfo for it
string exePath = Path.Combine(Name, exeName);
if (!File.Exists(exePath))
return false;
// Fix the Y2K timestamp issue
var fi = new FileInfo(exePath);
var dt = new DateTime(fi.LastWriteTimeUtc.Year >= 1900 && fi.LastWriteTimeUtc.Year < 1920 ? 2000 + fi.LastWriteTimeUtc.Year % 100 : fi.LastWriteTimeUtc.Year,
fi.LastWriteTimeUtc.Month, fi.LastWriteTimeUtc.Day);
date = dt.ToString("yyyy-MM-dd");
return true;
}
/// <summary>
/// Determine the region based on the PlayStation serial code
/// </summary>
/// <param name="serial">PlayStation serial code</param>
/// <returns>Region mapped from name, if possible</returns>
public static Region? GetPlayStationRegion(string? serial)
{
// If we have a fully invalid serial
if (string.IsNullOrEmpty(serial))
return null;
// Standardized "S" serials
if (serial!.StartsWith("S"))
{
// string publisher = serial[0] + serial[1];
// char secondRegion = serial[3];
switch (serial[2])
{
case 'A': return Region.Asia;
case 'C': return Region.China;
case 'E': return Region.Europe;
case 'K': return Region.SouthKorea;
case 'U': return Region.UnitedStatesOfAmerica;
case 'P':
// Region of S_P_ serials may be Japan, Asia, or SouthKorea
return serial[3] switch
{
// Check first two digits of S_PS serial
'S' => (Region?)(serial.Substring(5, 2) switch
{
"46" => Region.SouthKorea,
"51" => Region.Asia,
"56" => Region.SouthKorea,
"55" => Region.Asia,
_ => Region.Japan,
}),
// Check first three digits of S_PM serial
'M' => (Region?)(serial.Substring(5, 3) switch
{
"645" => Region.SouthKorea,
"675" => Region.SouthKorea,
"885" => Region.SouthKorea,
_ => Region.Japan, // Remaining S_PM serials may be Japan or Asia
}),
_ => (Region?)Region.Japan,
};
}
}
// Japan-only special serial
else if (serial.StartsWith("PAPX"))
return Region.Japan;
// Region appears entirely random
else if (serial.StartsWith("PABX"))
return null;
// Region appears entirely random
else if (serial.StartsWith("PBPX"))
return null;
// Japan-only special serial
else if (serial.StartsWith("PCBX"))
return Region.Japan;
// Japan-only special serial
else if (serial.StartsWith("PCXC"))
return Region.Japan;
// Single disc known, Japan
else if (serial.StartsWith("PDBX"))
return Region.Japan;
// Single disc known, Europe
else if (serial.StartsWith("PEBX"))
return Region.Europe;
// Single disc known, USA
else if (serial.StartsWith("PUBX"))
return Region.UnitedStatesOfAmerica;
return null;
}
/// <summary>
/// Get the version from a PlayStation 2 disc, if possible
/// </summary>
/// <returns>Game version if possible, null on error</returns>
public string? GetPlayStation2Version()
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(Name))
return null;
// Get the SYSTEM.CNF path to check
string systemCnfPath = Path.Combine(Name, "SYSTEM.CNF");
// Try to parse the SYSTEM.CNF file
var systemCnf = new IniFile(systemCnfPath);
if (systemCnf.ContainsKey("VER"))
return systemCnf["VER"];
// If "VER" can't be found, we can't do much
return null;
}
/// <summary>
/// Get the internal serial from a PlayStation 3 disc, if possible
/// </summary>
/// <returns>Internal disc serial if possible, null on error</returns>
public string? GetPlayStation3Serial()
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(Name))
return null;
// Attempt to use PS3_DISC.SFB
string sfbPath = Path.Combine(Name, "PS3_DISC.SFB");
if (File.Exists(sfbPath))
{
try
{
using var br = new BinaryReader(File.OpenRead(sfbPath));
br.BaseStream.Seek(0x220, SeekOrigin.Begin);
return new string(br.ReadChars(0x10)).TrimEnd('\0');
}
catch
{
// We don't care what the error was
return null;
}
}
// Attempt to use PARAM.SFO
#if NET20 || NET35
string sfoPath = Path.Combine(Path.Combine(Name, "PS3_GAME"), "PARAM.SFO");
#else
string sfoPath = Path.Combine(Name, "PS3_GAME", "PARAM.SFO");
#endif
if (File.Exists(sfoPath))
{
try
{
using var br = new BinaryReader(File.OpenRead(sfoPath));
br.BaseStream.Seek(-0x18, SeekOrigin.End);
return new string(br.ReadChars(9)).TrimEnd('\0').Insert(4, "-");
}
catch
{
// We don't care what the error was
return null;
}
}
return null;
}
/// <summary>
/// Get the version from a PlayStation 3 disc, if possible
/// </summary>
/// <returns>Game version if possible, null on error</returns>
public string? GetPlayStation3Version()
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(Name))
return null;
// Attempt to use PS3_DISC.SFB
string sfbPath = Path.Combine(Name, "PS3_DISC.SFB");
if (File.Exists(sfbPath))
{
try
{
using var br = new BinaryReader(File.OpenRead(sfbPath));
br.BaseStream.Seek(0x230, SeekOrigin.Begin);
var discVersion = new string(br.ReadChars(0x10)).TrimEnd('\0');
if (!string.IsNullOrEmpty(discVersion))
return discVersion;
}
catch
{
// We don't care what the error was
return null;
}
}
// Attempt to use PARAM.SFO
#if NET20 || NET35
string sfoPath = Path.Combine(Path.Combine(Name, "PS3_GAME"), "PARAM.SFO");
#else
string sfoPath = Path.Combine(Name, "PS3_GAME", "PARAM.SFO");
#endif
if (File.Exists(sfoPath))
{
try
{
using var br = new BinaryReader(File.OpenRead(sfoPath));
br.BaseStream.Seek(-0x08, SeekOrigin.End);
return new string(br.ReadChars(5)).TrimEnd('\0');
}
catch
{
// We don't care what the error was
return null;
}
}
return null;
}
/// <summary>
/// Get the firmware version from a PlayStation 3 disc, if possible
/// </summary>
/// <returns>Firmware version if possible, null on error</returns>
public string? GetPlayStation3FirmwareVersion()
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(Name))
return null;
// Attempt to read from /PS3_UPDATE/PS3UPDAT.PUP
#if NET20 || NET35
string pupPath = Path.Combine(Path.Combine(Name, "PS3_UPDATE"), "PS3UPDAT.PUP");
#else
string pupPath = Path.Combine(Name, "PS3_UPDATE", "PS3UPDAT.PUP");
#endif
if (!File.Exists(pupPath))
return null;
try
{
using var br = new BinaryReader(File.OpenRead(pupPath));
br.BaseStream.Seek(0x3E, SeekOrigin.Begin);
byte[] buf = new byte[2];
br.Read(buf, 0, 2);
Array.Reverse(buf);
short location = BitConverter.ToInt16(buf, 0);
br.BaseStream.Seek(location, SeekOrigin.Begin);
return new string(br.ReadChars(4));
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the internal serial from a PlayStation 4 disc, if possible
/// </summary>
/// <returns>Internal disc serial if possible, null on error</returns>
public string? GetPlayStation4Serial()
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(Name))
return null;
// If we can't find param.sfo, we don't have a PlayStation 4 disc
#if NET20 || NET35
string paramSfoPath = Path.Combine(Path.Combine(Name, "bd"), "param.sfo");
#else
string paramSfoPath = Path.Combine(Name, "bd", "param.sfo");
#endif
if (!File.Exists(paramSfoPath))
return null;
// Let's try reading param.sfo to find the serial at the end of the file
try
{
using var br = new BinaryReader(File.OpenRead(paramSfoPath));
br.BaseStream.Seek(-0x14, SeekOrigin.End);
return new string(br.ReadChars(9)).Insert(4, "-");
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the version from a PlayStation 4 disc, if possible
/// </summary>
/// <returns>Game version if possible, null on error</returns>
public string? GetPlayStation4Version()
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(Name))
return null;
// If we can't find param.sfo, we don't have a PlayStation 4 disc
#if NET20 || NET35
string paramSfoPath = Path.Combine(Path.Combine(Name, "bd"), "param.sfo");
#else
string paramSfoPath = Path.Combine(Name, "bd", "param.sfo");
#endif
if (!File.Exists(paramSfoPath))
return null;
// Let's try reading param.sfo to find the version at the end of the file
try
{
using var br = new BinaryReader(File.OpenRead(paramSfoPath));
br.BaseStream.Seek(-0x08, SeekOrigin.End);
return new string(br.ReadChars(5));
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the internal serial from a PlayStation 5 disc, if possible
/// </summary>
/// <returns>Internal disc serial if possible, null on error</returns>
public string? GetPlayStation5Serial()
{
// Attempt to get the param.json file
var json = GetPlayStation5ParamsJsonFromDrive();
if (json == null)
return null;
try
{
return json["disc"]?[0]?["masterDataId"]?.Value<string>()?.Insert(4, "-");
}
catch
{
// We don't care what the error was
return null;
}
}
// <summary>
/// Get the version from a PlayStation 5 disc, if possible
/// </summary>
/// <returns>Game version if possible, null on error</returns>
public string? GetPlayStation5Version()
{
// Attempt to get the param.json file
var json = GetPlayStation5ParamsJsonFromDrive();
if (json == null)
return null;
try
{
return json["masterVersion"]?.Value<string>();
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the params.json file from a drive path, if possible
/// </summary>
/// <returns>JObject representing the JSON on success, null on error</returns>
private JObject? GetPlayStation5ParamsJsonFromDrive()
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(Name))
return null;
// If we can't find param.json, we don't have a PlayStation 5 disc
#if NET20 || NET35
string paramJsonPath = Path.Combine(Path.Combine(Name, "bd"), "param.json");
#else
string paramJsonPath = Path.Combine(Name, "bd", "param.json");
#endif
return GetPlayStation5ParamsJsonFromFile(paramJsonPath);
}
/// <summary>
/// Get the params.json file from a filename, if possible
/// </summary>
/// <param name="filename">Filename to check</param>
/// <returns>JObject representing the JSON on success, null on error</returns>
private static JObject? GetPlayStation5ParamsJsonFromFile(string? filename)
{
// If the file doesn't exist
if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
return null;
// Let's try reading param.json to find the version in the unencrypted JSON
try
{
using var br = new BinaryReader(File.OpenRead(filename));
br.BaseStream.Seek(0x800, SeekOrigin.Begin);
byte[] jsonBytes = br.ReadBytes((int)(br.BaseStream.Length - 0x800));
return JsonConvert.DeserializeObject(Encoding.ASCII.GetString(jsonBytes)) as JObject;
}
catch
{
// We don't care what the error was
return null;
}
}
#endregion
#region Helpers
/// <summary>
@@ -800,20 +257,18 @@ namespace MPF.Frontend
// TODO: Reduce reliance on `DriveInfo`
// https://github.com/aaru-dps/Aaru/blob/5164a154e2145941472f2ee0aeb2eff3338ecbb3/Aaru.Devices/Windows/ListDevices.cs#L66
// Create an output drive list
var drives = new List<Drive>();
// Create an output drive array
Drive[] drives = [];
// Get all standard supported drive types
try
{
drives = DriveInfo.GetDrives()
.Where(d => desiredDriveTypes.Contains(d.DriveType))
.Select(d => Create(ToInternalDriveType(d.DriveType), d.Name) ?? new Drive())
.ToList();
var filteredDrives = Array.FindAll(DriveInfo.GetDrives(), d => desiredDriveTypes.Contains(d.DriveType));
drives = Array.ConvertAll(filteredDrives, d => Create(ToInternalDriveType(d.DriveType), d.Name) ?? new Drive());
}
catch
{
return drives;
return [.. drives];
}
// Find and update all floppy drives
@@ -830,7 +285,11 @@ namespace MPF.Frontend
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
{
char devId = (properties["Caption"].Value as string ?? string.Empty)[0];
drives.ForEach(d => { if (d?.Name != null && d.Name[0] == devId) { d.InternalDriveType = Frontend.InternalDriveType.Floppy; } });
Array.ForEach(drives, d =>
{
if (d?.Name != null && d.Name[0] == devId)
d.InternalDriveType = Frontend.InternalDriveType.Floppy;
});
}
}
}
@@ -840,7 +299,7 @@ namespace MPF.Frontend
}
#endif
return drives;
return [.. drives];
}
/// <summary>

View File

@@ -2,8 +2,10 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
#if NET40
using System.Threading;
#endif
using System.Threading.Tasks;
using BinaryObjectScanner;
using MPF.ExecutionContexts;
@@ -35,7 +37,7 @@ namespace MPF.Frontend
/// <summary>
/// Drive object representing the current drive
/// </summary>
private readonly Drive? _drive;
private Drive? _drive;
/// <summary>
/// ExecutionContext object representing how to invoke the internal program
@@ -119,7 +121,7 @@ namespace MPF.Frontend
/// <param name="internalProgram"></param>
/// <param name="parameters"></param>
public DumpEnvironment(Frontend.Options options,
string outputPath,
string? outputPath,
Drive? drive,
RedumpSystem? system,
MediaType? type,
@@ -155,22 +157,22 @@ namespace MPF.Frontend
if (programFound == null && _internalProgram != InternalProgram.Aaru)
{
var processor = new Processors.Aaru(_system, _type);
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true);
if (foundOtherFiles)
var missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.Aaru;
}
if (programFound == null && _internalProgram != InternalProgram.DiscImageCreator)
{
var processor = new Processors.DiscImageCreator(_system, _type);
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true);
if (foundOtherFiles)
var missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.DiscImageCreator;
}
if (programFound == null && _internalProgram != InternalProgram.Redumper)
{
var processor = new Processors.Redumper(_system, _type);
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true);
if (foundOtherFiles)
var missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.Redumper;
}
@@ -194,11 +196,15 @@ namespace MPF.Frontend
_ => null,
};
// Set system and type
// Set system, type, and drive
if (_executionContext != null)
{
_executionContext.System = _system;
_executionContext.Type = _type;
_executionContext.RedumpSystem = _system;
_executionContext.MediaType = _type;
// Set some parameters, if not already set
OutputPath ??= _executionContext.OutputPath!;
_drive ??= Drive.Create(InternalDriveType.Optical, _executionContext.InputPath!);
}
return _executionContext != null;
@@ -287,13 +293,13 @@ namespace MPF.Frontend
};
}
/// <inheritdoc cref="BaseProcessor.FoundAllFiles(string?, string, bool)"/>
public bool FoundAllFiles(string? outputDirectory, string outputFilename, bool preCheck)
/// <inheritdoc cref="BaseProcessor.FoundAllFiles(string?, string)"/>
public bool FoundAllFiles(string? outputDirectory, string outputFilename)
{
if (_processor == null)
return false;
return _processor.FoundAllFiles(outputDirectory, outputFilename, preCheck).Item1;
return _processor.FoundAllFiles(outputDirectory, outputFilename).Count == 0;
}
/// <inheritdoc cref="BaseExecutionContext.GetDefaultExtension(MediaType?)"/>
@@ -378,11 +384,7 @@ namespace MPF.Frontend
/// Execute the initial invocation of the dumping programs
/// </summary>
/// <param name="progress">Optional result progress callback</param>
#if NET40
public ResultEventArgs Run(IProgress<ResultEventArgs>? progress = null)
#else
public async Task<ResultEventArgs> Run(IProgress<ResultEventArgs>? progress = null)
#endif
{
// If we don't have parameters
if (_executionContext == null)
@@ -401,8 +403,7 @@ namespace MPF.Frontend
Directory.CreateDirectory(directoryName);
#if NET40
var executeTask = Task.Factory.StartNew(() => _executionContext.ExecuteInternalProgram());
executeTask.Wait();
await Task.Factory.StartNew(() => { _executionContext.ExecuteInternalProgram(); return true; });
#else
await Task.Run(_executionContext.ExecuteInternalProgram);
#endif
@@ -422,7 +423,7 @@ namespace MPF.Frontend
public async Task<ResultEventArgs> VerifyAndSaveDumpOutput(
IProgress<ResultEventArgs>? resultProgress = null,
IProgress<ProtectionProgress>? protectionProgress = null,
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null,
ProcessUserInfoDelegate? processUserInfo = null,
SubmissionInfo? seedInfo = null)
{
if (_processor == null)
@@ -435,8 +436,8 @@ namespace MPF.Frontend
var outputFilename = Path.GetFileName(OutputPath);
// Check to make sure that the output had all the correct files
(bool foundFiles, List<string> missingFiles) = _processor.FoundAllFiles(outputDirectory, outputFilename, false);
if (!foundFiles)
List<string> missingFiles = _processor.FoundAllFiles(outputDirectory, outputFilename);
if (missingFiles.Count > 0)
{
resultProgress?.Report(ResultEventArgs.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
return ResultEventArgs.Failure("Error! Please check output directory as dump may be incomplete!");
@@ -468,8 +469,7 @@ namespace MPF.Frontend
{
resultProgress?.Report(ResultEventArgs.Success("Waiting for additional disc information..."));
bool? filledInfo;
(filledInfo, submissionInfo) = processUserInfo(submissionInfo);
bool? filledInfo = processUserInfo(ref submissionInfo);
if (filledInfo == true)
resultProgress?.Report(ResultEventArgs.Success("Additional disc information added!"));
@@ -484,7 +484,7 @@ namespace MPF.Frontend
// Format the information for the text output
resultProgress?.Report(ResultEventArgs.Success("Formatting information..."));
(var formattedValues, var formatResult) = Formatter.FormatOutputData(submissionInfo, _options.EnableRedumpCompatibility);
var formattedValues = Formatter.FormatOutputData(submissionInfo, _options.EnableRedumpCompatibility, out string? formatResult);
if (formattedValues == null)
resultProgress?.Report(ResultEventArgs.Failure(formatResult));
else
@@ -494,19 +494,19 @@ namespace MPF.Frontend
var filenameSuffix = _options.AddFilenameSuffix ? Path.GetFileNameWithoutExtension(outputFilename) : null;
// Write the text output
resultProgress?.Report(ResultEventArgs.Success("Writing information to !submissionInfo.txt..."));
(bool txtSuccess, string txtResult) = WriteOutputData(outputDirectory, filenameSuffix, formattedValues);
resultProgress?.Report(ResultEventArgs.Success("Writing submission information file..."));
bool txtSuccess = WriteOutputData(outputDirectory, filenameSuffix, formattedValues, out string txtResult);
if (txtSuccess)
resultProgress?.Report(ResultEventArgs.Success(txtResult));
else
resultProgress?.Report(ResultEventArgs.Failure(txtResult));
// Write the copy protection output
if (submissionInfo?.CopyProtection?.FullProtections != null && submissionInfo.CopyProtection.FullProtections.Any())
if (submissionInfo?.CopyProtection?.FullProtections != null && submissionInfo.CopyProtection.FullProtections.Count > 0)
{
if (_options.ScanForProtection)
{
resultProgress?.Report(ResultEventArgs.Success("Writing protection to !protectionInfo.txt..."));
resultProgress?.Report(ResultEventArgs.Success("Writing protection information file..."));
bool scanSuccess = WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo, _options.HideDriveLetters);
if (scanSuccess)
resultProgress?.Report(ResultEventArgs.Success("Writing complete!"));
@@ -518,7 +518,7 @@ namespace MPF.Frontend
// Write the JSON output, if required
if (_options.OutputSubmissionJSON)
{
resultProgress?.Report(ResultEventArgs.Success($"Writing information to !submissionInfo.json{(_options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
resultProgress?.Report(ResultEventArgs.Success($"Writing submission information JSON file{(_options.IncludeArtifacts ? " with artifacts" : string.Empty)}..."));
bool jsonSuccess = WriteOutputData(outputDirectory, filenameSuffix, submissionInfo, _options.IncludeArtifacts);
if (jsonSuccess)
resultProgress?.Report(ResultEventArgs.Success("Writing complete!"));
@@ -530,33 +530,47 @@ namespace MPF.Frontend
if (_options.CompressLogFiles)
{
resultProgress?.Report(ResultEventArgs.Success("Compressing log files..."));
(bool compressSuccess, string compressResult) = _processor?.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename) ?? (false, "No processor provided!");
if (compressSuccess)
resultProgress?.Report(ResultEventArgs.Success(compressResult));
if (_processor == null)
{
resultProgress?.Report(ResultEventArgs.Failure("No processor provided!"));
}
else
resultProgress?.Report(ResultEventArgs.Failure(compressResult));
{
bool compressSuccess = _processor.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, out string compressResult);
if (compressSuccess)
resultProgress?.Report(ResultEventArgs.Success(compressResult));
else
resultProgress?.Report(ResultEventArgs.Failure(compressResult));
}
}
// Delete unnecessary files, if required
if (_options.DeleteUnnecessaryFiles)
{
resultProgress?.Report(ResultEventArgs.Success("Deleting unnecessary files..."));
(bool deleteSuccess, string deleteResult) = _processor?.DeleteUnnecessaryFiles(outputDirectory, outputFilename) ?? (false, "No processor provided!");
if (deleteSuccess)
resultProgress?.Report(ResultEventArgs.Success(deleteResult));
if (_processor == null)
{
resultProgress?.Report(ResultEventArgs.Failure("No processor provided!"));
}
else
resultProgress?.Report(ResultEventArgs.Failure(deleteResult));
{
bool deleteSuccess = _processor.DeleteUnnecessaryFiles(outputDirectory, outputFilename, out string deleteResult);
if (deleteSuccess)
resultProgress?.Report(ResultEventArgs.Success(deleteResult));
else
resultProgress?.Report(ResultEventArgs.Failure(deleteResult));
}
}
// Create PS3 IRD, if required
if (_options.CreateIRDAfterDumping && _system == RedumpSystem.SonyPlayStation3 && _type == MediaType.BluRay)
{
resultProgress?.Report(ResultEventArgs.Success("Creating IRD... please wait!"));
(bool deleteSuccess, string deleteResult) = await WriteIRD(OutputPath, submissionInfo?.Extras?.DiscKey, submissionInfo?.Extras?.DiscID, submissionInfo?.Extras?.PIC, submissionInfo?.SizeAndChecksums?.Layerbreak, submissionInfo?.SizeAndChecksums?.CRC32);
bool deleteSuccess = await WriteIRD(OutputPath, submissionInfo?.Extras?.DiscKey, submissionInfo?.Extras?.DiscID, submissionInfo?.Extras?.PIC, submissionInfo?.SizeAndChecksums?.Layerbreak, submissionInfo?.SizeAndChecksums?.CRC32);
if (deleteSuccess)
resultProgress?.Report(ResultEventArgs.Success(deleteResult));
resultProgress?.Report(ResultEventArgs.Success("IRD created!"));
else
resultProgress?.Report(ResultEventArgs.Failure(deleteResult));
resultProgress?.Report(ResultEventArgs.Failure("Failed to create IRD"));
}
resultProgress?.Report(ResultEventArgs.Success("Submission information process complete!"));
@@ -624,11 +638,14 @@ namespace MPF.Frontend
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
/// <param name="lines">Preformatted list of lines to write out to the file</param>
/// <returns>True on success, false on error</returns>
private static (bool, string) WriteOutputData(string? outputDirectory, string? filenameSuffix, List<string>? lines)
private static bool WriteOutputData(string? outputDirectory, string? filenameSuffix, List<string>? lines, out string status)
{
// Check to see if the inputs are valid
if (lines == null)
return (false, "No formatted data found to write!");
{
status = "No formatted data found to write!";
return false;
}
// Now write out to a generic file
try
@@ -652,10 +669,12 @@ namespace MPF.Frontend
}
catch (Exception ex)
{
return (false, $"Writing could not complete: {ex}");
status = $"Writing could not complete: {ex}";
return false;
}
return (true, "Writing complete!");
status = "Writing complete!";
return true;
}
// MOVE TO REDUMPLIB
@@ -735,7 +754,7 @@ namespace MPF.Frontend
private static bool WriteProtectionData(string? outputDirectory, string? filenameSuffix, SubmissionInfo? info, bool hideDriveLetters)
{
// Check to see if the inputs are valid
if (info?.CopyProtection?.FullProtections == null || !info.CopyProtection.FullProtections.Any())
if (info?.CopyProtection?.FullProtections == null || info.CopyProtection.FullProtections.Count == 0)
return true;
// Now write out to a generic file
@@ -753,7 +772,9 @@ namespace MPF.Frontend
using var sw = new StreamWriter(File.Open(path, FileMode.Create, FileAccess.Write), Encoding.UTF8);
List<string> sortedKeys = [.. info.CopyProtection.FullProtections.Keys.OrderBy(k => k)];
List<string> sortedKeys = [.. info.CopyProtection.FullProtections.Keys];
sortedKeys.Sort();
foreach (string key in sortedKeys)
{
string scanPath = key;
@@ -784,7 +805,12 @@ namespace MPF.Frontend
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
/// <param name="outputFilename">Output filename to use as the base path</param>
/// <returns>True on success, false on error</returns>
private static async Task<(bool, string)> WriteIRD(string isoPath, string? discKeyString, string? discIDString, string? picString, long? layerbreak, string? crc32)
private static async Task<bool> WriteIRD(string isoPath,
string? discKeyString,
string? discIDString,
string? picString,
long? layerbreak,
string? crc32)
{
try
{
@@ -794,7 +820,7 @@ namespace MPF.Frontend
// Parse disc key from submission info (Required)
byte[]? discKey = ProcessingTool.ParseHexKey(discKeyString);
if (discKey == null)
return (false, "Failed to create IRD: No key provided");
return false;
// Parse Disc ID from submission info (Optional)
byte[]? discID = ProcessingTool.ParseDiscID(discIDString);
@@ -823,12 +849,12 @@ namespace MPF.Frontend
// Write IRD to file
ird.Write(irdPath);
return (true, "IRD created!");
return true;
}
catch (Exception)
{
// We don't care what the error is
return (false, "Failed to create IRD");
return false;
}
}

View File

@@ -183,5 +183,39 @@ namespace MPF.Frontend
}
#endregion
#region Functionality Support
/// <summary>
/// Get if a system requires an anti-modchip scan
/// </summary>
public static bool SupportsAntiModchipScans(this RedumpSystem? system)
{
return system switch
{
RedumpSystem.SonyPlayStation => true,
_ => false,
};
}
/// <summary>
/// Get if a system requires a copy protection scan
/// </summary>
public static bool SupportsCopyProtectionScans(this RedumpSystem? system)
{
return system switch
{
RedumpSystem.AppleMacintosh => true,
RedumpSystem.EnhancedCD => true,
RedumpSystem.IBMPCcompatible => true,
RedumpSystem.PalmOS => true,
RedumpSystem.PocketPC => true,
RedumpSystem.RainbowDisc => true,
RedumpSystem.SonyElectronicBook => true,
_ => false,
};
}
#endregion
}
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend
@@ -17,34 +16,34 @@ namespace MPF.Frontend
/// <summary>
/// Set of accepted speeds for CD and GD media
/// </summary>
public static IList<int> CD => _speedValues.Where(s => s <= 72).ToList();
public static List<int> CD => _speedValues.FindAll(s => s <= 72);
/// <summary>
/// Set of accepted speeds for DVD media
/// </summary>
public static IList<int> DVD => _speedValues.Where(s => s <= 24).ToList();
public static List<int> DVD => _speedValues.FindAll(s => s <= 24);
/// <summary>
/// Set of accepted speeds for HD-DVD media
/// </summary>
public static IList<int> HDDVD => _speedValues.Where(s => s <= 24).ToList();
public static List<int> HDDVD => _speedValues.FindAll(s => s <= 24);
/// <summary>
/// Set of accepted speeds for BD media
/// </summary>
public static IList<int> BD => _speedValues.Where(s => s <= 16).ToList();
public static List<int> BD => _speedValues.FindAll(s => s <= 16);
/// <summary>
/// Set of accepted speeds for all other media
/// </summary>
public static IList<int> Unknown => _speedValues.Where(s => s <= 1).ToList();
public static List<int> Unknown => _speedValues.FindAll(s => s <= 1);
/// <summary>
/// Get list of all drive speeds for a given MediaType
/// </summary>
/// <param name="type">MediaType? that represents the current item</param>
/// <returns>Read-only list of drive speeds</returns>
public static IList<int> GetSpeedsForMediaType(MediaType? type)
public static List<int> GetSpeedsForMediaType(MediaType? type)
{
return type switch
{

View File

@@ -1,60 +1,51 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- 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.2.0</VersionPrefix>
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<NoWarn>NU1902;NU1903</NoWarn>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.2.4</VersionPrefix>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Common code for all MPF frontend 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>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Common code for all MPF frontend 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>
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.ExecutionContexts\MPF.ExecutionContexts.csproj" />
<ProjectReference Include="..\MPF.Processors\MPF.Processors.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.ExecutionContexts\MPF.ExecutionContexts.csproj" />
<ProjectReference Include="..\MPF.Processors\MPF.Processors.csproj" />
</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(`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.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>
<!-- Support for old .NET versions -->
<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.Net.Http" Version="4.3.4" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.13" 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="SabreTools.RedumpLib" Version="1.3.8" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.2.3" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="LibIRD" Version="0.9.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.5.2" />
</ItemGroup>
</Project>
</Project>

View File

@@ -83,6 +83,33 @@ namespace MPF.Frontend
set { Settings["EnableDarkMode"] = value.ToString(); }
}
/// <summary>
/// Enable purple mode for UI elements
/// </summary>
public bool EnablePurpMode
{
get { return GetBooleanSetting(Settings, "EnablePurpMode", false); }
set { Settings["EnablePurpMode"] = value.ToString(); }
}
/// <summary>
/// Custom color setting
/// </summary>
public string? CustomBackgroundColor
{
get { return GetStringSetting(Settings, "CustomBackgroundColor", null); }
set { Settings["CustomBackgroundColor"] = value; }
}
/// <summary>
/// Custom color setting
/// </summary>
public string? CustomTextColor
{
get { return GetStringSetting(Settings, "CustomTextColor", null); }
set { Settings["CustomTextColor"] = value; }
}
/// <summary>
/// Check for updates on startup
/// </summary>
@@ -644,7 +671,7 @@ namespace MPF.Frontend
/// <param name="settings"></param>
public Options(Dictionary<string, string?>? settings = null)
{
this.Settings = settings ?? [];
Settings = settings ?? [];
}
/// <summary>
@@ -661,8 +688,8 @@ namespace MPF.Frontend
/// </summary>
public string? this[string key]
{
get => this.Settings[key];
set => this.Settings[key] = value;
get => Settings[key];
set => Settings[key] = value;
}
#region Helpers

View File

@@ -0,0 +1,11 @@
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend
{
/// <summary>
/// Determines how user information is processed, if at all
/// </summary>
/// <param name="info">Submission info that may be overwritten</param>
/// <returns>True for successful updating, false or null otherwise</returns>
public delegate bool? ProcessUserInfoDelegate(ref SubmissionInfo? info);
}

View File

@@ -32,11 +32,7 @@ namespace MPF.Frontend
public ProcessingQueue(Action<T> customProcessing)
{
#if NET20 || NET35
_internalQueue = new Queue<T>();
#else
_internalQueue = new ConcurrentQueue<T>();
#endif
_internalQueue = [];
_customProcessing = customProcessing;
_tokenSource = new CancellationTokenSource();
#if NET20 || NET35
@@ -72,11 +68,7 @@ namespace MPF.Frontend
while (true)
{
// Nothing in the queue means we get to idle
#if NET20 || NET35
if (_internalQueue.Count == 0)
#else
if (_internalQueue.IsEmpty)
#endif
{
if (_tokenSource.IsCancellationRequested)
break;

View File

@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using SabreTools.RedumpLib.Data;
@@ -11,6 +10,33 @@ namespace MPF.Frontend.Tools
{
#region Information Extraction
/// <summary>
/// Get the default speed for a given media type from the supplied options
/// </summary>
public static int GetDefaultSpeedForMediaType(MediaType? mediaType, Options options)
{
return mediaType switch
{
// CD dump speed
MediaType.CDROM => options.PreferredDumpSpeedCD,
MediaType.GDROM => options.PreferredDumpSpeedCD,
// DVD dump speed
MediaType.DVD => options.PreferredDumpSpeedDVD,
MediaType.NintendoGameCubeGameDisc => options.PreferredDumpSpeedDVD,
MediaType.NintendoWiiOpticalDisc => options.PreferredDumpSpeedDVD,
// HD-DVD dump speed
MediaType.HDDVD => options.PreferredDumpSpeedHDDVD,
// BD dump speed
MediaType.BluRay => options.PreferredDumpSpeedBD,
// Default
_ => options.PreferredDumpSpeedCD,
};
}
/// <summary>
/// Get the current system from the drive volume label
/// </summary>
@@ -88,7 +114,7 @@ namespace MPF.Frontend.Tools
}
// If we didn't already try English, try it now
if (!languages.Contains(Language.English))
if (!Array.Exists(languages, l => l == Language.English))
return NormalizeDiscTitle(title, Language.English);
// If all fails, then the title didn't need normalization
@@ -116,7 +142,7 @@ namespace MPF.Frontend.Tools
language = Language.English;
// Get the title split into parts
string[] splitTitle = title!.Split(' ').Where(s => !string.IsNullOrEmpty(s)).ToArray();
string[] splitTitle = Array.FindAll(title!.Split(' '), s => !string.IsNullOrEmpty(s));
// If we only have one part, we can't do anything
if (splitTitle.Length <= 1)
@@ -534,32 +560,37 @@ namespace MPF.Frontend.Tools
/// String representing the message to display the the user.
/// String representing the new release URL.
/// </returns>
public static (bool different, string message, string? url) CheckForNewVersion()
public static void CheckForNewVersion(out bool different, out string message, out string? url)
{
try
{
// Get current assembly version
var assemblyVersion = Assembly.GetEntryAssembly()?.GetName()?.Version;
if (assemblyVersion == null)
return (false, "Assembly version could not be determined", null);
{
different = false;
message = "Assembly version could not be determined";
url = null;
return;
}
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}.{assemblyVersion.Build}";
// Get the latest tag from GitHub
var (tag, url) = GetRemoteVersionAndUrl();
bool different = version != tag && tag != null;
_ = GetRemoteVersionAndUrl(out string? tag, out url);
different = version != tag && tag != null;
string message = $"Local version: {version}"
message = $"Local version: {version}"
+ $"{Environment.NewLine}Remote version: {tag}"
+ (different
? $"{Environment.NewLine}The update URL has been added copied to your clipboard"
: $"{Environment.NewLine}You have the newest version!");
return (different, message, url);
}
catch (Exception ex)
{
return (false, ex.ToString(), null);
different = false;
message = ex.ToString();
url = null;
}
}
@@ -586,11 +617,12 @@ namespace MPF.Frontend.Tools
/// <summary>
/// Get the latest version of MPF from GitHub and the release URL
/// </summary>
private static (string? tag, string? url) GetRemoteVersionAndUrl()
private static bool GetRemoteVersionAndUrl(out string? tag, out string? url)
{
tag = null; url = null;
#if NET20 || NET35 || NET40
// Not supported in .NET Frameworks 2.0, 3.5, or 4.0
return (null, null);
return false;
#else
using var hc = new System.Net.Http.HttpClient();
#if NET452
@@ -598,22 +630,22 @@ namespace MPF.Frontend.Tools
#endif
// TODO: Figure out a better way than having this hardcoded...
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
var message = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
string releaseUrl = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
var message = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, releaseUrl);
message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
var latestReleaseJsonString = hc.SendAsync(message)?.ConfigureAwait(false).GetAwaiter().GetResult()
.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
if (latestReleaseJsonString == null)
return (null, null);
return false;
var latestReleaseJson = Newtonsoft.Json.Linq.JObject.Parse(latestReleaseJsonString);
if (latestReleaseJson == null)
return (null, null);
return false;
var latestTag = latestReleaseJson["tag_name"]?.ToString();
var releaseUrl = latestReleaseJson["html_url"]?.ToString();
tag = latestReleaseJson["tag_name"]?.ToString();
url = latestReleaseJson["html_url"]?.ToString();
return (latestTag, releaseUrl);
return true;
#endif
}

View File

@@ -1,15 +1,22 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Newtonsoft.Json;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend.Tools
{
public static class OptionsLoader
{
private const string ConfigurationPath = "config.json";
/// <summary>
/// Full path to the configuration file used by the program
/// </summary>
#if NET20 || NET35 || NET40 || NET452
private static string ConfigurationPath => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "config.json");
#else
private static string ConfigurationPath => Path.Combine(AppContext.BaseDirectory, "config.json");
#endif
#region Arguments
@@ -21,7 +28,7 @@ namespace MPF.Frontend.Tools
{
// Help options
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
return false;
return null;
// List options
if (args[0] == "-lc" || args[0] == "--listcodes")
@@ -31,7 +38,7 @@ namespace MPF.Frontend.Tools
{
Console.WriteLine(siteCode);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-lm" || args[0] == "--listmedia")
@@ -41,7 +48,7 @@ namespace MPF.Frontend.Tools
{
Console.WriteLine(mediaType);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-lp" || args[0] == "--listprograms")
@@ -51,7 +58,7 @@ namespace MPF.Frontend.Tools
{
Console.WriteLine(program);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-ls" || args[0] == "--listsystems")
@@ -61,7 +68,7 @@ namespace MPF.Frontend.Tools
{
Console.WriteLine(system);
}
Console.ReadLine();
return true;
}
@@ -72,188 +79,36 @@ namespace MPF.Frontend.Tools
/// 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)
public static bool ProcessCommonArguments(string[] args, out MediaType mediaType, out RedumpSystem? system, out string? message)
{
// 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 = 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, SubmissionInfo?, string?, int) LoadFromArguments(string[] args, int startIndex = 0)
{
// Create the output values with defaults
var options = new Options()
{
RedumpUsername = null,
RedumpPassword = null,
InternalProgram = InternalProgram.NONE,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
CompressLogFiles = false,
DeleteUnnecessaryFiles = false,
};
// 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, hideDriveLetters = false;
// If we have no arguments, just return
if (args == null || args.Length == 0)
return (options, null, null, 0);
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
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 = Options.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = Options.ToInternalProgram(internalProgram);
startIndex++;
}
// Redump login
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
{
string[] credentials = args[startIndex].Split('=')[1].Split(';');
options.RedumpUsername = credentials[0];
options.RedumpPassword = credentials[1];
}
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
{
options.RedumpUsername = args[startIndex + 1];
options.RedumpPassword = args[startIndex + 2];
startIndex += 2;
}
// Pull all information (requires Redump login)
else if (args[startIndex].Equals("-a") || args[startIndex].Equals("--pull-all"))
{
options.PullAllInformation = true;
}
// Use a device path for physical checks
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
{
parsedPath = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
{
parsedPath = args[startIndex + 1];
startIndex++;
}
// Scan for protection (requires device path)
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
{
scan = 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"))
{
options.OutputSubmissionJSON = true;
}
// Compress log and extraneous files
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
{
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
{
break;
}
mediaType = MediaType.NONE;
system = null;
message = "Invalid number of arguments";
return false;
}
// Now deal with the complex options
options.ScanForProtection = scan && !string.IsNullOrEmpty(parsedPath);
options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(parsedPath);
return (options, info, parsedPath, startIndex);
}
/// <summary>
/// Return a list of supported arguments and descriptions
/// </summary>
public static List<string> PrintSupportedArguments()
{
var supportedArguments = new List<string>
// Check the MediaType
mediaType = ToMediaType(args[0].Trim('"'));
if (mediaType == MediaType.NONE)
{
"-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)",
"-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",
};
system = null;
message = $"{args[0]} is not a recognized media type";
return false;
}
return supportedArguments;
// Check the RedumpSystem
system = Extensions.ToRedumpSystem(args[1].Trim('"'));
if (system == null)
{
message = $"{args[1]} is not a recognized system";
return false;
}
message = null;
return true;
}
/// <summary>
@@ -428,7 +283,7 @@ namespace MPF.Frontend.Tools
{
if (!File.Exists(ConfigurationPath))
{
_ = File.Create(ConfigurationPath);
File.Create(ConfigurationPath).Dispose();
return new Options();
}
@@ -436,14 +291,38 @@ namespace MPF.Frontend.Tools
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?>;
reader.Dispose();
return new Options(settings);
}
/// <summary>
/// Save the current set of options to the application configuration
/// </summary>
public static void SaveToConfig(Options options)
public static void SaveToConfig(Options options, bool saveDefault = false)
{
// If default values should be saved as well
if (saveDefault)
{
PropertyInfo[] properties = typeof(Options).GetProperties();
foreach (var property in properties)
{
// Skip dictionary properties
if (property.Name == "Item")
continue;
// Skip non-option properties
if (property.Name == "Settings" || property.Name == "HasRedumpLogin")
continue;
var val = property.GetValue(options, null);
property.SetValue(options, val, null);
}
}
// Handle a very strange edge case
if (!File.Exists(ConfigurationPath))
File.Create(ConfigurationPath).Dispose();
var serializer = JsonSerializer.Create();
var sw = new StreamWriter(ConfigurationPath) { AutoFlush = true };
var writer = new JsonTextWriter(sw) { Formatting = Formatting.Indented };

View File

@@ -0,0 +1,590 @@
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SabreTools.IO;
namespace MPF.Frontend.Tools
{
public static class PhysicalTool
{
#region Generic
/// <summary>s
/// Get the last modified date for a file from a physical disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <param name="filePath">Relative file path</param>
/// <returns>Output last modified date in "yyyy-mm-dd" format if possible, null on error</returns>
public static string? GetFileDate(Drive? drive, string? filePath, bool fixTwoDigitYear = false)
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(drive?.Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(drive!.Name))
return null;
// If the executable name is invalid, we can't do this part
if (string.IsNullOrEmpty(filePath))
return null;
// Now that we have the EXE name, try to get the fileinfo for it
string exePath = Path.Combine(drive.Name, filePath);
if (!File.Exists(exePath))
return null;
// Get the last modified time
var fi = new FileInfo(exePath);
var lastModified = fi.LastWriteTimeUtc;
int year = lastModified.Year;
int month = lastModified.Month;
int day = lastModified.Day;
// Fix the Y2K timestamp issue, if required
if (fixTwoDigitYear)
year = year >= 1900 && year < 1920 ? 2000 + year % 100 : year;
// Format and return the string
var dt = new DateTime(year, month, day);
return dt.ToString("yyyy-MM-dd");
}
#endregion
#region BD-Video
/// <summary>
/// Get if the Bus Encryption Enabled (BEE) flag is set in a path
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Bus encryption enabled status if possible, false otherwise</returns>
public static bool GetBusEncryptionEnabled(Drive? drive)
{
// If there's no drive path, we can't get BEE flag
if (string.IsNullOrEmpty(drive?.Name))
return false;
// If the folder no longer exists, we can't get exe name
if (!Directory.Exists(drive!.Name))
return false;
// Get the two possible file paths
#if NET20 || NET35
string content000 = Path.Combine(Path.Combine(drive.Name, "AACS"), "Content000.cer");
string content001 = Path.Combine(Path.Combine(drive.Name, "AACS"), "Content001.cer");
#else
string content000 = Path.Combine(drive.Name, "AACS", "Content000.cer");
string content001 = Path.Combine(drive.Name, "AACS", "Content001.cer");
#endif
try
{
// Check the required files
if (File.Exists(content000) && new FileInfo(content000).Length > 1)
{
using var fs = File.OpenRead(content000);
_ = fs.ReadByte(); // Skip the first byte
return fs.ReadByte() > 127;
}
else if (File.Exists(content001) && new FileInfo(content001).Length > 1)
{
using var fs = File.OpenRead(content001);
_ = fs.ReadByte(); // Skip the first byte
return fs.ReadByte() > 127;
}
// False if neither file fits the criteria
return false;
}
catch
{
// We don't care what the error is right now
return false;
}
}
#endregion
#region PlayStation
/// <summary>
/// Get the EXE name from a PlayStation disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Executable name on success, null otherwise</returns>
public static string? GetPlayStationExecutableName(Drive? drive)
{
// If there's no drive path, we can't get exe name
if (string.IsNullOrEmpty(drive?.Name))
return null;
// If the folder no longer exists, we can't get exe name
if (!Directory.Exists(drive!.Name))
return null;
// Get the two paths that we will need to check
string psxExePath = Path.Combine(drive.Name, "PSX.EXE");
string systemCnfPath = Path.Combine(drive.Name, "SYSTEM.CNF");
// Read the CNF file as an INI file
var systemCnf = new IniFile(systemCnfPath);
string? bootValue = string.Empty;
// PlayStation uses "BOOT" as the key
if (systemCnf.ContainsKey("BOOT"))
bootValue = systemCnf["BOOT"];
// PlayStation 2 uses "BOOT2" as the key
if (systemCnf.ContainsKey("BOOT2"))
bootValue = systemCnf["BOOT2"];
// If we had any boot value, parse it and get the executable name
if (!string.IsNullOrEmpty(bootValue))
{
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)", RegexOptions.Compiled);
if (match.Groups.Count > 1)
{
string? serial = match.Groups[1].Value;
// Some games may have the EXE in a subfolder
serial = Path.GetFileName(serial);
return serial;
}
}
// If the SYSTEM.CNF value can't be found, try PSX.EXE
if (File.Exists(psxExePath))
return "PSX.EXE";
// If neither can be found, we return null
return null;
}
/// <summary>
/// Get the serial from a PlayStation disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Serial on success, null otherwise</returns>
public static string? GetPlayStationSerial(Drive? drive)
{
// Try to get the executable name
string? exeName = GetPlayStationExecutableName(drive);
if (string.IsNullOrEmpty(exeName))
return null;
// Handle generic PSX.EXE
if (exeName == "PSX.EXE")
return null;
// EXE name may have a trailing `;` after
// EXE name should always be in all caps
exeName = exeName!
.Split(';')[0]
.ToUpperInvariant();
// Serial is most of the EXE name normalized
return exeName
.Replace('_', '-')
.Replace(".", string.Empty);
}
/// <summary>
/// Get the version from a PlayStation 2 disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Game version if possible, null on error</returns>
public static string? GetPlayStation2Version(Drive? drive)
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(drive?.Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(drive!.Name))
return null;
// Get the SYSTEM.CNF path to check
string systemCnfPath = Path.Combine(drive.Name, "SYSTEM.CNF");
// Try to parse the SYSTEM.CNF file
var systemCnf = new IniFile(systemCnfPath);
if (systemCnf.ContainsKey("VER"))
return systemCnf["VER"];
// If "VER" can't be found, we can't do much
return null;
}
/// <summary>
/// Get the internal serial from a PlayStation 3 disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Internal disc serial if possible, null on error</returns>
public static string? GetPlayStation3Serial(Drive? drive)
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(drive?.Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(drive!.Name))
return null;
// Attempt to use PS3_DISC.SFB
string sfbPath = Path.Combine(drive.Name, "PS3_DISC.SFB");
if (File.Exists(sfbPath))
{
try
{
using var br = new BinaryReader(File.OpenRead(sfbPath));
br.BaseStream.Seek(0x220, SeekOrigin.Begin);
return new string(br.ReadChars(0x10)).TrimEnd('\0');
}
catch
{
// We don't care what the error was
return null;
}
}
// Attempt to use PARAM.SFO
#if NET20 || NET35
string sfoPath = Path.Combine(Path.Combine(drive.Name, "PS3_GAME"), "PARAM.SFO");
#else
string sfoPath = Path.Combine(drive.Name, "PS3_GAME", "PARAM.SFO");
#endif
if (File.Exists(sfoPath))
{
try
{
using var br = new BinaryReader(File.OpenRead(sfoPath));
br.BaseStream.Seek(-0x18, SeekOrigin.End);
return new string(br.ReadChars(9)).TrimEnd('\0').Insert(4, "-");
}
catch
{
// We don't care what the error was
return null;
}
}
return null;
}
/// <summary>
/// Get the version from a PlayStation 3 disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Game version if possible, null on error</returns>
public static string? GetPlayStation3Version(Drive? drive)
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(drive?.Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(drive!.Name))
return null;
// Attempt to use PS3_DISC.SFB
string sfbPath = Path.Combine(drive.Name, "PS3_DISC.SFB");
if (File.Exists(sfbPath))
{
try
{
using var br = new BinaryReader(File.OpenRead(sfbPath));
br.BaseStream.Seek(0x230, SeekOrigin.Begin);
var discVersion = new string(br.ReadChars(0x10)).TrimEnd('\0');
if (!string.IsNullOrEmpty(discVersion))
return discVersion;
}
catch
{
// We don't care what the error was
return null;
}
}
// Attempt to use PARAM.SFO
#if NET20 || NET35
string sfoPath = Path.Combine(Path.Combine(drive.Name, "PS3_GAME"), "PARAM.SFO");
#else
string sfoPath = Path.Combine(drive.Name, "PS3_GAME", "PARAM.SFO");
#endif
if (File.Exists(sfoPath))
{
try
{
using var br = new BinaryReader(File.OpenRead(sfoPath));
br.BaseStream.Seek(-0x08, SeekOrigin.End);
return new string(br.ReadChars(5)).TrimEnd('\0');
}
catch
{
// We don't care what the error was
return null;
}
}
return null;
}
/// <summary>
/// Get the firmware version from a PlayStation 3 disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Firmware version if possible, null on error</returns>
public static string? GetPlayStation3FirmwareVersion(Drive? drive)
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(drive?.Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(drive!.Name))
return null;
// Attempt to read from /PS3_UPDATE/PS3UPDAT.PUP
#if NET20 || NET35
string pupPath = Path.Combine(Path.Combine(drive.Name, "PS3_UPDATE"), "PS3UPDAT.PUP");
#else
string pupPath = Path.Combine(drive.Name, "PS3_UPDATE", "PS3UPDAT.PUP");
#endif
if (!File.Exists(pupPath))
return null;
try
{
using var br = new BinaryReader(File.OpenRead(pupPath));
br.BaseStream.Seek(0x3E, SeekOrigin.Begin);
byte[] buf = new byte[2];
br.Read(buf, 0, 2);
Array.Reverse(buf);
short location = BitConverter.ToInt16(buf, 0);
br.BaseStream.Seek(location, SeekOrigin.Begin);
return new string(br.ReadChars(4));
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the internal serial from a PlayStation 4 disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Internal disc serial if possible, null on error</returns>
public static string? GetPlayStation4Serial(Drive? drive)
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(drive?.Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(drive!.Name))
return null;
// If we can't find param.sfo, we don't have a PlayStation 4 disc
#if NET20 || NET35
string paramSfoPath = Path.Combine(Path.Combine(drive.Name, "bd"), "param.sfo");
#else
string paramSfoPath = Path.Combine(drive.Name, "bd", "param.sfo");
#endif
if (!File.Exists(paramSfoPath))
return null;
// Let's try reading param.sfo to find the serial at the end of the file
try
{
using var br = new BinaryReader(File.OpenRead(paramSfoPath));
br.BaseStream.Seek(-0x14, SeekOrigin.End);
return new string(br.ReadChars(9)).Insert(4, "-");
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the version from a PlayStation 4 disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Game version if possible, null on error</returns>
public static string? GetPlayStation4Version(Drive? drive)
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(drive?.Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(drive!.Name))
return null;
// If we can't find param.sfo, we don't have a PlayStation 4 disc
#if NET20 || NET35
string paramSfoPath = Path.Combine(Path.Combine(drive.Name, "bd"), "param.sfo");
#else
string paramSfoPath = Path.Combine(drive.Name, "bd", "param.sfo");
#endif
if (!File.Exists(paramSfoPath))
return null;
// Let's try reading param.sfo to find the version at the end of the file
try
{
using var br = new BinaryReader(File.OpenRead(paramSfoPath));
br.BaseStream.Seek(-0x08, SeekOrigin.End);
return new string(br.ReadChars(5));
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the internal serial from a PlayStation 5 disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Internal disc serial if possible, null on error</returns>
public static string? GetPlayStation5Serial(Drive? drive)
{
// Attempt to get the param.json file
var json = GetPlayStation5ParamsJsonFromDrive(drive);
if (json == null)
return null;
try
{
return json["disc"]?[0]?["masterDataId"]?.Value<string>()?.Insert(4, "-");
}
catch
{
// We don't care what the error was
return null;
}
}
// <summary>
/// Get the version from a PlayStation 5 disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Game version if possible, null on error</returns>
public static string? GetPlayStation5Version(Drive? drive)
{
// Attempt to get the param.json file
var json = GetPlayStation5ParamsJsonFromDrive(drive);
if (json == null)
return null;
try
{
return json["masterVersion"]?.Value<string>();
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the params.json file from a drive path, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>JObject representing the JSON on success, null on error</returns>
private static JObject? GetPlayStation5ParamsJsonFromDrive(Drive? drive)
{
// If there's no drive path, we can't do this part
if (string.IsNullOrEmpty(drive?.Name))
return null;
// If the folder no longer exists, we can't do this part
if (!Directory.Exists(drive!.Name))
return null;
// If we can't find param.json, we don't have a PlayStation 5 disc
#if NET20 || NET35
string paramJsonPath = Path.Combine(Path.Combine(drive.Name, "bd"), "param.json");
#else
string paramJsonPath = Path.Combine(drive.Name, "bd", "param.json");
#endif
return GetPlayStation5ParamsJsonFromFile(paramJsonPath);
}
/// <summary>
/// Get the params.json file from a filename, if possible
/// </summary>
/// <param name="filename">Filename to check</param>
/// <returns>JObject representing the JSON on success, null on error</returns>
private static JObject? GetPlayStation5ParamsJsonFromFile(string? filename)
{
// If the file doesn't exist
if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
return null;
// Let's try reading param.json to find the version in the unencrypted JSON
try
{
using var br = new BinaryReader(File.OpenRead(filename));
br.BaseStream.Seek(0x800, SeekOrigin.Begin);
byte[] jsonBytes = br.ReadBytes((int)(br.BaseStream.Length - 0x800));
return JsonConvert.DeserializeObject(Encoding.ASCII.GetString(jsonBytes)) as JObject;
}
catch
{
// We don't care what the error was
return null;
}
}
#endregion
#region Xbox
/// <summary>
/// Get all filenames for Xbox One and Xbox Series X
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>Filenames if possible, null on error</returns>
public static string? GetXboxFilenames(Drive? drive)
{
// If there's no drive path, we can't get BEE flag
if (string.IsNullOrEmpty(drive?.Name))
return null;
// If the folder no longer exists, we can't get exe name
if (!Directory.Exists(drive!.Name))
return null;
// Get the MSXC directory path
string msxc = Path.Combine(drive.Name, "MSXC");
if (!Directory.Exists(msxc))
return null;
try
{
var files = Directory.GetFiles(msxc, "*", SearchOption.TopDirectoryOnly);
var filenames = Array.ConvertAll(files, Path.GetFileName);
return string.Join("\n", filenames);
}
catch
{
// We don't care what the error is right now
return null;
}
}
#endregion
}
}

View File

@@ -1,37 +1,18 @@
using System;
using System.Collections.Generic;
using System.IO;
#if NET35_OR_GREATER || NETCOREAPP
using System.Linq;
#endif
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BinaryObjectScanner;
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'.
using SabreTools.IO.Extensions;
namespace MPF.Frontend.Tools
{
public static class ProtectionTool
{
/// <summary>
/// Get the current detected copy protection(s), if possible
/// </summary>
/// <param name="drive">Drive object representing the current drive</param>
/// <param name="options">Options object that determines what to scan</param>
/// <param name="progress">Optional progress callback</param>
/// <returns>Detected copy protection(s) if possible, null on error</returns>
public static async Task<(string?, Dictionary<string, List<string>>?)> GetCopyProtection(Drive? drive,
Frontend.Options options,
IProgress<ProtectionProgress>? progress = null)
{
if (options.ScanForProtection && drive?.Name != null)
{
(var protection, _) = await RunProtectionScanOnPath(drive.Name, options, progress);
return (FormatProtections(protection), protection);
}
return ("(CHECK WITH PROTECTIONID)", null);
}
/// <summary>
/// Run protection scan on a given path
/// </summary>
@@ -39,68 +20,37 @@ namespace MPF.Frontend.Tools
/// <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,
public static async Task<ProtectionDictionary> RunProtectionScanOnPath(string path,
Frontend.Options options,
IProgress<ProtectionProgress>? progress = null)
{
try
{
#if NET40
var found = await Task.Factory.StartNew(() =>
{
var scanner = new 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);
});
var found = await Task.Factory.StartNew(() =>
#else
var found = await Task.Run(() =>
{
var scanner = new 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);
});
var found = await Task.Run(() =>
#endif
// If nothing was returned, return
#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
#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());
// Return the filtered set of protections
return (filteredProtections, null);
}
catch (Exception ex)
{
return (null, ex.ToString());
}
var scanner = new 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);
});
// If nothing was returned, return
if (found == null || found.Count == 0)
return [];
// Filter out any empty protections
found.ClearEmptyKeys();
// Return the filtered set of protections
return found;
}
/// <summary>
@@ -108,20 +58,37 @@ namespace MPF.Frontend.Tools
/// </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(ProtectionDictionary? protections)
{
// If the filtered list is empty in some way, return
if (protections == null || !protections.Any())
if (protections == null)
return "(CHECK WITH PROTECTIONID)";
else if (protections.Count == 0)
return "None found [OMIT FROM SUBMISSION]";
// Get an ordered list of distinct found protections
var orderedDistinctProtections = protections
// Get a list of distinct found protections
#if NET20
var protectionValues = new List<string>();
foreach (var value in protections.Values)
{
if (value.Count == 0)
continue;
foreach (var prot in value)
{
if (!protectionValues.Contains(prot))
protectionValues.Add(prot);
}
}
#else
var protectionValues = protections
.SelectMany(kvp => kvp.Value)
.Distinct()
.OrderBy(p => p);
.ToList();
#endif
// Sanitize and join protections for writing
string protectionString = SanitizeFoundProtections(orderedDistinctProtections);
string protectionString = SanitizeFoundProtections(protectionValues);
if (string.IsNullOrEmpty(protectionString))
return "None found [OMIT FROM SUBMISSION]";
@@ -141,37 +108,14 @@ namespace MPF.Frontend.Tools
#if NET40
return await Task.Factory.StartNew(() =>
{
try
{
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
{
try
{
byte[] fileContent = File.ReadAllBytes(file);
var protection = antiModchip.CheckContents(file, fileContent, false);
if (!string.IsNullOrEmpty(protection))
return true;
}
catch { }
}
}
catch { }
return false;
});
#else
return await Task.Run(() =>
#endif
{
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
foreach (string file in IOExtensions.SafeGetFiles(path!, "*", SearchOption.AllDirectories))
{
try
{
@@ -187,86 +131,99 @@ namespace MPF.Frontend.Tools
return false;
});
#endif
}
/// <summary>
/// Sanitize unnecessary protection duplication from output
/// </summary>
/// <param name="foundProtections">Enumerable of found protections</param>
public static string SanitizeFoundProtections(IEnumerable<string> foundProtections)
public static string SanitizeFoundProtections(List<string> foundProtections)
{
// EXCEPTIONS
if (foundProtections.Any(p => p.StartsWith("[Exception opening file")))
if (foundProtections.Exists(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
foundProtections = foundProtections.FindAll(p => !p.StartsWith("[Exception opening file"));
foundProtections.Add("Exception occurred while scanning [RESCAN NEEDED]");
}
// ActiveMARK
if (foundProtections.Any(p => p == "ActiveMARK 5") && foundProtections.Any(p => p == "ActiveMARK"))
foundProtections = foundProtections.Where(p => p != "ActiveMARK");
if (foundProtections.Exists(p => p == "ActiveMARK 5")
&& foundProtections.Exists(p => p == "ActiveMARK"))
{
foundProtections = foundProtections.FindAll(p => p != "ActiveMARK");
}
// Cactus Data Shield
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");
if (foundProtections.Exists(p => Regex.IsMatch(p, @"Cactus Data Shield [0-9]{3} .+", RegexOptions.Compiled))
&& foundProtections.Exists(p => p == "Cactus Data Shield 200"))
{
foundProtections = foundProtections.FindAll(p => p != "Cactus Data Shield 200");
}
// CD-Check
foundProtections = foundProtections.Where(p => p != "Executable-Based CD Check");
foundProtections = foundProtections.FindAll(p => p != "Executable-Based CD Check");
// CD-Cops
if (foundProtections.Any(p => p == "CD-Cops") && foundProtections.Any(p => p.StartsWith("CD-Cops") && p.Length > "CD-Cops".Length))
foundProtections = foundProtections.Where(p => p != "CD-Cops");
if (foundProtections.Exists(p => p == "CD-Cops") && foundProtections.Exists(p => p.StartsWith("CD-Cops") && p.Length > "CD-Cops".Length))
foundProtections = foundProtections.FindAll(p => p != "CD-Cops");
// CD-Key / Serial
foundProtections = foundProtections.Where(p => p != "CD-Key / Serial");
foundProtections = foundProtections.FindAll(p => p != "CD-Key / Serial");
// Electronic Arts
if (foundProtections.Any(p => p == "EA CdKey Registration Module") && foundProtections.Any(p => p.StartsWith("EA CdKey Registration Module") && p.Length > "EA CdKey Registration Module".Length))
foundProtections = foundProtections.Where(p => p != "EA CdKey Registration Module");
if (foundProtections.Any(p => p == "EA DRM Protection") && foundProtections.Any(p => p.StartsWith("EA DRM Protection") && p.Length > "EA DRM Protection".Length))
foundProtections = foundProtections.Where(p => p != "EA DRM Protection");
if (foundProtections.Exists(p => p == "EA CdKey Registration Module")
&& foundProtections.Exists(p => p.StartsWith("EA CdKey Registration Module")
&& p.Length > "EA CdKey Registration Module".Length))
{
foundProtections = foundProtections.FindAll(p => p != "EA CdKey Registration Module");
}
if (foundProtections.Exists(p => p == "EA DRM Protection")
&& foundProtections.Exists(p => p.StartsWith("EA DRM Protection")
&& p.Length > "EA DRM Protection".Length))
{
foundProtections = foundProtections.FindAll(p => p != "EA DRM Protection");
}
// Games for Windows LIVE
if (foundProtections.Any(p => p == "Games for Windows LIVE") && foundProtections.Any(p => p.StartsWith("Games for Windows LIVE") && !p.Contains("Zero Day Piracy Protection") && p.Length > "Games for Windows LIVE".Length))
foundProtections = foundProtections.Where(p => p != "Games for Windows LIVE");
if (foundProtections.Exists(p => p == "Games for Windows LIVE")
&& foundProtections.Exists(p => p.StartsWith("Games for Windows LIVE")
&& !p.Contains("Zero Day Piracy Protection")
&& p.Length > "Games for Windows LIVE".Length))
{
foundProtections = foundProtections.FindAll(p => p != "Games for Windows LIVE");
}
// Impulse Reactor
if (foundProtections.Any(p => p.StartsWith("Impulse Reactor Core Module")) && foundProtections.Any(p => p == "Impulse Reactor"))
foundProtections = foundProtections.Where(p => p != "Impulse Reactor");
if (foundProtections.Exists(p => p.StartsWith("Impulse Reactor Core Module"))
&& foundProtections.Exists(p => p == "Impulse Reactor"))
{
foundProtections = foundProtections.FindAll(p => p != "Impulse Reactor");
}
// JoWood X-Prot
if (foundProtections.Any(p => p.StartsWith("JoWood X-Prot")))
if (foundProtections.Exists(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}", RegexOptions.Compiled)))
if (foundProtections.Exists(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")
.Where(p => p != "JoWood X-Prot v1.4+")
.Where(p => p != "JoWood X-Prot v2");
foundProtections = foundProtections.FindAll(p => p != "JoWood X-Prot")
.FindAll(p => p != "JoWood X-Prot v1.0-v1.3")
.FindAll(p => p != "JoWood X-Prot v1.4+")
.FindAll(p => p != "JoWood X-Prot v2");
}
else if (foundProtections.Any(p => p == "JoWood X-Prot v2"))
else if (foundProtections.Exists(p => p == "JoWood X-Prot v2"))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
.Where(p => p != "JoWood X-Prot v1.0-v1.3")
.Where(p => p != "JoWood X-Prot v1.4+");
foundProtections = foundProtections.FindAll(p => p != "JoWood X-Prot")
.FindAll(p => p != "JoWood X-Prot v1.0-v1.3")
.FindAll(p => p != "JoWood X-Prot v1.4+");
}
else if (foundProtections.Any(p => p == "JoWood X-Prot v1.4+"))
else if (foundProtections.Exists(p => p == "JoWood X-Prot v1.4+"))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
.Where(p => p != "JoWood X-Prot v1.0-v1.3");
foundProtections = foundProtections.FindAll(p => p != "JoWood X-Prot")
.FindAll(p => p != "JoWood X-Prot v1.0-v1.3");
}
else if (foundProtections.Any(p => p == "JoWood X-Prot v1.0-v1.3"))
else if (foundProtections.Exists(p => p == "JoWood X-Prot v1.0-v1.3"))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot");
foundProtections = foundProtections.FindAll(p => p != "JoWood X-Prot");
}
}
@@ -274,7 +231,7 @@ namespace MPF.Frontend.Tools
// TODO: Figure this one out
// Online Registration
foundProtections = foundProtections.Where(p => !p.StartsWith("Executable-Based Online Registration"));
foundProtections = foundProtections.FindAll(p => !p.StartsWith("Executable-Based Online Registration"));
// ProtectDISC / VOB ProtectCD/DVD
// TODO: Figure this one out
@@ -283,76 +240,127 @@ namespace MPF.Frontend.Tools
// TODO: Figure this one out
// Cactus Data Shield / SafeDisc
if (foundProtections.Any(p => p == "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)"))
if (foundProtections.Exists(p => p == "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)"))
{
foundProtections = foundProtections
.Where(p => p != "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)");
.FindAll(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
}
if (foundProtections.Exists(p => !p.StartsWith("SafeDisc")))
foundProtections.Add("Cactus Data Shield 300");
}
// SafeDisc
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
if (foundProtections.Exists(p => p.StartsWith("SafeDisc")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled) && !p.StartsWith("Macrovision Protection File")))
// Confirmed this set of checks works with Redump entries 10430, 11347, 13230, 18614, 28257, 31149, 31824, 52606, 57721, 58455, 58573, 62935, 63941, 64255, 65569, 66005, 70504, 73502, 74520, 78048, 79729, 83468, 98589, and 101261.
// Best case scenario for SafeDisc 2+: A full SafeDisc version is found in a line starting with "Macrovision Protected Application". All other SafeDisc detections can be safely scrubbed.
// TODO: Scrub "Macrovision Protected Application, " from before the SafeDisc version.
if (foundProtections.Exists(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled) && p.StartsWith("Macrovision Protected Application") && !p.Contains("SRV Tool APP")))
{
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}", 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+");
foundProtections = foundProtections.FindAll(p => !p.StartsWith("Macrovision Protection File"))
.FindAll(p => !p.StartsWith("Macrovision Security Driver"))
.FindAll(p => !p.Contains("SRV Tool APP"))
.FindAll(p => p != "SafeDisc")
.FindAll(p => !p.StartsWith("Macrovision Protected Application [Version Expunged]"))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\/4\+", RegexOptions.Compiled)))
.FindAll(p => p != "SafeDisc 1/Lite")
.FindAll(p => p != "SafeDisc 2+")
.FindAll(p => p != "SafeDisc 3+ (DVD)");
}
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")))
// Next best case for SafeDisc 2+: A full SafeDisc version is found from the "SafeDisc SRV Tool APP".
else if (foundProtections.Exists(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled) && p.StartsWith("Macrovision Protected Application") && p.Contains("SRV Tool APP")))
{
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}/+", RegexOptions.Compiled)))
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
foundProtections = foundProtections.FindAll(p => !p.StartsWith("Macrovision Protection File"))
.FindAll(p => !p.StartsWith("Macrovision Security Driver"))
.FindAll(p => p != "SafeDisc")
.FindAll(p => !p.StartsWith("Macrovision Protected Application [Version Expunged]"))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\/4\+", RegexOptions.Compiled)))
.FindAll(p => p != "SafeDisc 1/Lite")
.FindAll(p => p != "SafeDisc 2+")
.FindAll(p => p != "SafeDisc 3+ (DVD)");
}
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled) && !p.StartsWith("Macrovision Protection File")))
// Covers specific edge cases where older drivers are erroneously placed in discs with a newer version of SafeDisc, and the specific SafeDisc version is expunged.
else if (foundProtections.Exists(p => Regex.IsMatch(p, @"SafeDisc [1-2]\.[0-9]{2}\.[0-9]{3}-[1-2]\.[0-9]{2}\.[0-9]{3}$", RegexOptions.Compiled) || Regex.IsMatch(p, @"SafeDisc [1-2]\.[0-9]{2}\.[0-9]{3}$", RegexOptions.Compiled)) && foundProtections.Exists(p => p == "SafeDisc 3+ (DVD)"))
{
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 => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
foundProtections = foundProtections.FindAll(p => !p.StartsWith("Macrovision Protection File"))
.FindAll(p => !p.StartsWith("Macrovision Protected Application [Version Expunged]"))
.FindAll(p => !p.StartsWith("Macrovision Security Driver"))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [1-2]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [1-2]\.[0-9]{2}\.[0-9]{3}-[1-2]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.FindAll(p => p != "SafeDisc")
.FindAll(p => p != "SafeDisc 1/Lite")
.FindAll(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => p.StartsWith("Macrovision Security Driver")))
// Best case for SafeDisc 1.X: A full SafeDisc version is found that isn't part of a version range.
else if (foundProtections.Exists(p => Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}$", RegexOptions.Compiled) && !(Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled))))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
foundProtections = foundProtections.FindAll(p => !p.StartsWith("Macrovision Protection File"))
.FindAll(p => !p.StartsWith("Macrovision Security Driver"))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)))
.FindAll(p => p != "SafeDisc")
.FindAll(p => p != "SafeDisc 1")
.FindAll(p => p != "SafeDisc 1/Lite");
}
else if (foundProtections.Any(p => p == "SafeDisc 2+"))
// Next best case for SafeDisc 1: A SafeDisc version range is found from "SECDRV.SYS".
// TODO: Scrub "Macrovision Security Driver {Version}" from before the SafeDisc version.
else if (foundProtections.Exists(p => p.StartsWith("Macrovision Security Driver") && Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}-[1-2]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled) || Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}$")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => p != "SafeDisc");
foundProtections = foundProtections.FindAll(p => !p.StartsWith("Macrovision Protection File"))
.FindAll(p => !p.StartsWith("Macrovision Protected Application [Version Expunged]"))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.FindAll(p => p != "SafeDisc")
.FindAll(p => p != "SafeDisc 1")
.FindAll(p => p != "SafeDisc 1/Lite");
}
else if (foundProtections.Any(p => p == "SafeDisc 1/Lite"))
// Next best case for SafeDisc 2+: A SafeDisc version range is found from "SECDRV.SYS".
// TODO: Scrub "Macrovision Security Driver {Version}" from before the SafeDisc version.
else if (foundProtections.Exists(p => p.StartsWith("Macrovision Security Driver")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => p != "SafeDisc");
foundProtections = foundProtections.FindAll(p => !p.StartsWith("Macrovision Protection File"))
.FindAll(p => !p.StartsWith("Macrovision Protected Application [Version Expunged]"))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.FindAll(p => p != "SafeDisc")
.FindAll(p => p != "SafeDisc 1")
.FindAll(p => p != "SafeDisc 1/Lite")
.FindAll(p => p != "SafeDisc 2+")
.FindAll(p => p != "SafeDisc 3+ (DVD)");
}
// Only SafeDisc Lite is found.
else if (foundProtections.Exists(p => p == "SafeDisc Lite"))
{
foundProtections = foundProtections.FindAll(p => p != "SafeDisc")
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}-1\.[0-9]{2}\.[0-9]{3}\/Lite", RegexOptions.Compiled)));
}
// Only SafeDisc 3+ is found.
else if (foundProtections.Exists(p => p == "SafeDisc 3+ (DVD)"))
{
foundProtections = foundProtections.FindAll(p => p != "SafeDisc")
.FindAll(p => p != "SafeDisc 2+")
.FindAll(p => !p.StartsWith("Macrovision Protection File"))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)));
}
// Only SafeDisc 2+ is found.
else if (foundProtections.Exists(p => p == "SafeDisc 2+"))
{
foundProtections = foundProtections.FindAll(p => p != "SafeDisc")
.FindAll(p => !p.StartsWith("Macrovision Protection File"))
.FindAll(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)));
}
}
@@ -363,44 +371,52 @@ namespace MPF.Frontend.Tools
// TODO: Figure this one out
// StarForce
if (foundProtections.Any(p => p.StartsWith("StarForce")))
if (foundProtections.Exists(p => p.StartsWith("StarForce")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"StarForce [0-9]+\..+", RegexOptions.Compiled)))
if (foundProtections.Exists(p => Regex.IsMatch(p, @"StarForce [0-9]+\..+", RegexOptions.Compiled)))
{
foundProtections = foundProtections.Where(p => p != "StarForce")
.Where(p => p != "StarForce 3-5")
.Where(p => p != "StarForce 5")
.Where(p => p != "StarForce 5 [Protected Module]");
foundProtections = foundProtections.FindAll(p => p != "StarForce")
.FindAll(p => p != "StarForce 3-5")
.FindAll(p => p != "StarForce 5")
.FindAll(p => p != "StarForce 5 [Protected Module]");
}
else if (foundProtections.Any(p => p == "StarForce 5 [Protected Module]"))
else if (foundProtections.Exists(p => p == "StarForce 5 [Protected Module]"))
{
foundProtections = foundProtections.Where(p => p != "StarForce")
.Where(p => p != "StarForce 3-5")
.Where(p => p != "StarForce 5");
foundProtections = foundProtections.FindAll(p => p != "StarForce")
.FindAll(p => p != "StarForce 3-5")
.FindAll(p => p != "StarForce 5");
}
else if (foundProtections.Any(p => p == "StarForce 5"))
else if (foundProtections.Exists(p => p == "StarForce 5"))
{
foundProtections = foundProtections.Where(p => p != "StarForce")
.Where(p => p != "StarForce 3-5");
foundProtections = foundProtections.FindAll(p => p != "StarForce")
.FindAll(p => p != "StarForce 3-5");
}
else if (foundProtections.Any(p => p == "StarForce 3-5"))
else if (foundProtections.Exists(p => p == "StarForce 3-5"))
{
foundProtections = foundProtections.Where(p => p != "StarForce");
foundProtections = foundProtections.FindAll(p => p != "StarForce");
}
}
// Sysiphus
if (foundProtections.Any(p => p == "Sysiphus") && foundProtections.Any(p => p.StartsWith("Sysiphus") && p.Length > "Sysiphus".Length))
foundProtections = foundProtections.Where(p => p != "Sysiphus");
if (foundProtections.Exists(p => p == "Sysiphus")
&& foundProtections.Exists(p => p.StartsWith("Sysiphus") && p.Length > "Sysiphus".Length))
{
foundProtections = foundProtections.FindAll(p => p != "Sysiphus");
}
// TAGES
// TODO: Figure this one out
// XCP
if (foundProtections.Any(p => p == "XCP") && foundProtections.Any(p => p.StartsWith("XCP") && p.Length > "XCP".Length))
foundProtections = foundProtections.Where(p => p != "XCP");
if (foundProtections.Exists(p => p == "XCP")
&& foundProtections.Exists(p => p.StartsWith("XCP") && p.Length > "XCP".Length))
{
foundProtections = foundProtections.FindAll(p => p != "XCP");
}
return string.Join(", ", foundProtections.ToArray());
// Sort and return the protections
foundProtections.Sort();
return string.Join(", ", [.. foundProtections]);
}
}
}

View File

@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
#if NET35_OR_GREATER || NETCOREAPP
using System.Linq;
#endif
using System.Threading.Tasks;
using BinaryObjectScanner;
using MPF.Processors;
@@ -57,8 +59,8 @@ namespace MPF.Frontend.Tools
string outputFilename = Path.GetFileName(outputPath);
// Check that all of the relevant files are there
(bool foundFiles, List<string> missingFiles) = processor.FoundAllFiles(outputDirectory, outputFilename, false);
if (!foundFiles)
List<string> missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
if (missingFiles.Count > 0)
{
resultProgress?.Report(ResultEventArgs.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
resultProgress?.Report(ResultEventArgs.Failure($"This may indicate an issue with the hardware or media, including unsupported devices.\nPlease see dumping program documentation for more details."));
@@ -81,15 +83,11 @@ namespace MPF.Frontend.Tools
// Get specific tool output handling
processor?.GenerateSubmissionInfo(info, combinedBase, options.EnableRedumpCompatibility);
if (options.IncludeArtifacts)
processor?.GenerateArtifacts(info, combinedBase);
info.Artifacts = processor?.GenerateArtifacts(combinedBase);
// 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))
@@ -107,7 +105,7 @@ namespace MPF.Frontend.Tools
ProcessSystem(info, system, drive, options.AddPlaceholders, processor is DiscImageCreator, combinedBase);
// Run anti-modchip check, if necessary
if (drive != null && SupportsAntiModchipScans(system) && info.CopyProtection!.AntiModchip == YesNo.NULL)
if (drive != null && system.SupportsAntiModchipScans() && info.CopyProtection!.AntiModchip == YesNo.NULL)
{
resultProgress?.Report(ResultEventArgs.Success("Checking for anti-modchip strings... this might take a while!"));
info.CopyProtection.AntiModchip = await ProtectionTool.GetPlayStationAntiModchipDetected(drive?.Name) ? YesNo.Yes : YesNo.No;
@@ -115,14 +113,26 @@ namespace MPF.Frontend.Tools
}
// Run copy protection, if possible or necessary
if (SupportsCopyProtectionScans(system))
if (system.SupportsCopyProtectionScans())
{
resultProgress?.Report(ResultEventArgs.Success("Running copy protection scan... this might take a while!"));
var (protectionString, fullProtections) = await ProtectionTool.GetCopyProtection(drive, options, protectionProgress);
info.CopyProtection!.Protection += protectionString;
info.CopyProtection.FullProtections = fullProtections as Dictionary<string, List<string>?> ?? [];
resultProgress?.Report(ResultEventArgs.Success("Copy protection scan complete!"));
ProtectionDictionary? protections = null;
try
{
if (options.ScanForProtection && drive?.Name != null)
protections = await ProtectionTool.RunProtectionScanOnPath(drive.Name, options, protectionProgress);
var protectionString = ProtectionTool.FormatProtections(protections);
info.CopyProtection!.Protection += protectionString;
info.CopyProtection.FullProtections = ReformatProtectionDictionary(protections);
resultProgress?.Report(ResultEventArgs.Success("Copy protection scan complete!"));
}
catch (Exception ex)
{
resultProgress?.Report(ResultEventArgs.Failure(ex.ToString()));
}
}
// Set fields that may have automatic filling otherwise
@@ -147,11 +157,9 @@ namespace MPF.Frontend.Tools
/// <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(Frontend.Options options, SubmissionInfo info, IProgress<ResultEventArgs>? resultProgress = null)
#else
public async static Task<bool> FillFromRedump(Frontend.Options options, SubmissionInfo info, IProgress<ResultEventArgs>? resultProgress = null)
#endif
public async static Task<bool> FillFromRedump(Frontend.Options options,
SubmissionInfo info,
IProgress<ResultEventArgs>? resultProgress = null)
{
// If no username is provided
if (string.IsNullOrEmpty(options.RedumpUsername) || string.IsNullOrEmpty(options.RedumpPassword))
@@ -163,13 +171,8 @@ namespace MPF.Frontend.Tools
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
var wc = new RedumpClient();
bool? loggedIn = await wc.Login(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty);
if (loggedIn == null)
{
resultProgress?.Report(ResultEventArgs.Failure("There was an unknown error connecting to Redump"));
@@ -183,7 +186,7 @@ namespace MPF.Frontend.Tools
// Setup the checks
bool allFound = true;
List<int[]> foundIdSets = [];
List<int[]> foundIdSets = [];
// Loop through all of the hashdata to find matching IDs
resultProgress?.Report(ResultEventArgs.Success("Finding disc matches on Redump..."));
@@ -221,109 +224,96 @@ namespace MPF.Frontend.Tools
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(ResultEventArgs.Success(result));
var foundIds = await Validator.ValidateSingleTrack(wc, info, sha1);
if (foundIds != null && foundIds.Count == 1)
resultProgress?.Report(ResultEventArgs.Success($"Single match found for {sha1}"));
else if (foundIds != null && foundIds.Count != 1)
resultProgress?.Report(ResultEventArgs.Success($"Multiple matches found for {sha1}"));
else
resultProgress?.Report(ResultEventArgs.Failure(result));
resultProgress?.Report(ResultEventArgs.Failure($"No matches found for {sha1}"));
// Add the found IDs to the map
foundIdSets.Add(foundIds?.ToArray() ?? []);
// Ensure that all tracks are found
allFound &= singleFound;
allFound &= (foundIds != null && foundIds.Count >= 1);
}
// If all tracks were found, check if there are any fully-matched IDs
List<int>? fullyMatchedIDs = null;
HashSet<int>? fullyMatchedIdsSet = null;
if (allFound)
{
fullyMatchedIDs = null;
fullyMatchedIdsSet = null;
foreach (var set in foundIdSets)
{
// First track is always all IDs
if (fullyMatchedIDs == null)
if (fullyMatchedIdsSet == null)
{
fullyMatchedIDs = [.. set];
fullyMatchedIdsSet = [.. set];
continue;
}
// Try to intersect with all known IDs
fullyMatchedIDs = fullyMatchedIDs.Intersect(set).ToList();
if (!fullyMatchedIDs.Any())
fullyMatchedIdsSet.IntersectWith(set);
if (fullyMatchedIdsSet.Count == 0)
break;
}
}
// 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 (info.PartiallyMatchedIDs.Count == 0 && 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(ResultEventArgs.Success(result));
string sha1 = info.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash];
var foundIds = await Validator.ValidateUniversalHash(wc, info);
if (foundIds != null && foundIds.Count == 1)
resultProgress?.Report(ResultEventArgs.Success($"Single match found for universal hash {sha1}"));
else if (foundIds != null && foundIds.Count != 1)
resultProgress?.Report(ResultEventArgs.Success($"Multiple matches found for universal hash {sha1}"));
else
resultProgress?.Report(ResultEventArgs.Failure(result));
resultProgress?.Report(ResultEventArgs.Failure($"No matches found for universal hash {sha1}"));
// Ensure that the hash is found
allFound = singleFound;
allFound = (foundIds != null && foundIds.Count == 1);
// If we found a match, then the disc is a match
if (singleFound && foundIds != null)
fullyMatchedIDs = foundIds;
if (foundIds != null && foundIds.Count == 1)
fullyMatchedIdsSet = [.. foundIds];
else
fullyMatchedIDs = [];
fullyMatchedIdsSet = [];
}
// Make sure we only have unique IDs
info.PartiallyMatchedIDs = [.. info.PartiallyMatchedIDs.Distinct().OrderBy(id => id)];
// Get a list version of the fully matched IDs
List<int> fullyMatchedIdsList = fullyMatchedIdsSet != null ? [.. fullyMatchedIdsSet] : [];
resultProgress?.Report(ResultEventArgs.Success("Match finding complete! " + (fullyMatchedIDs != null && fullyMatchedIDs.Count > 0
? "Fully Matched IDs: " + string.Join(",", fullyMatchedIDs.Select(i => i.ToString()).ToArray())
// Make sure we only have unique IDs
var partiallyMatchedIds = new HashSet<int>();
partiallyMatchedIds.IntersectWith(info.PartiallyMatchedIDs);
info.PartiallyMatchedIDs = [.. partiallyMatchedIds];
info.PartiallyMatchedIDs.Sort();
resultProgress?.Report(ResultEventArgs.Success("Match finding complete! " + (fullyMatchedIdsList != null && fullyMatchedIdsList.Count > 0
? "Fully Matched IDs: " + string.Join(",", [.. fullyMatchedIdsList.ConvertAll(i => i.ToString())])
: "No matches found")));
// Exit early if one failed or there are no matched IDs
if (!allFound || fullyMatchedIDs == null || fullyMatchedIDs.Count == 0)
if (!allFound || fullyMatchedIdsList == null || fullyMatchedIdsList.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;
int totalMatchedIDsCount = fullyMatchedIdsList.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
if (!await Validator.ValidateTrackCount(wc, fullyMatchedIdsList[i], trackCount))
continue;
// Fill in the fields from the existing ID
resultProgress?.Report(ResultEventArgs.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(ResultEventArgs.Success($"Filling fields from existing ID {fullyMatchedIdsList[i]}..."));
_ = await Builder.FillFromId(wc, info, fullyMatchedIdsList[i], options.PullAllInformation);
resultProgress?.Report(ResultEventArgs.Success("Information filling complete!"));
// Set the fully matched ID to the current
info.FullyMatchedID = fullyMatchedIDs[i];
info.FullyMatchedID = fullyMatchedIdsList[i];
break;
}
@@ -428,7 +418,13 @@ namespace MPF.Frontend.Tools
}
// If only one label, don't mention fs
#if NET20
string[] keyArr = new string[labels.Count];
labels.Keys.CopyTo(keyArr, 0);
string firstLabel = keyArr[0];
#else
string firstLabel = labels.First().Key;
#endif
if (labels.Count == 1 && (firstLabel == driveLabel || driveLabel == null))
{
// Ignore common volume labels
@@ -454,7 +450,7 @@ namespace MPF.Frontend.Tools
}
// Ensure that no labels are empty
volLabels = volLabels.Where(l => !string.IsNullOrEmpty(l?.Trim())).ToList();
volLabels = volLabels.FindAll(l => !string.IsNullOrEmpty(l?.Trim()));
// Print each label separated by a comma and a space
if (volLabels.Count == 0)
@@ -558,7 +554,7 @@ namespace MPF.Frontend.Tools
info.CommonDiscInfo.Layer0MouldSID = addPlaceholders ? RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = addPlaceholders ? RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = addPlaceholders ? RequiredIfExistsValue : string.Empty;
info.Extras!.BCA ??= (addPlaceholders ? RequiredValue : string.Empty);
info.Extras!.BCA ??= addPlaceholders ? RequiredValue : string.Empty;
break;
case MediaType.NintendoWiiOpticalDisc:
@@ -589,7 +585,7 @@ namespace MPF.Frontend.Tools
}
info.Extras!.DiscKey = addPlaceholders ? RequiredValue : string.Empty;
info.Extras.BCA = info.Extras.BCA ?? (addPlaceholders ? RequiredValue : string.Empty);
info.Extras.BCA ??= addPlaceholders ? RequiredValue : string.Empty;
break;
@@ -639,6 +635,17 @@ namespace MPF.Frontend.Tools
break;
case RedumpSystem.BDVideo:
info.CommonDiscInfo!.Category ??= DiscCategory.Video;
bool bee = PhysicalTool.GetBusEncryptionEnabled(drive);
if (bee && string.IsNullOrEmpty(info.CopyProtection!.Protection))
info.CopyProtection.Protection = "Bus encryption enabled flag set";
else if (bee)
info.CopyProtection!.Protection += "\nBus encryption enabled flag set";
else
info.CopyProtection!.Protection ??= addPlaceholders ? RequiredIfExistsValue : string.Empty;
break;
case RedumpSystem.DVDVideo:
case RedumpSystem.HDDVDVideo:
info.CommonDiscInfo!.Category ??= DiscCategory.Video;
@@ -694,25 +701,19 @@ namespace MPF.Frontend.Tools
break;
case RedumpSystem.KonamiPython2:
string? kp2Exe = PhysicalTool.GetPlayStationExecutableName(drive);
// TODO: Remove this hack when DIC supports build date output
if (isDiscImageCreator)
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", drive?.GetPlayStationExecutableName());
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", kp2Exe);
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? kp2Exe) || string.IsNullOrEmpty(kp2Exe))
info.CommonDiscInfo.Region = Drive.GetPlayStationRegion(kp2Exe);
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStationSerial);
info.CommonDiscInfo!.EXEDateBuildDate ??= PhysicalTool.GetFileDate(drive, kp2Exe, fixTwoDigitYear: true);
if (drive?.GetPlayStationExecutableInfo(out var kp2Serial, out Region? kp2Region, out var kp2Date) == true)
{
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? value) || string.IsNullOrEmpty(value))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = kp2Serial ?? string.Empty;
info.CommonDiscInfo.Region ??= kp2Region;
info.CommonDiscInfo.EXEDateBuildDate ??= kp2Date;
}
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
info.VersionAndEditions!.Version = drive?.GetPlayStation2Version() ?? string.Empty;
if (CommentFieldExists(info, SiteCode.InternalSerialName, out kp2Exe))
info.CommonDiscInfo!.Region = ProcessingTool.GetPlayStationRegion(kp2Exe);
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation2Version);
break;
case RedumpSystem.KonamiSystemGV:
@@ -732,29 +733,11 @@ namespace MPF.Frontend.Tools
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());
}
}
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = PhysicalTool.GetXboxFilenames(drive) ?? string.Empty;
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());
}
}
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = PhysicalTool.GetXboxFilenames(drive) ?? string.Empty;
break;
case RedumpSystem.NamcoSegaNintendoTriforce:
@@ -808,83 +791,54 @@ namespace MPF.Frontend.Tools
break;
case RedumpSystem.SonyPlayStation:
string? ps1Exe = PhysicalTool.GetPlayStationExecutableName(drive);
// TODO: Remove this hack when DIC supports build date output
if (isDiscImageCreator)
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", drive?.GetPlayStationExecutableName(), psx: true);
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", ps1Exe, psx: true);
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? psxExe) || string.IsNullOrEmpty(psxExe))
info.CommonDiscInfo.Region = Drive.GetPlayStationRegion(psxExe);
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStationSerial);
info.CommonDiscInfo!.EXEDateBuildDate ??= PhysicalTool.GetFileDate(drive, ps1Exe, fixTwoDigitYear: true);
if (drive?.GetPlayStationExecutableInfo(out var psxSerial, out Region? psxRegion, out var psxDate) == true)
{
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? value) || string.IsNullOrEmpty(value))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = psxSerial ?? string.Empty;
info.CommonDiscInfo.Region ??= psxRegion;
info.CommonDiscInfo.EXEDateBuildDate ??= psxDate;
}
if (CommentFieldExists(info, SiteCode.InternalSerialName, out ps1Exe))
info.CommonDiscInfo!.Region = ProcessingTool.GetPlayStationRegion(ps1Exe);
break;
case RedumpSystem.SonyPlayStation2:
info.CommonDiscInfo!.LanguageSelection ??= [];
string? ps2Exe = PhysicalTool.GetPlayStationExecutableName(drive);
// TODO: Remove this hack when DIC supports build date output
if (isDiscImageCreator)
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", drive?.GetPlayStationExecutableName());
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", ps2Exe);
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps2Exe) || string.IsNullOrEmpty(ps2Exe))
info.CommonDiscInfo.Region = Drive.GetPlayStationRegion(ps2Exe);
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStationSerial);
info.CommonDiscInfo!.EXEDateBuildDate ??= PhysicalTool.GetFileDate(drive, ps2Exe, fixTwoDigitYear: true);
if (drive?.GetPlayStationExecutableInfo(out var ps2Serial, out Region? ps2Region, out var ps2Date) == true)
{
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? value) || string.IsNullOrEmpty(value))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = ps2Serial ?? string.Empty;
info.CommonDiscInfo.Region ??= ps2Region;
info.CommonDiscInfo.EXEDateBuildDate ??= ps2Date;
}
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
info.VersionAndEditions!.Version = drive?.GetPlayStation2Version() ?? string.Empty;
if (CommentFieldExists(info, SiteCode.InternalSerialName, out ps2Exe))
info.CommonDiscInfo.Region = ProcessingTool.GetPlayStationRegion(ps2Exe);
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation2Version);
break;
case RedumpSystem.SonyPlayStation3:
info.Extras!.DiscKey ??= addPlaceholders ? RequiredValue : string.Empty;
info.Extras.DiscID ??= addPlaceholders ? RequiredValue : string.Empty;
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps3Serial) || string.IsNullOrEmpty(ps3Serial))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = drive?.GetPlayStation3Serial() ?? string.Empty;
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
info.VersionAndEditions!.Version = drive?.GetPlayStation3Version() ?? string.Empty;
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.Patches, out string? ps3Firmware) || string.IsNullOrEmpty(ps3Firmware))
{
string? firmwareVersion = drive?.GetPlayStation3FirmwareVersion();
if (firmwareVersion != null)
info.CommonDiscInfo!.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}";
}
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStation3Serial);
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation3Version);
SetContentFieldIfNotExists(info, SiteCode.Patches, drive, FormatPlayStation3FirmwareVersion);
break;
case RedumpSystem.SonyPlayStation4:
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps4Serial) || string.IsNullOrEmpty(ps4Serial))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = drive?.GetPlayStation4Serial() ?? string.Empty;
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
info.VersionAndEditions!.Version = drive?.GetPlayStation4Version() ?? string.Empty;
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStation4Serial);
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation4Version);
break;
case RedumpSystem.SonyPlayStation5:
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps5Serial) || string.IsNullOrEmpty(ps5Serial))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = drive?.GetPlayStation5Serial() ?? string.Empty;
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
info.VersionAndEditions!.Version = drive?.GetPlayStation5Version() ?? string.Empty;
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStation5Serial);
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation5Version);
break;
case RedumpSystem.TomyKissSite:
@@ -901,33 +855,113 @@ namespace MPF.Frontend.Tools
}
/// <summary>
/// Helper to determine if a system requires an anti-modchip scan
/// Get a preformatted string for the PS3 firmware version, if possible
/// </summary>
private static bool SupportsAntiModchipScans(RedumpSystem? system)
private static string? FormatPlayStation3FirmwareVersion(Drive? drive)
{
return system switch
{
RedumpSystem.SonyPlayStation => true,
_ => false,
};
string? firmwareVersion = PhysicalTool.GetPlayStation3FirmwareVersion(drive);
if (string.IsNullOrEmpty(firmwareVersion))
return string.Empty;
return $"PS3 Firmware {firmwareVersion}";
}
/// <summary>
/// Helper to determine if a system requires a copy protection scan
/// Determine if a comment field exists based on key
/// </summary>
private static bool SupportsCopyProtectionScans(RedumpSystem? system)
private static bool CommentFieldExists(SubmissionInfo info, SiteCode key, out string? value)
{
return system switch
// Ensure the comments fields exist
if (info.CommonDiscInfo!.CommentsSpecialFields == null)
info.CommonDiscInfo.CommentsSpecialFields = [];
// Check if the field exists
if (!info.CommonDiscInfo.CommentsSpecialFields.TryGetValue(key, out value))
return false;
if (string.IsNullOrEmpty(value))
return false;
// The value is valid
return true;
}
/// <summary>
/// Set a comment field if it doesn't already have a value
/// </summary>
private static void SetCommentFieldIfNotExists(SubmissionInfo info, SiteCode key, Drive? drive, Func<Drive?, string?> valueFunc)
{
// If the field has a valid value, skip
if (CommentFieldExists(info, key, out _))
return;
// Set the value
info.CommonDiscInfo!.CommentsSpecialFields![key] = valueFunc(drive) ?? string.Empty;
}
/// <summary>
/// Determine if a content field exists based on key
/// </summary>
private static bool ContentFieldExists(SubmissionInfo info, SiteCode key, out string? value)
{
// Ensure the contents fields exist
if (info.CommonDiscInfo!.ContentsSpecialFields == null)
info.CommonDiscInfo.ContentsSpecialFields = [];
// Check if the field exists
if (!info.CommonDiscInfo.ContentsSpecialFields.TryGetValue(key, out value))
return false;
if (string.IsNullOrEmpty(value))
return false;
// The value is valid
return true;
}
/// <summary>
/// Set a content field if it doesn't already have a value
/// </summary>
private static void SetContentFieldIfNotExists(SubmissionInfo info, SiteCode key, Drive? drive, Func<Drive?, string?> valueFunc)
{
// If the field has a valid value, skip
if (ContentFieldExists(info, key, out _))
return;
// Set the value
info.CommonDiscInfo!.ContentsSpecialFields![key] = valueFunc(drive) ?? string.Empty;
}
/// <summary>
/// Set the version if it doesn't already have a value
/// </summary>
private static void SetVersionIfNotExists(SubmissionInfo info, Drive? drive, Func<Drive?, string?> valueFunc)
{
// If the version already exists, skip
if (!string.IsNullOrEmpty(info.VersionAndEditions!.Version))
return;
// Set the version
info.VersionAndEditions.Version = valueFunc(drive) ?? string.Empty;
}
/// <summary>
/// Reformat a protection dictionary for submission info
/// </summary>
/// <param name="oldDict">ProtectionDictionary to format</param>
/// <returns>Reformatted dictionary on success, empty on error</returns>
private static Dictionary<string, List<string>?> ReformatProtectionDictionary(ProtectionDictionary? oldDict)
{
// Null or empty protections return empty
if (oldDict == null || oldDict.Count == 0)
return [];
// Reformat each set into a List
var newDict = new Dictionary<string, List<string>?>();
foreach (string key in oldDict.Keys)
{
RedumpSystem.AppleMacintosh => true,
RedumpSystem.EnhancedCD => true,
RedumpSystem.IBMPCcompatible => true,
RedumpSystem.PalmOS => true,
RedumpSystem.PocketPC => true,
RedumpSystem.RainbowDisc => true,
RedumpSystem.SonyElectronicBook => true,
_ => false,
};
newDict[key] = [.. oldDict[key]];
}
return newDict;
}
#endregion

View File

@@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BinaryObjectScanner;
using MPF.Frontend.ComboBoxItems;
@@ -260,7 +259,7 @@ namespace MPF.Frontend.ViewModels
CancelButtonEnabled = true;
MediaTypes = [];
Systems = RedumpSystemComboBoxItem.GenerateElements().ToList();
Systems = RedumpSystemComboBoxItem.GenerateElements();
InternalPrograms = [];
PopulateMediaType();
@@ -296,7 +295,7 @@ namespace MPF.Frontend.ViewModels
public void ChangeSystem()
{
PopulateMediaType();
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
@@ -304,7 +303,7 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public void ChangeMediaType()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
@@ -312,7 +311,7 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public void ChangeDumpingProgram()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
@@ -320,7 +319,7 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public void ChangeInputPath()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
#endregion
@@ -368,20 +367,20 @@ namespace MPF.Frontend.ViewModels
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
if (this.CurrentSystem != null)
if (CurrentSystem != null)
{
var mediaTypeValues = this.CurrentSystem.MediaTypes();
int index = mediaTypeValues.FindIndex(m => m == this.CurrentMediaType);
var mediaTypeValues = CurrentSystem.MediaTypes();
int index = mediaTypeValues.FindIndex(m => m == 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]);
MediaTypes = mediaTypeValues.ConvertAll(m => new Element<MediaType>(m ?? MediaType.NONE));
MediaTypeComboBoxEnabled = MediaTypes.Count > 1;
CurrentMediaType = (index > -1 ? MediaTypes[index] : MediaTypes[0]);
}
else
{
this.MediaTypeComboBoxEnabled = false;
this.MediaTypes = null;
this.CurrentMediaType = null;
MediaTypeComboBoxEnabled = false;
MediaTypes = null;
CurrentMediaType = null;
}
// Reenable event handlers, if necessary
@@ -398,15 +397,15 @@ namespace MPF.Frontend.ViewModels
DisableEventHandlers();
// Get the current internal program
InternalProgram internalProgram = this.Options.InternalProgram;
InternalProgram internalProgram = 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();
InternalPrograms = internalPrograms.ConvertAll(ip => new Element<InternalProgram>(ip));
// Select the current default dumping program
int currentIndex = InternalPrograms.FindIndex(m => m == internalProgram);
this.CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
@@ -418,7 +417,7 @@ namespace MPF.Frontend.ViewModels
private bool ShouldEnableCheckDumpButton()
{
return this.CurrentSystem != null && this.CurrentMediaType != null && !string.IsNullOrEmpty(this.InputPath);
return CurrentSystem != null && CurrentMediaType != null && !string.IsNullOrEmpty(InputPath);
}
/// <summary>
@@ -445,12 +444,12 @@ namespace MPF.Frontend.ViewModels
/// 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)
public async Task<string?> CheckDump(ProcessUserInfoDelegate processUserInfo)
{
if (string.IsNullOrEmpty(InputPath))
return "Invalid Input path";
if (!File.Exists(this.InputPath!.Trim('"')))
if (!File.Exists(InputPath!.Trim('"')))
return "Input Path is not a valid file";
// Disable UI while Check is running
@@ -459,7 +458,7 @@ namespace MPF.Frontend.ViewModels
DisableEventHandlers();
// Populate an environment
var env = new DumpEnvironment(Options, Path.GetFullPath(this.InputPath.Trim('"')), null, this.CurrentSystem, this.CurrentMediaType, this.CurrentProgram, parameters: null);
var env = new DumpEnvironment(Options, Path.GetFullPath(InputPath.Trim('"')), null, CurrentSystem, CurrentMediaType, CurrentProgram, parameters: null);
// Make new Progress objects
var resultProgress = new Progress<ResultEventArgs>();

View File

@@ -1,5 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System;
using System.Collections.Generic;
using MPF.Frontend.ComboBoxItems;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib.Data;
@@ -27,12 +27,14 @@ namespace MPF.Frontend.ViewModels
/// <summary>
/// List of available disc categories
/// </summary>
public List<Element<DiscCategory>> Categories { get; private set; } = Element<DiscCategory>.GenerateElements().ToList();
public List<Element<DiscCategory>> Categories { get; private set; }
= Element<DiscCategory>.GenerateElements();
/// <summary>
/// List of available regions
/// </summary>
public List<Element<Region>> Regions { get; private set; } = Element<Region>.GenerateElements().ToList();
public List<Element<Region>> Regions { get; private set; }
= Element<Region>.GenerateElements();
/// <summary>
/// List of Redump-supported Regions
@@ -123,7 +125,8 @@ namespace MPF.Frontend.ViewModels
/// <summary>
/// List of available languages
/// </summary>
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
public List<Element<Language>> Languages { get; private set; }
= Element<Language>.GenerateElements();
/// <summary>
/// List of Redump-supported Languages
@@ -183,7 +186,8 @@ namespace MPF.Frontend.ViewModels
/// <summary>
/// List of available languages
/// </summary>
public List<Element<LanguageSelection>> LanguageSelections { get; private set; } = Element<LanguageSelection>.GenerateElements().ToList();
public List<Element<LanguageSelection>> LanguageSelections { get; private set; }
= Element<LanguageSelection>.GenerateElements();
#endregion
@@ -205,9 +209,9 @@ namespace MPF.Frontend.ViewModels
public void Load()
{
if (SubmissionInfo.CommonDiscInfo?.Languages != null)
Languages.ForEach(l => l.IsChecked = SubmissionInfo.CommonDiscInfo.Languages.Contains(l));
Languages.ForEach(l => l.IsChecked = Array.IndexOf(SubmissionInfo.CommonDiscInfo.Languages, l) > -1);
if (SubmissionInfo.CommonDiscInfo?.LanguageSelection != null)
LanguageSelections.ForEach(ls => ls.IsChecked = SubmissionInfo.CommonDiscInfo.LanguageSelection.Contains(ls));
LanguageSelections.ForEach(ls => ls.IsChecked = Array.IndexOf(SubmissionInfo.CommonDiscInfo.LanguageSelection, ls) > -1);
}
/// <summary>
@@ -218,10 +222,10 @@ namespace MPF.Frontend.ViewModels
{
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 = [.. Languages.FindAll(l => l.IsChecked).ConvertAll(l => l?.Value)];
if (SubmissionInfo.CommonDiscInfo.Languages.Length == 0)
SubmissionInfo.CommonDiscInfo.Languages = [null];
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
SubmissionInfo.CommonDiscInfo.LanguageSelection = [.. LanguageSelections.FindAll(ls => ls.IsChecked).ConvertAll(ls => ls?.Value)];
SubmissionInfo.CommonDiscInfo.Title = FrontendTool.NormalizeDiscTitle(SubmissionInfo.CommonDiscInfo.Title, SubmissionInfo.CommonDiscInfo.Languages);
}
@@ -230,7 +234,7 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public void SetRedumpLanguages()
{
this.Languages = RedumpLanguages.Select(l => new Element<Language>(l)).ToList();
Languages = RedumpLanguages.ConvertAll(l => new Element<Language>(l));
}
/// <summary>
@@ -238,7 +242,7 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public void SetRedumpRegions()
{
this.Regions = RedumpRegions.Select(r => new Element<Region>(r)).ToList();
Regions = RedumpRegions.ConvertAll(r => new Element<Region>(r));
}
#endregion

View File

@@ -2,12 +2,12 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using BinaryObjectScanner;
using MPF.Frontend.ComboBoxItems;
using MPF.Frontend.Tools;
using SabreTools.IO;
using SabreTools.IO.Extensions;
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend.ViewModels
@@ -68,7 +68,7 @@ namespace MPF.Frontend.ViewModels
/// <summary>
/// Function to process user information
/// </summary>
private Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? _processUserInfo;
private ProcessUserInfoDelegate? _processUserInfo;
#endregion
@@ -556,7 +556,7 @@ namespace MPF.Frontend.ViewModels
LogPanelExpanded = _options.OpenLogWindowAtStartup;
MediaTypes = [];
Systems = RedumpSystemComboBoxItem.GenerateElements().ToList();
Systems = RedumpSystemComboBoxItem.GenerateElements();
InternalPrograms = [];
}
@@ -566,7 +566,7 @@ namespace MPF.Frontend.ViewModels
public void Init(
Action<LogLevel, string> loggerAction,
Func<string, string, int, bool, bool?> displayUserMessage,
Func<SubmissionInfo?, (bool?, SubmissionInfo?)> processUserInfo)
ProcessUserInfoDelegate processUserInfo)
{
// Set the callbacks
_logger = loggerAction;
@@ -612,18 +612,18 @@ namespace MPF.Frontend.ViewModels
VerboseLogLn("Scanning for drives..");
// Always enable the media scan
this.MediaScanButtonEnabled = true;
this.UpdateVolumeLabelEnabled = true;
MediaScanButtonEnabled = true;
UpdateVolumeLabelEnabled = true;
// If we have a selected drive, keep track of it
char? lastSelectedDrive = this.CurrentDrive?.Name?[0] ?? null;
char? lastSelectedDrive = CurrentDrive?.Name?[0] ?? null;
// Populate the list of drives and add it to the combo box
Drives = Drive.CreateListOfDrives(this.Options.IgnoreFixedDrives);
Drives = Drive.CreateListOfDrives(Options.IgnoreFixedDrives);
if (Drives.Count > 0)
{
VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Name).ToArray())}");
VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", [.. Drives.ConvertAll(d => d.Name)])}");
// Check for the last selected drive, if possible
int index = -1;
@@ -644,23 +644,23 @@ namespace MPF.Frontend.ViewModels
// Set the selected index
CurrentDrive = (index != -1 ? Drives[index] : Drives[0]);
this.Status = "Valid drive found! Choose your Media Type";
this.CopyProtectScanButtonEnabled = true;
Status = "Valid drive found! Choose your Media Type";
CopyProtectScanButtonEnabled = true;
// Get the current system type
if (index != -1)
DetermineSystemType();
// Only enable the start/stop if we don't have the default selected
this.StartStopButtonEnabled = ShouldEnableDumpingButton();
StartStopButtonEnabled = ShouldEnableDumpingButton();
}
else
{
VerboseLogLn("Found no drives");
this.CurrentDrive = null;
this.Status = "No valid drive found!";
this.StartStopButtonEnabled = false;
this.CopyProtectScanButtonEnabled = false;
CurrentDrive = null;
Status = "No valid drive found!";
StartStopButtonEnabled = false;
CopyProtectScanButtonEnabled = false;
}
// Reenable event handlers, if necessary
@@ -676,22 +676,22 @@ namespace MPF.Frontend.ViewModels
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
if (this.CurrentSystem != null)
if (CurrentSystem != null)
{
var mediaTypeValues = this.CurrentSystem.MediaTypes();
int index = mediaTypeValues.FindIndex(m => m == this.CurrentMediaType);
if (this.CurrentMediaType != null && index == -1)
var mediaTypeValues = CurrentSystem.MediaTypes();
int index = mediaTypeValues.FindIndex(m => m == CurrentMediaType);
if (CurrentMediaType != null && index == -1)
VerboseLogLn($"Disc of type '{CurrentMediaType.LongName()}' found, but the current system does not support it!");
MediaTypes = mediaTypeValues.Select(m => new Element<MediaType>(m ?? MediaType.NONE)).ToList();
this.MediaTypeComboBoxEnabled = MediaTypes.Count > 1;
this.CurrentMediaType = (index > -1 ? MediaTypes[index] : MediaTypes[0]);
MediaTypes = mediaTypeValues.ConvertAll(m => new Element<MediaType>(m ?? MediaType.NONE));
MediaTypeComboBoxEnabled = MediaTypes.Count > 1;
CurrentMediaType = (index > -1 ? MediaTypes[index] : MediaTypes[0]);
}
else
{
this.MediaTypeComboBoxEnabled = false;
this.MediaTypes = null;
this.CurrentMediaType = null;
MediaTypeComboBoxEnabled = false;
MediaTypes = null;
CurrentMediaType = null;
}
// Reenable event handlers, if necessary
@@ -708,21 +708,23 @@ namespace MPF.Frontend.ViewModels
DisableEventHandlers();
// Create a static list of supported programs, not everything
InternalPrograms = Enum.GetValues(typeof(InternalProgram)).Cast<InternalProgram>().Where(ip => InternalProgramExists(ip)).Select(ip => new Element<InternalProgram>(ip)).ToList();
var ipArr = (InternalProgram[])Enum.GetValues(typeof(InternalProgram));
ipArr = Array.FindAll(ipArr, ip => InternalProgramExists(ip));
InternalPrograms = [.. Array.ConvertAll(ipArr, ip => new Element<InternalProgram>(ip))];
// Get the current internal program
InternalProgram internalProgram = this.Options.InternalProgram;
InternalProgram internalProgram = Options.InternalProgram;
// Select the current default dumping program
if (InternalPrograms.Count == 0)
{
// If no programs are found, default to InternalProgram.NONE
this.CurrentProgram = InternalProgram.NONE;
CurrentProgram = InternalProgram.NONE;
}
else
{
int currentIndex = InternalPrograms.FindIndex(m => m == internalProgram);
this.CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
}
// Reenable event handlers, if necessary
@@ -738,9 +740,12 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public void ChangeDumpingProgram()
{
VerboseLogLn($"Changed dumping program to: {((InternalProgram?)this.CurrentProgram).LongName()}");
VerboseLogLn($"Changed dumping program to: {((InternalProgram?)CurrentProgram).LongName()}");
EnsureDiscInformation();
// New output name depends on new environment
GetOutputNames(false);
// New environment depends on new output name
EnsureDiscInformation();
}
/// <summary>
@@ -761,7 +766,7 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public void ChangeSystem()
{
VerboseLogLn($"Changed system to: {this.CurrentSystem.LongName()}");
VerboseLogLn($"Changed system to: {CurrentSystem.LongName()}");
PopulateMediaType();
GetOutputNames(false);
EnsureDiscInformation();
@@ -770,15 +775,13 @@ namespace MPF.Frontend.ViewModels
/// <summary>
/// Check for available updates
/// </summary>
public (bool, string, string?) CheckForUpdates()
public void CheckForUpdates(out bool different, out string message, out string? url)
{
(bool different, string message, var url) = FrontendTool.CheckForNewVersion();
FrontendTool.CheckForNewVersion(out different, out message, out url);
SecretLogLn(message);
if (url == null)
message = "An exception occurred while checking for versions, please try again later. See the log window for more details.";
return (different, message, url);
}
/// <summary>
@@ -946,15 +949,15 @@ namespace MPF.Frontend.ViewModels
public void ToggleStartStop()
{
// Dump or stop the dump
if (this.StartStopButtonText as string == StartDumpingValue)
if (StartStopButtonText as string == StartDumpingValue)
{
StartDumping();
}
else if (this.StartStopButtonText as string == StopDumpingValue)
else if (StartStopButtonText as string == StopDumpingValue)
{
VerboseLogLn("Canceling dumping process...");
_environment?.CancelDumping();
this.CopyProtectScanButtonEnabled = true;
CopyProtectScanButtonEnabled = true;
}
}
@@ -970,7 +973,7 @@ namespace MPF.Frontend.ViewModels
// Ensure the first run flag is unset
var continuingOptions = new Frontend.Options(optionsToSave) { FirstRun = false };
this.Options = continuingOptions;
Options = continuingOptions;
// If settings were changed, reinitialize the UI
if (savedSettings)
@@ -1043,14 +1046,14 @@ namespace MPF.Frontend.ViewModels
public void FastUpdateLabel(bool removeEventHandlers)
{
// Disable the dumping button
this.StartStopButtonEnabled = false;
StartStopButtonEnabled = false;
// Safely uncheck the parameters box, just in case
if (this.ParametersCheckBoxEnabled == true)
if (ParametersCheckBoxEnabled == true)
{
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
this.DisableEventHandlers();
this.ParametersCheckBoxEnabled = false;
DisableEventHandlers();
ParametersCheckBoxEnabled = false;
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
@@ -1059,7 +1062,7 @@ namespace MPF.Frontend.ViewModels
DisableEventHandlers();
// Refresh the drive info
this.CurrentDrive?.RefreshDrive();
CurrentDrive?.RefreshDrive();
// Set the initial environment and UI values
_environment = DetermineEnvironment();
@@ -1070,7 +1073,7 @@ namespace MPF.Frontend.ViewModels
EnableEventHandlers();
// Enable the dumping button, if necessary
this.StartStopButtonEnabled = ShouldEnableDumpingButton();
StartStopButtonEnabled = ShouldEnableDumpingButton();
}
/// <summary>
@@ -1168,34 +1171,31 @@ namespace MPF.Frontend.ViewModels
private void CacheCurrentDiscType()
{
// If the selected item is invalid, we just skip
if (this.CurrentDrive == null)
if (CurrentDrive == null)
return;
// Get reasonable default values based on the current system
MediaType? defaultMediaType = this.CurrentSystem.MediaTypes().FirstOrDefault() ?? MediaType.CDROM;
var mediaTypes = CurrentSystem.MediaTypes();
MediaType? defaultMediaType = mediaTypes.Count > 0 ? mediaTypes[0] : MediaType.CDROM;
if (defaultMediaType == MediaType.NONE)
defaultMediaType = MediaType.CDROM;
// If we're skipping detection, set the default value
if (this.Options.SkipMediaTypeDetection)
if (Options.SkipMediaTypeDetection)
{
VerboseLogLn($"Media type detection disabled, defaulting to {defaultMediaType.LongName()}.");
CurrentMediaType = defaultMediaType;
}
// If the drive is marked active, try to read from it
else if (this.CurrentDrive.MarkedActive)
else if (CurrentDrive.MarkedActive)
{
VerboseLog($"Trying to detect media type for drive {this.CurrentDrive.Name} [{this.CurrentDrive.DriveFormat}] using size and filesystem.. ");
(MediaType? detectedMediaType, var errorMessage) = this.CurrentDrive.GetMediaType(this.CurrentSystem);
// If we got an error message, post it to the log
if (errorMessage != null)
VerboseLogLn($"Message from detecting media type: {errorMessage}");
VerboseLog($"Trying to detect media type for drive {CurrentDrive.Name} [{CurrentDrive.DriveFormat}] using size and filesystem.. ");
MediaType? detectedMediaType = CurrentDrive.GetMediaType(CurrentSystem);
// If we got either an error or no media, default to the current System default
if (detectedMediaType == null)
{
VerboseLogLn($"Unable to detect, defaulting to {defaultMediaType.LongName()}.");
VerboseLogLn($"Could not detect media type, defaulting to {defaultMediaType.LongName()}.");
CurrentMediaType = defaultMediaType;
}
else
@@ -1217,17 +1217,17 @@ namespace MPF.Frontend.ViewModels
/// <summary>
/// Create a DumpEnvironment with all current settings
/// </summary>
/// <returns>Filled DumpEnvironment this.Parent</returns>
/// <returns>Filled DumpEnvironment Parent</returns>
private DumpEnvironment DetermineEnvironment()
{
return new DumpEnvironment(
this.Options,
EvaluateOutputPath(this.OutputPath),
this.CurrentDrive,
this.CurrentSystem,
this.CurrentMediaType,
this.CurrentProgram,
this.Parameters);
Options,
EvaluateOutputPath(OutputPath),
CurrentDrive,
CurrentSystem,
CurrentMediaType,
CurrentProgram,
Parameters);
}
/// <summary>
@@ -1235,32 +1235,32 @@ namespace MPF.Frontend.ViewModels
/// </summary>
private void DetermineSystemType()
{
if (Drives == null || Drives.Count == 0 || this.CurrentDrive == null)
if (Drives == null || Drives.Count == 0 || CurrentDrive == null)
{
VerboseLogLn("Skipping system type detection because no valid drives found!");
}
else if (this.CurrentDrive?.MarkedActive != true)
else if (CurrentDrive?.MarkedActive != true)
{
VerboseLogLn("Skipping system type detection because drive not marked as active!");
}
else if (!this.Options.SkipSystemDetection)
else if (!Options.SkipSystemDetection)
{
VerboseLog($"Trying to detect system for drive {this.CurrentDrive.Name}.. ");
VerboseLog($"Trying to detect system for drive {CurrentDrive.Name}.. ");
var currentSystem = GetRedumpSystem(CurrentDrive, Options.DefaultSystem) ?? Options.DefaultSystem;
VerboseLogLn(currentSystem == null ? "unable to detect." : ($"detected {currentSystem.LongName()}."));
if (currentSystem != null)
{
int sysIndex = Systems.FindIndex(s => s == currentSystem);
this.CurrentSystem = Systems[sysIndex];
CurrentSystem = Systems[sysIndex];
}
}
else if (this.Options.SkipSystemDetection && this.Options.DefaultSystem != null)
else if (Options.SkipSystemDetection && Options.DefaultSystem != null)
{
var currentSystem = this.Options.DefaultSystem;
var currentSystem = Options.DefaultSystem;
VerboseLogLn($"System detection disabled, setting to default of {currentSystem.LongName()}.");
int sysIndex = Systems.FindIndex(s => s == currentSystem);
this.CurrentSystem = Systems[sysIndex];
CurrentSystem = Systems[sysIndex];
}
}
@@ -1318,23 +1318,23 @@ namespace MPF.Frontend.ViewModels
// Get the status to write out
ResultEventArgs result = _environment.GetSupportStatus();
if (this.CurrentProgram == InternalProgram.NONE)
this.Status = "No dumping program found";
if (CurrentProgram == InternalProgram.NONE)
Status = "No dumping program found";
else
this.Status = result.Message;
Status = result.Message;
// Enable or disable the button
this.StartStopButtonEnabled = result && ShouldEnableDumpingButton();
StartStopButtonEnabled = result && ShouldEnableDumpingButton();
// If we're in a type that doesn't support drive speeds
this.DriveSpeedComboBoxEnabled = _environment.DoesSupportDriveSpeed();
DriveSpeedComboBoxEnabled = _environment.DoesSupportDriveSpeed();
// If input params are not enabled, generate the full parameters from the environment
if (!this.ParametersCheckBoxEnabled)
if (!ParametersCheckBoxEnabled)
{
var generated = _environment.GetFullParameters(this.DriveSpeed);
var generated = _environment.GetFullParameters(DriveSpeed);
if (generated != null)
this.Parameters = generated;
Parameters = generated;
}
}
@@ -1345,16 +1345,16 @@ namespace MPF.Frontend.ViewModels
/// <returns>String with %-delimited variables evaluated</returns>
public string EvaluateOutputPath(string outputPath)
{
string systemLong = this._currentSystem.LongName() ?? "Unknown System";
string systemLong = _currentSystem.LongName() ?? "Unknown System";
if (string.IsNullOrEmpty(systemLong))
systemLong = "Unknown System";
string systemShort = this._currentSystem.ShortName() ?? "unknown";
string systemShort = _currentSystem.ShortName() ?? "unknown";
if (string.IsNullOrEmpty(systemShort))
systemShort = "unknown";
string mediaLong = this._currentMediaType.LongName() ?? "Unknown Media";
string mediaLong = _currentMediaType.LongName() ?? "Unknown Media";
if (string.IsNullOrEmpty(mediaLong))
mediaLong = "Unknown Media";
string program = this._currentProgram.ToString() ?? "Unknown Program";
string program = _currentProgram.ToString() ?? "Unknown Program";
if (string.IsNullOrEmpty(program))
program = "Unknown Program";
string programShort = program == "DiscImageCreator" ? "DIC" : program;
@@ -1372,7 +1372,8 @@ namespace MPF.Frontend.ViewModels
return outputPath
.Replace("%SYSTEM%", systemLong)
.Replace("%SYS%", systemShort)
.Replace("%SYS%", systemShort.ToUpperInvariant())
.Replace("%sys%", systemShort)
.Replace("%MEDIA%", mediaLong)
.Replace("%PROGRAM%", program)
.Replace("%PROG%", programShort)
@@ -1387,20 +1388,20 @@ namespace MPF.Frontend.ViewModels
/// <param name="driveChanged">Force an updated name if the drive letter changes</param>
public void GetOutputNames(bool driveChanged)
{
if (Drives == null || Drives.Count == 0 || this.CurrentDrive == null)
if (Drives == null || Drives.Count == 0 || CurrentDrive == null)
{
VerboseLogLn("Skipping output name building because no valid drives found!");
return;
}
// Get the extension for the file for the next two statements
var extension = _environment?.GetDefaultExtension(this.CurrentMediaType);
var extension = _environment?.GetDefaultExtension(CurrentMediaType);
// Set the output filename, if it's not already
if (string.IsNullOrEmpty(this.OutputPath))
if (string.IsNullOrEmpty(OutputPath))
{
var label = GetFormattedVolumeLabel(CurrentDrive) ?? this.CurrentSystem.LongName();
var directory = this.Options.DefaultOutputPath;
var label = GetFormattedVolumeLabel(CurrentDrive) ?? CurrentSystem.LongName();
var directory = Options.DefaultOutputPath;
string filename = $"{label}{extension ?? ".bin"}";
// If the path ends with the label already
@@ -1409,19 +1410,19 @@ namespace MPF.Frontend.ViewModels
if (directory != null && label != null)
#if NET20 || NET35
this.OutputPath = Path.Combine(Path.Combine(directory, label), filename);
OutputPath = Path.Combine(Path.Combine(directory, label), filename);
#else
this.OutputPath = Path.Combine(directory, label, filename);
OutputPath = Path.Combine(directory, label, filename);
#endif
else
this.OutputPath = filename;
OutputPath = filename;
}
// Set the output filename, if we changed drives
else if (driveChanged)
{
var label = GetFormattedVolumeLabel(CurrentDrive) ?? this.CurrentSystem.LongName();
string oldPath = FrontendTool.NormalizeOutputPaths(this.OutputPath, false);
var label = GetFormattedVolumeLabel(CurrentDrive) ?? CurrentSystem.LongName();
string oldPath = FrontendTool.NormalizeOutputPaths(OutputPath, false);
string oldFilename = Path.GetFileNameWithoutExtension(oldPath);
var directory = Path.GetDirectoryName(oldPath);
string filename = $"{label}{extension ?? ".bin"}";
@@ -1436,26 +1437,26 @@ namespace MPF.Frontend.ViewModels
if (directory != null && label != null)
#if NET20 || NET35
this.OutputPath = Path.Combine(Path.Combine(directory, label), filename);
OutputPath = Path.Combine(Path.Combine(directory, label), filename);
#else
this.OutputPath = Path.Combine(directory, label, filename);
OutputPath = Path.Combine(directory, label, filename);
#endif
else
this.OutputPath = filename;
OutputPath = filename;
}
// Otherwise, reset the extension of the currently set path
else
{
string oldPath = FrontendTool.NormalizeOutputPaths(this.OutputPath, false);
string oldPath = FrontendTool.NormalizeOutputPaths(OutputPath, false);
string filename = Path.GetFileNameWithoutExtension(oldPath);
var directory = Path.GetDirectoryName(oldPath);
filename = $"{filename}{extension ?? ".bin"}";
if (directory != null)
this.OutputPath = Path.Combine(directory, filename);
OutputPath = Path.Combine(directory, filename);
else
this.OutputPath = filename;
OutputPath = filename;
}
}
@@ -1506,14 +1507,10 @@ namespace MPF.Frontend.ViewModels
// Bandai Playdia Quick Interactive System
try
{
#if NET20 || NET35
List<string> files = [.. Directory.GetFiles(drive.Name, "*", SearchOption.TopDirectoryOnly)];
#else
List<string> files = Directory.EnumerateFiles(drive.Name, "*", SearchOption.TopDirectoryOnly).ToList();
#endif
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
if (files.Exists(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
&& files.Exists(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
{
return RedumpSystem.BandaiPlaydiaQuickInteractiveSystem;
}
@@ -1559,11 +1556,7 @@ namespace MPF.Frontend.ViewModels
try
{
if (Directory.Exists(Path.Combine(drive.Name, "$SystemUpdate"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(drive.Name, "$SystemUpdate")).Any()
#else
&& Directory.EnumerateFiles(Path.Combine(drive.Name, "$SystemUpdate")).Any()
#endif
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "$SystemUpdate")).Length > 0
&& drive.TotalSize <= 500_000_000)
{
return RedumpSystem.MicrosoftXbox360;
@@ -1715,21 +1708,13 @@ namespace MPF.Frontend.ViewModels
try
{
if (Directory.Exists(Path.Combine(drive.Name, "AUDIO_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(drive.Name, "AUDIO_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(drive.Name, "AUDIO_TS")).Any())
#endif
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "AUDIO_TS")).Length > 0)
{
return RedumpSystem.DVDAudio;
}
else if (Directory.Exists(Path.Combine(drive.Name, "VIDEO_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(drive.Name, "VIDEO_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(drive.Name, "VIDEO_TS")).Any())
#endif
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "VIDEO_TS")).Length > 0)
{
return RedumpSystem.DVDVideo;
}
@@ -1740,11 +1725,7 @@ namespace MPF.Frontend.ViewModels
try
{
if (Directory.Exists(Path.Combine(drive.Name, "HVDVD_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(drive.Name, "HVDVD_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(drive.Name, "HVDVD_TS")).Any())
#endif
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "HVDVD_TS")).Length > 0)
{
return RedumpSystem.HDDVDVideo;
}
@@ -1755,11 +1736,7 @@ namespace MPF.Frontend.ViewModels
try
{
if (Directory.Exists(Path.Combine(drive.Name, "PHOTO_CD"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(drive.Name, "PHOTO_CD")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(drive.Name, "PHOTO_CD")).Any())
#endif
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "PHOTO_CD")).Length > 0)
{
return RedumpSystem.PhotoCD;
}
@@ -1770,11 +1747,7 @@ namespace MPF.Frontend.ViewModels
try
{
if (Directory.Exists(Path.Combine(drive.Name, "VCD"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(drive.Name, "drive.VCD")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(drive.Name, "VCD")).Any())
#endif
&& IOExtensions.SafeGetFiles(Path.Combine(drive.Name, "VCD")).Length > 0)
{
return RedumpSystem.VideoCD;
}
@@ -1793,7 +1766,7 @@ namespace MPF.Frontend.ViewModels
public void ProcessCustomParameters()
{
// Set the execution context and processor
if (_environment?.SetExecutionContext(this.Parameters) != true)
if (_environment?.SetExecutionContext(Parameters) != true)
return;
if (_environment?.SetProcessor() != true)
return;
@@ -1801,27 +1774,31 @@ namespace MPF.Frontend.ViewModels
// Catch this in case there's an input path issue
try
{
int driveIndex = Drives.Select(d => d.Name?[0] ?? '\0').ToList().IndexOf(_environment.ContextInputPath?[0] ?? default);
this.CurrentDrive = (driveIndex != -1 ? Drives[driveIndex] : Drives[0]);
int driveIndex = Drives.ConvertAll(d => d.Name?[0] ?? '\0')
.IndexOf(_environment.ContextInputPath?[0] ?? default);
CurrentDrive = (driveIndex != -1 ? Drives[driveIndex] : Drives[0]);
}
catch { }
int driveSpeed = _environment.Speed ?? -1;
if (driveSpeed > 0)
this.DriveSpeed = driveSpeed;
DriveSpeed = driveSpeed;
else
_environment.Speed = this.DriveSpeed;
_environment.Speed = DriveSpeed;
// Disable change handling
DisableEventHandlers();
this.OutputPath = FrontendTool.NormalizeOutputPaths(_environment.ContextOutputPath, false);
OutputPath = FrontendTool.NormalizeOutputPaths(_environment.ContextOutputPath, false);
if (MediaTypes != null)
{
MediaType? mediaType = _environment.GetMediaType();
int mediaTypeIndex = MediaTypes.FindIndex(m => m == mediaType);
this.CurrentMediaType = (mediaTypeIndex > -1 ? MediaTypes[mediaTypeIndex] : MediaTypes[0]);
if (mediaType != null)
{
int mediaTypeIndex = MediaTypes.FindIndex(m => m == mediaType);
CurrentMediaType = (mediaTypeIndex > -1 ? MediaTypes[mediaTypeIndex] : MediaTypes[0]);
}
}
// Reenable change handling
@@ -1831,59 +1808,49 @@ namespace MPF.Frontend.ViewModels
/// <summary>
/// Scan and show copy protection for the current disc
/// </summary>
#if NET40
public (string?, string?) ScanAndShowProtection()
#else
public async Task<(string?, string?)> ScanAndShowProtection()
#endif
public async Task<string?> ScanAndShowProtection()
{
// Determine current environment, just in case
_environment ??= DetermineEnvironment();
// If we don't have a valid drive
if (this.CurrentDrive?.Name == null)
return (null, "No valid drive found!");
if (CurrentDrive?.Name == null)
{
ErrorLogLn("No valid drive found!");
return null;
}
VerboseLogLn($"Scanning for copy protection in {this.CurrentDrive.Name}");
VerboseLogLn($"Scanning for copy protection in {CurrentDrive.Name}");
var tempContent = this.Status;
this.Status = "Scanning for copy protection... this might take a while!";
this.StartStopButtonEnabled = false;
this.MediaScanButtonEnabled = false;
this.UpdateVolumeLabelEnabled = false;
this.CopyProtectScanButtonEnabled = false;
var tempContent = Status;
Status = "Scanning for copy protection... this might take a while!";
StartStopButtonEnabled = false;
MediaScanButtonEnabled = false;
UpdateVolumeLabelEnabled = false;
CopyProtectScanButtonEnabled = false;
var progress = new Progress<ProtectionProgress>();
progress.ProgressChanged += ProgressUpdated;
#if NET40
var protectionTask = ProtectionTool.RunProtectionScanOnPath(this.CurrentDrive.Name, this.Options, progress);
protectionTask.Wait();
var (protections, error) = protectionTask.Result;
#else
var (protections, error) = await ProtectionTool.RunProtectionScanOnPath(this.CurrentDrive.Name, this.Options, progress);
#endif
var output = ProtectionTool.FormatProtections(protections);
// If SmartE is detected on the current disc, remove `/sf` from the flags for DIC only -- Disabled until further notice
//if (Env.InternalProgram == InternalProgram.DiscImageCreator && output.Contains("SmartE"))
//{
// ((ExecutionContexts.DiscImageCreator.ExecutionContext)Env.ExecutionContext)[ExecutionContexts.DiscImageCreator.FlagStrings.ScanFileProtect] = false;
// if (this.Options.VerboseLogging)
// this.Logger.VerboseLogLn($"SmartE detected, removing {ExecutionContexts.DiscImageCreator.FlagStrings.ScanFileProtect} from parameters");
//}
try
{
var protections = await ProtectionTool.RunProtectionScanOnPath(CurrentDrive.Name, Options, progress);
var output = ProtectionTool.FormatProtections(protections);
LogLn($"Detected the following protections in {CurrentDrive.Name}:\r\n\r\n{output}");
if (string.IsNullOrEmpty(error))
LogLn($"Detected the following protections in {this.CurrentDrive.Name}:\r\n\r\n{output}");
else
ErrorLogLn($"Path could not be scanned! Exception information:\r\n\r\n{error}");
Status = tempContent;
StartStopButtonEnabled = ShouldEnableDumpingButton();
MediaScanButtonEnabled = true;
UpdateVolumeLabelEnabled = true;
CopyProtectScanButtonEnabled = true;
this.Status = tempContent;
this.StartStopButtonEnabled = ShouldEnableDumpingButton();
this.MediaScanButtonEnabled = true;
this.UpdateVolumeLabelEnabled = true;
this.CopyProtectScanButtonEnabled = true;
return (output, error);
return output;
}
catch (Exception ex)
{
ErrorLogLn($"Path could not be scanned! Exception information:\r\n\r\n{ex}");
return null;
}
}
/// <summary>
@@ -1911,7 +1878,7 @@ namespace MPF.Frontend.ViewModels
{
case RedumpSystem.SonyPlayStation:
case RedumpSystem.SonyPlayStation2:
drive.GetPlayStationExecutableInfo(out string? serial, out _, out _);
string? serial = PhysicalTool.GetPlayStationSerial(drive);
volumeLabel = serial ?? "track";
break;
@@ -1953,10 +1920,10 @@ namespace MPF.Frontend.ViewModels
// Now set the selected item, if possible
int index = MediaTypes.FindIndex(kvp => kvp.Value == CurrentMediaType);
if (this.CurrentMediaType != null && index == -1)
if (CurrentMediaType != null && index == -1)
VerboseLogLn($"Disc of type '{CurrentMediaType.LongName()}' found, but the current system does not support it!");
this.CurrentMediaType = (index > -1 ? MediaTypes[index] : MediaTypes[0]);
CurrentMediaType = (index > -1 ? MediaTypes[index] : MediaTypes[0]);
}
/// <summary>
@@ -1965,33 +1932,14 @@ namespace MPF.Frontend.ViewModels
public void SetSupportedDriveSpeed()
{
// Set the drive speed list that's appropriate
this.DriveSpeeds = (List<int>)InterfaceConstants.GetSpeedsForMediaType(CurrentMediaType);
VerboseLogLn($"Supported media speeds: {string.Join(", ", this.DriveSpeeds.Select(ds => ds.ToString()).ToArray())}");
DriveSpeeds = (List<int>)InterfaceConstants.GetSpeedsForMediaType(CurrentMediaType);
VerboseLogLn($"Supported media speeds: {string.Join(", ", [.. DriveSpeeds.ConvertAll(ds => ds.ToString())])}");
// Set the selected speed
int speed = (CurrentMediaType) switch
{
// CD dump speed
MediaType.CDROM => Options.PreferredDumpSpeedCD,
MediaType.GDROM => Options.PreferredDumpSpeedCD,
// DVD dump speed
MediaType.DVD => Options.PreferredDumpSpeedDVD,
MediaType.NintendoGameCubeGameDisc => Options.PreferredDumpSpeedDVD,
MediaType.NintendoWiiOpticalDisc => Options.PreferredDumpSpeedDVD,
// HD-DVD dump speed
MediaType.HDDVD => Options.PreferredDumpSpeedHDDVD,
// BD dump speed
MediaType.BluRay => Options.PreferredDumpSpeedBD,
// Default
_ => Options.PreferredDumpSpeedCD,
};
int speed = FrontendTool.GetDefaultSpeedForMediaType(CurrentMediaType, Options);
VerboseLogLn($"Setting drive speed to: {speed}");
this.DriveSpeed = speed;
DriveSpeed = speed;
}
/// <summary>
@@ -2001,8 +1949,8 @@ namespace MPF.Frontend.ViewModels
{
return Drives != null
&& Drives.Count > 0
&& this.CurrentSystem != null
&& this.CurrentMediaType != null
&& CurrentSystem != null
&& CurrentMediaType != null
&& ProgramSupportsMedia();
}
@@ -2069,12 +2017,12 @@ namespace MPF.Frontend.ViewModels
_environment.RefreshDrive();
// If still in custom parameter mode, check that users meant to continue or not
if (this.ParametersCheckBoxEnabled == true && _displayUserMessage != null)
if (ParametersCheckBoxEnabled == true && _displayUserMessage != null)
{
bool? result = _displayUserMessage("Custom Changes", "It looks like you have custom parameters that have not been saved. Would you like to apply those changes before starting to dump?", 3, true);
if (result == true)
{
this.ParametersCheckBoxEnabled = false;
ParametersCheckBoxEnabled = false;
ProcessCustomParameters();
}
else if (result == null)
@@ -2100,7 +2048,7 @@ namespace MPF.Frontend.ViewModels
_environment.RefreshDrive();
// Output to the label and log
this.Status = "Starting dumping process... please wait!";
Status = "Starting dumping process... please wait!";
LogLn("Starting dumping process... please wait!");
LogLn("Look for the separate command window for more details");
@@ -2112,17 +2060,13 @@ namespace MPF.Frontend.ViewModels
_environment.ReportStatus += ProgressUpdated;
// Run the program with the parameters
#if NET40
ResultEventArgs result = _environment.Run(resultProgress);
#else
ResultEventArgs result = await _environment.Run(resultProgress);
#endif
// If we didn't execute a dumping command we cannot get submission output
if (!_environment.IsDumpingCommand())
{
LogLn("No dumping command was run, submission information will not be gathered.");
this.Status = "Execution complete!";
Status = "Execution complete!";
// Reset all UI elements
EnableAllUIElements();
@@ -2137,13 +2081,13 @@ namespace MPF.Frontend.ViewModels
else
{
ErrorLogLn(result.Message);
this.Status = "Execution failed!";
Status = "Execution failed!";
}
}
catch (Exception ex)
{
ErrorLogLn(ex.ToString());
this.Status = "An exception occurred!";
Status = "An exception occurred!";
}
finally
{
@@ -2223,7 +2167,7 @@ namespace MPF.Frontend.ViewModels
string outputFilename = Path.GetFileName(_environment.OutputPath);
// If a complete dump already exists
bool foundFiles = _environment.FoundAllFiles(outputDirectory, outputFilename, true);
bool foundFiles = _environment.FoundAllFiles(outputDirectory, outputFilename);
if (foundFiles && _displayUserMessage != null)
{
bool? mbresult = _displayUserMessage("Overwrite?", "A complete dump already exists! Are you sure you want to overwrite?", 2, true);
@@ -2282,9 +2226,9 @@ namespace MPF.Frontend.ViewModels
{
return program switch
{
InternalProgram.Redumper => File.Exists(this.Options.RedumperPath),
InternalProgram.Aaru => File.Exists(this.Options.AaruPath),
InternalProgram.DiscImageCreator => File.Exists(this.Options.DiscImageCreatorPath),
InternalProgram.Redumper => File.Exists(Options.RedumperPath),
InternalProgram.Aaru => File.Exists(Options.AaruPath),
InternalProgram.DiscImageCreator => File.Exists(Options.DiscImageCreatorPath),
_ => false,
};
}
@@ -2318,10 +2262,10 @@ namespace MPF.Frontend.ViewModels
var message = value?.Message;
// Update the label with only the first line of output
if (message != null && message.Contains('\n'))
this.Status = value?.Message?.Split('\n')[0] + " (See log output)";
if (message != null && message.Contains("\n"))
Status = value?.Message?.Split('\n')[0] + " (See log output)";
else
this.Status = value?.Message ?? string.Empty;
Status = value?.Message ?? string.Empty;
// Log based on success or failure
if (value != null && value)
@@ -2336,7 +2280,7 @@ namespace MPF.Frontend.ViewModels
private void ProgressUpdated(object? sender, ProtectionProgress value)
{
string message = $"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}";
this.Status = message;
Status = message;
VerboseLogLn(message);
}

View File

@@ -1,9 +1,6 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using MPF.Frontend.ComboBoxItems;
using SabreTools.RedumpLib.Web;
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
@@ -65,7 +62,7 @@ namespace MPF.Frontend.ViewModels
/// <summary>
/// Current list of supported system profiles
/// </summary>
public static List<RedumpSystemComboBoxItem> Systems => RedumpSystemComboBoxItem.GenerateElements().ToList();
public static List<RedumpSystemComboBoxItem> Systems => RedumpSystemComboBoxItem.GenerateElements();
#endregion
@@ -93,7 +90,7 @@ namespace MPF.Frontend.ViewModels
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();
return internalPrograms.ConvertAll(ip => new Element<InternalProgram>(ip));
}
/// <summary>
@@ -102,7 +99,7 @@ namespace MPF.Frontend.ViewModels
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();
return readMethods.ConvertAll(rm => new Element<RedumperReadMethod>(rm));
}
/// <summary>
@@ -111,7 +108,7 @@ namespace MPF.Frontend.ViewModels
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();
return sectorOrders.ConvertAll(so => new Element<RedumperSectorOrder>(so));
}
#endregion
@@ -119,21 +116,16 @@ namespace MPF.Frontend.ViewModels
#region UI Commands
/// <summary>
/// Test Redump login credentials
/// Get the human-readable result for a Redump login result
/// </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
public static string GetRedumpLoginResult(bool? success)
{
#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
return success switch
{
true => "Redump username and password accepted!",
false => "Redump username and password denied!",
null => "An error occurred validating your credentials!",
};
}
/// <summary>

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;
@@ -27,77 +26,6 @@ namespace MPF.Processors
#region BaseProcessor Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
switch (Type)
{
case MediaType.CDROM:
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}.cicm.xml"))
missingFiles.Add($"{basePath}.cicm.xml");
if (!File.Exists($"{basePath}.ibg"))
missingFiles.Add($"{basePath}.ibg");
if (!File.Exists($"{basePath}.log"))
missingFiles.Add($"{basePath}.log");
if (!File.Exists($"{basePath}.mhddlog.bin"))
missingFiles.Add($"{basePath}.mhddlog.bin");
if (!File.Exists($"{basePath}.resume.xml"))
missingFiles.Add($"{basePath}.resume.xml");
if (!File.Exists($"{basePath}.sub.log"))
missingFiles.Add($"{basePath}.sub.log");
}
break;
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}.cicm.xml"))
missingFiles.Add($"{basePath}.cicm.xml");
if (!File.Exists($"{basePath}.ibg"))
missingFiles.Add($"{basePath}.ibg");
if (!File.Exists($"{basePath}.log"))
missingFiles.Add($"{basePath}.log");
if (!File.Exists($"{basePath}.mhddlog.bin"))
missingFiles.Add($"{basePath}.mhddlog.bin");
if (!File.Exists($"{basePath}.resume.xml"))
missingFiles.Add($"{basePath}.resume.xml");
}
break;
default:
missingFiles.Add("Media and system combination not supported for Aaru");
break;
}
return (!missingFiles.Any(), missingFiles);
}
/// <inheritdoc/>
public override void GenerateArtifacts(SubmissionInfo info, string basePath)
{
info.Artifacts ??= [];
if (File.Exists(basePath + ".cicm.xml"))
info.Artifacts["cicm"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + ".cicm.xml")) ?? string.Empty;
if (File.Exists(basePath + ".ibg"))
info.Artifacts["ibg"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".ibg"));
if (File.Exists(basePath + ".log"))
info.Artifacts["log"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + ".log")) ?? string.Empty;
if (File.Exists(basePath + ".mhddlog.bin"))
info.Artifacts["mhddlog_bin"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".mhddlog.bin"));
if (File.Exists(basePath + ".resume.xml"))
info.Artifacts["resume"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + ".resume.xml")) ?? string.Empty;
if (File.Exists(basePath + ".sub.log"))
info.Artifacts["sub_log"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + ".sub.log")) ?? string.Empty;
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
{
@@ -257,49 +185,74 @@ namespace MPF.Processors
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
internal override List<OutputFile> GetOutputFiles(string? baseDirectory, string baseFilename)
{
var logFiles = new List<string>();
switch (Type)
{
case MediaType.CDROM:
if (File.Exists($"{basePath}.cicm.xml"))
logFiles.Add($"{basePath}.cicm.xml");
if (File.Exists($"{basePath}.error.log"))
logFiles.Add($"{basePath}.error.log");
if (File.Exists($"{basePath}.ibg"))
logFiles.Add($"{basePath}.ibg");
if (File.Exists($"{basePath}.log"))
logFiles.Add($"{basePath}.log");
if (File.Exists($"{basePath}.mhddlog.bin"))
logFiles.Add($"{basePath}.mhddlog.bin");
if (File.Exists($"{basePath}.resume.xml"))
logFiles.Add($"{basePath}.resume.xml");
if (File.Exists($"{basePath}.sub.log"))
logFiles.Add($"{basePath}.sub.log");
break;
return [
new($"{baseFilename}.aaruf", OutputFileFlags.Required),
new($"{baseFilename}.cicm.xml", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"cicm"),
new($"{baseFilename}.error.log", OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"error_log"),
new($"{baseFilename}.ibg", OutputFileFlags.Required
| OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"ibg"),
new($"{baseFilename}.log", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"log"),
new($"{baseFilename}.mhddlog.bin", OutputFileFlags.Required
| OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"mhddlog"),
new($"{baseFilename}.resume.xml", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"resume"),
new($"{baseFilename}.sub.log", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"sub_log"),
];
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
if (File.Exists($"{basePath}.cicm.xml"))
logFiles.Add($"{basePath}.cicm.xml");
if (File.Exists($"{basePath}.error.log"))
logFiles.Add($"{basePath}.error.log");
if (File.Exists($"{basePath}.ibg"))
logFiles.Add($"{basePath}.ibg");
if (File.Exists($"{basePath}.log"))
logFiles.Add($"{basePath}.log");
if (File.Exists($"{basePath}.mhddlog.bin"))
logFiles.Add($"{basePath}.mhddlog.bin");
if (File.Exists($"{basePath}.resume.xml"))
logFiles.Add($"{basePath}.resume.xml");
break;
return [
new($"{baseFilename}.aaruf", OutputFileFlags.Required),
new($"{baseFilename}.cicm.xml", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"cicm"),
new($"{baseFilename}.error.log", OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"error_log"),
new($"{baseFilename}.ibg", OutputFileFlags.Required
| OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"ibg"),
new($"{baseFilename}.log", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"log"),
new($"{baseFilename}.mhddlog.bin", OutputFileFlags.Required
| OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"mhddlog"),
new($"{baseFilename}.resume.xml", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"resume"),
];
}
return logFiles;
return [];
}
#endregion
@@ -480,6 +433,7 @@ namespace MPF.Processors
// Add the track to the file
cueTracks.Add(cueTrack);
cueFile.Tracks = [.. cueTracks];
// Add the file to the cuesheet
cueFiles.Add(cueFile);
@@ -663,31 +617,16 @@ namespace MPF.Processors
continue;
// Build each row in consecutive order
string pvd = string.Empty;
#if NET20 || NET35 || NET40
byte[] pvdLine = new byte[16];
Array.Copy(pvdData, 0, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0320", pvdLine);
Array.Copy(pvdData, 16, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0330", pvdLine);
Array.Copy(pvdData, 32, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0340", pvdLine);
Array.Copy(pvdData, 48, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0350", pvdLine);
Array.Copy(pvdData, 64, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0360", pvdLine);
Array.Copy(pvdData, 80, pvdLine, 0, 16);
pvd += GenerateSectorOutputLine("0370", pvdLine);
#else
pvd += GenerateSectorOutputLine("0320", new ReadOnlySpan<byte>(pvdData, 0, 16).ToArray());
pvd += GenerateSectorOutputLine("0330", new ReadOnlySpan<byte>(pvdData, 16, 16).ToArray());
pvd += GenerateSectorOutputLine("0340", new ReadOnlySpan<byte>(pvdData, 32, 16).ToArray());
pvd += GenerateSectorOutputLine("0350", new ReadOnlySpan<byte>(pvdData, 48, 16).ToArray());
pvd += GenerateSectorOutputLine("0360", new ReadOnlySpan<byte>(pvdData, 64, 16).ToArray());
pvd += GenerateSectorOutputLine("0370", new ReadOnlySpan<byte>(pvdData, 80, 16).ToArray());
#endif
var pvd = new StringBuilder();
return pvd;
pvd.AppendLine(GenerateSectorOutputLine("0320", pvdData, 0 * 16));
pvd.AppendLine(GenerateSectorOutputLine("0330", pvdData, 1 * 16));
pvd.AppendLine(GenerateSectorOutputLine("0340", pvdData, 2 * 16));
pvd.AppendLine(GenerateSectorOutputLine("0350", pvdData, 3 * 16));
pvd.AppendLine(GenerateSectorOutputLine("0360", pvdData, 4 * 16));
pvd.AppendLine(GenerateSectorOutputLine("0370", pvdData, 5 * 16));
return pvd.ToString();
}
}
@@ -777,13 +716,13 @@ namespace MPF.Processors
// Now generate the byte array data
var pvdData = new List<byte>();
pvdData.AddRange(new string(' ', 13).ToCharArray().Select(c => (byte)c));
pvdData.AddRange(Array.ConvertAll(new string(' ', 13).ToCharArray(), c => (byte)c));
pvdData.AddRange(GeneratePVDDateTimeBytes(creation));
pvdData.AddRange(GeneratePVDDateTimeBytes(modification));
pvdData.AddRange(GeneratePVDDateTimeBytes(expiration));
pvdData.AddRange(GeneratePVDDateTimeBytes(effective));
pvdData.Add(0x01);
pvdData.AddRange(new string((char)0, 14).ToCharArray().Select(c => (byte)c));
pvdData.AddRange(Array.ConvertAll(new string((char)0, 14).ToCharArray(), c => (byte)c));
// Return the filled array
return [.. pvdData];
@@ -825,7 +764,7 @@ namespace MPF.Processors
}
// Get and return the byte array
List<byte> dateTimeList = dateTimeString.ToCharArray().Select(c => (byte)c).ToList();
List<byte> dateTimeList = [.. Array.ConvertAll(dateTimeString.ToCharArray(), c => (byte)c)];
dateTimeList.Add(timeZoneNumber);
return [.. dateTimeList];
}
@@ -835,22 +774,26 @@ namespace MPF.Processors
/// </summary>
/// <param name="row">Row ID for outputting</param>
/// <param name="bytes">Bytes representing the data to write</param>
/// <param name="offset">Offset into the byte array</param>
/// <param name="length">Length of the byte array</param>
/// <returns>Formatted string representing the sector line</returns>
private static string? GenerateSectorOutputLine(string row, byte[] bytes)
private static string? GenerateSectorOutputLine(string row, byte[] bytes, int offset)
{
// If the data isn't correct, return null
if (bytes == null || bytes.Length != 16)
if (bytes == null || offset < 0 || offset >= bytes.Length || bytes.Length - offset < 16)
return null;
string pvdLine = $"{row} : ";
pvdLine += BitConverter.ToString(bytes.Take(8).ToArray()).Replace("-", " ");
pvdLine += " ";
pvdLine += BitConverter.ToString(bytes.Skip(8).Take(8).ToArray()).Replace("-", " ");
pvdLine += " ";
pvdLine += Encoding.ASCII.GetString([.. bytes]).Replace((char)0, '.').Replace('?', '.');
pvdLine += "\n";
var pvdLine = new StringBuilder();
return pvdLine;
pvdLine.Append($"{row} : ");
pvdLine.Append(BitConverter.ToString(bytes, offset + 0, 8).Replace("-", " "));
pvdLine.Append(" ");
pvdLine.Append(BitConverter.ToString(bytes, offset + 8, 8).Replace("-", " "));
pvdLine.Append(" ");
pvdLine.Append(Encoding.ASCII.GetString(bytes, offset, 16).Replace((char)0, '.').Replace('?', '.'));
return pvdLine.ToString();
}
/// <summary>
@@ -1031,7 +974,7 @@ namespace MPF.Processors
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
{
// If there's no hardware information, skip
if (opticalDisc.DumpHardwareArray == null || !opticalDisc.DumpHardwareArray.Any())
if (opticalDisc.DumpHardwareArray == null || opticalDisc.DumpHardwareArray.Length == 0)
continue;
foreach (DumpHardwareType hardware in opticalDisc.DumpHardwareArray)

View File

@@ -4,8 +4,10 @@ using System.IO;
#if NET452_OR_GREATER || NETCOREAPP
using System.IO.Compression;
#endif
#if NET35_OR_GREATER || NETCOREAPP
using System.Linq;
using System.Text.RegularExpressions;
#endif
using System.Text;
using SabreTools.RedumpLib.Data;
namespace MPF.Processors
@@ -44,21 +46,6 @@ namespace MPF.Processors
#region Abstract Methods
/// <summary>
/// Validate if all required output files exist
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <param name="preCheck">True if this is a check done before a dump, false if done after</param>
/// <returns>Tuple of true if all required files exist, false otherwise and a list representing missing files</returns>
public abstract (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck);
/// <summary>
/// Generate artifacts and add to the SubmissionInfo
/// </summary>
/// <param name="submissionInfo">Base submission info to fill in specifics for</param>
/// <param name="basePath">Base filename and path to use for checking</param>
public abstract void GenerateArtifacts(SubmissionInfo submissionInfo, string basePath);
/// <summary>
/// Generate a SubmissionInfo for the output files
/// </summary>
@@ -67,27 +54,17 @@ namespace MPF.Processors
/// <param name="redumpCompat">Determines if outputs are processed according to Redump specifications</param>
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, string basePath, bool redumpCompat);
/// <summary>
/// Generate a list of all log files generated
// <summary>
/// Generate a list of all output files generated
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>List of all log file paths, empty otherwise</returns>
public abstract List<string> GetLogFilePaths(string basePath);
/// <param name="baseDirectory">Base filename and path to use for checking</param>
/// <param name="baseFilename">Base filename and path to use for checking</param>
/// <returns>List of all output files, empty otherwise</returns>
internal abstract List<OutputFile> GetOutputFiles(string? baseDirectory, string baseFilename);
#endregion
#region Virtual Methods
/// <summary>
/// Generate a list of all deleteable files generated
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>List of all deleteable file paths, empty otherwise</returns>
public virtual List<string> GetDeleteableFilePaths(string basePath) => [];
#endregion
#region Shared Methods
#region Output Files
/// <summary>
/// Compress log files to save space
@@ -97,12 +74,12 @@ namespace MPF.Processors
/// <param name="outputFilename">Output filename to use as the base path</param>
/// <param name="processor">Processor object representing how to process the outputs</param>
/// <returns>True if the process succeeded, false otherwise</returns>
public (bool, string) CompressLogFiles(string? outputDirectory, string? filenameSuffix, string outputFilename)
public bool CompressLogFiles(string? outputDirectory, string? filenameSuffix, string outputFilename, out string status)
{
#if NET20 || NET35 || NET40
return (false, "Log compression is not available for this framework version");
status = "Log compression is not available for this framework version";
return false;
#else
// Prepare the necessary paths
outputFilename = Path.GetFileNameWithoutExtension(outputFilename);
string combinedBase;
@@ -111,17 +88,19 @@ namespace MPF.Processors
else
combinedBase = Path.Combine(outputDirectory, outputFilename);
string archiveName = combinedBase + "_logs.zip";
// Generate the archive filename
string archiveName = $"{combinedBase}_logs.zip";
// Get the list of log files from the parameters object
var files = GetLogFilePaths(combinedBase);
// Get the lists of zippable files
var zippableFiles = GetZippableFilePaths(combinedBase);
var generatedFiles = GetGeneratedFilePaths(outputDirectory, filenameSuffix);
// Add on generated log files if they exist
var mpfFiles = GetGeneratedFilePaths(outputDirectory, filenameSuffix);
files.AddRange(mpfFiles);
if (!files.Any())
return (true, "No files to compress!");
// Don't create an archive if there are no paths
if (zippableFiles.Count == 0 && generatedFiles.Count == 0)
{
status = "No files to compress!";
return true;
}
// If the file already exists, we want to delete the old one
try
@@ -131,7 +110,8 @@ namespace MPF.Processors
}
catch
{
return (false, "Could not delete old archive!");
status = "Could not delete old archive!";
return false;
}
// Add the log files to the archive and delete the uncompressed file after
@@ -139,39 +119,17 @@ namespace MPF.Processors
try
{
zf = ZipFile.Open(archiveName, ZipArchiveMode.Create);
foreach (string file in files)
{
if (string.IsNullOrEmpty(outputDirectory))
{
zf.CreateEntryFromFile(file, file, CompressionLevel.Optimal);
}
else
{
string entryName = file[outputDirectory!.Length..].TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
#if NETFRAMEWORK || NETCOREAPP3_1 || NET5_0
zf.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
#else
zf.CreateEntryFromFile(file, entryName, CompressionLevel.SmallestSize);
#endif
}
_ = AddToArchive(zf, zippableFiles, outputDirectory, true);
_ = AddToArchive(zf, generatedFiles, outputDirectory, false);
// If the file is MPF-specific, don't delete
if (mpfFiles.Contains(file))
continue;
try
{
File.Delete(file);
}
catch { }
}
return (true, "Compression complete!");
status = "Compression complete!";
return true;
}
catch (Exception ex)
{
return (false, $"Compression could not complete: {ex}");
status = $"Compression could not complete: {ex}";
return false;
}
finally
{
@@ -187,7 +145,7 @@ namespace MPF.Processors
/// <param name="outputFilename">Output filename to use as the base path</param>
/// <param name="processor">Processor object representing how to process the outputs</param>
/// <returns>True if the process succeeded, false otherwise</returns>
public (bool, string) DeleteUnnecessaryFiles(string? outputDirectory, string outputFilename)
public bool DeleteUnnecessaryFiles(string? outputDirectory, string outputFilename, out string status)
{
// Prepare the necessary paths
outputFilename = Path.GetFileNameWithoutExtension(outputFilename);
@@ -200,27 +158,24 @@ namespace MPF.Processors
// Get the list of deleteable files from the parameters object
var files = GetDeleteableFilePaths(combinedBase);
if (!files.Any())
return (true, "No files to delete!");
if (files.Count == 0)
{
status = "No files to delete!";
return true;
}
// Attempt to delete all of the files
try
foreach (string file in files)
{
foreach (string file in files)
try
{
try
{
File.Delete(file);
}
catch { }
File.Delete(file);
}
catch { }
}
return (true, "Deletion complete!");
}
catch (Exception ex)
{
return (false, $"Deletion could not complete: {ex}");
}
status = "Deletion complete!";
return true;
}
/// <summary>
@@ -229,11 +184,10 @@ namespace MPF.Processors
/// <param name="outputDirectory">Output folder to write to</param>
/// <param name="outputFilename">Output filename to use as the base path</param>
/// <param name="processor">Processor object representing how to process the outputs</param>
/// <param name="preCheck">True if this is a check done before a dump, false if done after</param>
/// <returns>Tuple of true if all required files exist, false otherwise and a list representing missing files</returns>
public (bool, List<string>) FoundAllFiles(string? outputDirectory, string outputFilename, bool preCheck)
/// <returns>A list representing missing files, empty if none</returns>
public List<string> FoundAllFiles(string? outputDirectory, string outputFilename)
{
// First, sanitized the output filename to strip off any potential extension
// Sanitize the output filename to strip off any potential extension
outputFilename = Path.GetFileNameWithoutExtension(outputFilename);
// Then get the base path for all checking
@@ -244,9 +198,386 @@ namespace MPF.Processors
basePath = Path.Combine(outputDirectory, outputFilename);
// Finally, let the parameters say if all files exist
return CheckAllOutputFilesExist(basePath, preCheck);
return CheckRequiredFiles(basePath);
}
/// <summary>
/// Generate artifacts and return them as a dictionary
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>Dictiionary of artifact keys to Base64-encoded values, if possible</param>
public Dictionary<string, string> GenerateArtifacts(string basePath)
{
// Split the base path for matching
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
string baseFilename = Path.GetFileNameWithoutExtension(basePath);
// Get the list of output files
var outputFiles = GetOutputFiles(baseDirectory, baseFilename);
if (outputFiles.Count == 0)
return [];
// Create the artifacts dictionary
var artifacts = new Dictionary<string, string>();
// Only try to create artifacts for files that exist
foreach (var outputFile in outputFiles)
{
// Skip non-artifact files
if (!outputFile.IsArtifact || outputFile.ArtifactKey == null)
continue;
// Skip non-existent files
foreach (string filename in outputFile.Filenames)
{
string possibleFile = Path.Combine(baseDirectory, filename);
if (!File.Exists(possibleFile))
continue;
// Get binary artifacts as a byte array
if (outputFile.IsBinaryArtifact)
{
byte[] data = File.ReadAllBytes(possibleFile);
string str = Convert.ToBase64String(data);
artifacts.Add(outputFile.ArtifactKey, str);
}
else
{
string? data = ProcessingTool.GetFullFile(possibleFile);
string str = ProcessingTool.GetBase64(data) ?? string.Empty;
artifacts.Add(outputFile.ArtifactKey, str);
}
break;
}
}
return artifacts;
}
#if NET452_OR_GREATER || NETCOREAPP
/// <summary>
/// Try to add a set of files to an existing archive
/// </summary>
/// <param name="archive">Archive to add the file to</param>
/// <param name="files">Full path to a set of existing files</param>
/// <param name="outputDirectory">Directory that the existing files live in</param>
/// <param name="delete">Indicates if the files should be deleted after adding</param>
/// <returns>True if all files were added successfully, false otherwise</returns>
private static bool AddToArchive(ZipArchive archive, List<string> files, string? outputDirectory, bool delete)
{
// An empty list means success
if (files.Count == 0)
return true;
// Loop through and add all files
bool allAdded = true;
foreach (string file in files)
{
allAdded &= AddToArchive(archive, file, outputDirectory, delete);
}
return allAdded;
}
/// <summary>
/// Try to add a file to an existing archive
/// </summary>
/// <param name="archive">Archive to add the file to</param>
/// <param name="file">Full path to an existing file</param>
/// <param name="outputDirectory">Directory that the existing file lives in</param>
/// <param name="delete">Indicates if the file should be deleted after adding</param>
/// <returns>True if the file was added successfully, false otherwise</returns>
private static bool AddToArchive(ZipArchive archive, string file, string? outputDirectory, bool delete)
{
// Check if the file exists
if (!File.Exists(file))
return false;
// Get the entry name from the file
string entryName = file;
if (!string.IsNullOrEmpty(outputDirectory))
entryName = entryName.Substring(outputDirectory!.Length);
// Ensure the entry is formatted correctly
entryName = entryName.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
// Create and add the entry
try
{
#if NETFRAMEWORK || NETCOREAPP3_1 || NET5_0
archive.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
#else
archive.CreateEntryFromFile(file, entryName, CompressionLevel.SmallestSize);
#endif
}
catch
{
return false;
}
// Try to delete the file if requested
if (delete)
{
try { File.Delete(file); } catch { }
}
return true;
}
#endif
/// <summary>
/// Validate if all required output files exist
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>A list representing missing files, empty if none</returns>
private List<string> CheckRequiredFiles(string basePath)
{
// Split the base path for matching
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
string baseFilename = Path.GetFileNameWithoutExtension(basePath);
// Get the list of output files
var outputFiles = GetOutputFiles(baseDirectory, baseFilename);
if (outputFiles.Count == 0)
return ["Media and system combination not supported"];
// Check for the log file
bool logArchiveExists = false;
#if NET452_OR_GREATER || NETCOREAPP
ZipArchive? logArchive = null;
#endif
if (File.Exists($"{basePath}_logs.zip"))
{
logArchiveExists = true;
#if NET452_OR_GREATER || NETCOREAPP
try
{
// Try to open the archive
logArchive = ZipFile.OpenRead($"{basePath}_logs.zip");
}
catch
{
logArchiveExists = false;
}
#endif
}
// Get a list of all missing required files
var missingFiles = new List<string>();
foreach (var outputFile in outputFiles)
{
// Only check required files
if (!outputFile.IsRequired)
continue;
// Use the built-in existence function
if (outputFile.Exists(baseDirectory))
continue;
// If the log archive doesn't exist
if (!logArchiveExists)
{
missingFiles.Add(outputFile.Filenames[0]);
continue;
}
#if NET20 || NET35 || NET40
// Assume the zipfile has the file in it
continue;
#else
// Check the log archive
if (outputFile.Exists(logArchive))
continue;
// Add the file to the missing list
missingFiles.Add(outputFile.Filenames[0]);
#endif
}
return missingFiles;
}
/// <summary>
/// Generate a list of all deleteable filenames
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>List of all deleteable filenames, empty otherwise</returns>
private List<string> GetDeleteableFilenames(string basePath)
{
// Split the base path for matching
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
string baseFilename = Path.GetFileNameWithoutExtension(basePath);
// Get the list of output files
var outputFiles = GetOutputFiles(baseDirectory, baseFilename);
if (outputFiles.Count == 0)
return [];
// Filter down to deleteable files
var deleteableFiles = outputFiles.FindAll(of => of.IsDeleteable);
#if NET20
var deleteableFilenames = new List<string>();
foreach (var deleteableFile in deleteableFiles)
{
deleteableFilenames.AddRange(deleteableFile.Filenames);
}
return deleteableFilenames;
#else
return deleteableFiles.SelectMany(of => of.Filenames).ToList();
#endif
}
/// <summary>
/// Generate a list of all deleteable file paths
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>List of all deleteable file paths, empty otherwise</returns>
private List<string> GetDeleteableFilePaths(string basePath)
{
// Split the base path for matching
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
// Get the list of deleteable files
var deleteableFilenames = GetDeleteableFilenames(basePath);
if (deleteableFilenames.Count == 0)
return [];
// Return only files that exist
var deleteableFiles = new List<string>();
foreach (var filename in deleteableFilenames)
{
// Skip non-existent files
string possiblePath = Path.Combine(baseDirectory, filename);
if (!File.Exists(possiblePath))
continue;
deleteableFiles.Add(possiblePath);
}
return deleteableFiles;
}
/// <summary>
/// Generate a list of all MPF-generated filenames
/// </summary>
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
/// <returns>List of all MPF-generated filenames, empty otherwise</returns>
private static List<string> GetGeneratedFilenames(string? filenameSuffix)
{
// Set the base file path names
const string submissionInfoBase = "!submissionInfo";
const string protectionInfoBase = "!protectionInfo";
// Ensure the filename suffix is formatted correctly
filenameSuffix = string.IsNullOrEmpty(filenameSuffix) ? string.Empty : $"_{filenameSuffix}";
// Define the output filenames
return [
$"{protectionInfoBase}{filenameSuffix}.txt",
$"{submissionInfoBase}{filenameSuffix}.json",
$"{submissionInfoBase}{filenameSuffix}.json.gz",
$"{submissionInfoBase}{filenameSuffix}.txt",
];
}
/// <summary>
/// Generate a list of all MPF-specific log files generated
/// </summary>
/// <param name="basePath">Base directory to use for checking</param>
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
/// <returns>List of all log file paths, empty otherwise</returns>
private static List<string> GetGeneratedFilePaths(string? baseDirectory, string? filenameSuffix)
{
// Get the list of generated files
var generatedFilenames = GetGeneratedFilenames(filenameSuffix);
if (generatedFilenames.Count == 0)
return [];
// Ensure the output directory
baseDirectory ??= string.Empty;
// Return only files that exist
var generatedFiles = new List<string>();
foreach (var filename in generatedFilenames)
{
// Skip non-existent files
string possiblePath = Path.Combine(baseDirectory, filename);
if (!File.Exists(possiblePath))
continue;
generatedFiles.Add(possiblePath);
}
return generatedFiles;
}
/// <summary>
/// Generate a list of all zippable filenames
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>List of all zippable filenames, empty otherwise</returns>
private List<string> GetZippableFilenames(string basePath)
{
// Split the base path for matching
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
string baseFilename = Path.GetFileNameWithoutExtension(basePath);
// Get the list of output files
var outputFiles = GetOutputFiles(baseDirectory, baseFilename);
if (outputFiles.Count == 0)
return [];
// Filter down to zippable files
var zippableFiles = outputFiles.FindAll(of => of.IsZippable);
#if NET20
var zippableFilenames = new List<string>();
foreach (var zippableFile in zippableFiles)
{
zippableFilenames.AddRange(zippableFile.Filenames);
}
return zippableFilenames;
#else
return zippableFiles.SelectMany(of => of.Filenames).ToList();
#endif
}
/// <summary>
/// Generate a list of all zippable file paths
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>List of all zippable file paths, empty otherwise</returns>
private List<string> GetZippableFilePaths(string basePath)
{
// Split the base path for matching
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
// Get the list of zippable files
var zippableFilenames = GetZippableFilenames(basePath);
if (zippableFilenames.Count == 0)
return [];
// Return only files that exist
var zippableFiles = new List<string>();
foreach (var filename in zippableFilenames)
{
// Skip non-existent files
string possiblePath = Path.Combine(baseDirectory, filename);
if (!File.Exists(possiblePath))
continue;
zippableFiles.Add(possiblePath);
}
return zippableFiles;
}
#endregion
#region Shared Methods
/// <summary>
/// Get the hex contents of the PIC file
/// </summary>
@@ -270,8 +601,7 @@ namespace MPF.Processors
hex = hex.Substring(0, trimLength);
// TODO: Check for non-zero values in discarded PIC
return Regex.Replace(hex, ".{32}", "$0\n", RegexOptions.Compiled);
return SplitString(hex, 32);
}
catch
{
@@ -335,61 +665,29 @@ namespace MPF.Processors
}
/// <summary>
/// Generate a list of all MPF-specific log files generated
/// Split a string with newlines every <paramref name="count"/> characters
/// </summary>
/// <param name="outputDirectory">Output folder to write to</param>
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
/// <returns>List of all log file paths, empty otherwise</returns>
private static List<string> GetGeneratedFilePaths(string? outputDirectory, string? filenameSuffix)
protected static string SplitString(string? str, int count, bool trim = false)
{
var files = new List<string>();
// Ignore invalid inputs
if (str == null)
return string.Empty;
if (count < 1)
return str;
if (string.IsNullOrEmpty(outputDirectory) && string.IsNullOrEmpty(filenameSuffix))
// Build the output string
var sb = new StringBuilder();
for (int i = 0; i < str.Length; i += count)
{
if (File.Exists("!submissionInfo.txt"))
files.Add("!submissionInfo.txt");
if (File.Exists("!submissionInfo.json"))
files.Add("!submissionInfo.json");
if (File.Exists("!submissionInfo.json.gz"))
files.Add("!submissionInfo.json.gz");
if (File.Exists("!protectionInfo.txt"))
files.Add("!protectionInfo.txt");
}
else if (string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
{
if (File.Exists($"!submissionInfo_{filenameSuffix}.txt"))
files.Add($"!submissionInfo_{filenameSuffix}.txt");
if (File.Exists($"!submissionInfo_{filenameSuffix}.json"))
files.Add($"!submissionInfo_{filenameSuffix}.json");
if (File.Exists($"!submissionInfo_{filenameSuffix}.json.gz"))
files.Add($"!submissionInfo_{filenameSuffix}.json.gz");
if (File.Exists($"!protectionInfo_{filenameSuffix}.txt"))
files.Add($"!protectionInfo_{filenameSuffix}.txt");
}
else if (!string.IsNullOrEmpty(outputDirectory) && string.IsNullOrEmpty(filenameSuffix))
{
if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.txt")))
files.Add(Path.Combine(outputDirectory, "!submissionInfo.txt"));
if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.json")))
files.Add(Path.Combine(outputDirectory, "!submissionInfo.json"));
if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.json.gz")))
files.Add(Path.Combine(outputDirectory, "!submissionInfo.json.gz"));
if (File.Exists(Path.Combine(outputDirectory, "!protectionInfo.txt")))
files.Add(Path.Combine(outputDirectory, "!protectionInfo.txt"));
}
else if (!string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
{
if (File.Exists(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.txt")))
files.Add(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.txt"));
if (File.Exists(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json")))
files.Add(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json"));
if (File.Exists(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json.gz")))
files.Add(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json.gz"));
if (File.Exists(Path.Combine(outputDirectory, $"!protectionInfo_{filenameSuffix}.txt")))
files.Add(Path.Combine(outputDirectory, $"!protectionInfo_{filenameSuffix}.txt"));
string line = str.Substring(i, count);
if (trim)
line = line.Trim();
sb.Append(line);
sb.Append('\n');
}
return files;
return sb.ToString();
}
#endregion

View File

@@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Text;
using SabreTools.Hashing;
using SabreTools.Models.Logiqx;
using SabreTools.RedumpLib;
@@ -19,44 +18,6 @@ namespace MPF.Processors
#region BaseProcessor Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
switch (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 GenerateArtifacts(SubmissionInfo info, string basePath)
{
info.Artifacts ??= [];
if (File.Exists(basePath + ".bca"))
info.Artifacts["bca"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty;
if (File.Exists(basePath + "-dumpinfo.txt"))
info.Artifacts["dumpinfo"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + "-dumpinfo.txt")) ?? string.Empty;
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
{
@@ -105,23 +66,28 @@ namespace MPF.Processors
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
internal override List<OutputFile> GetOutputFiles(string? baseDirectory, string baseFilename)
{
var logFiles = new List<string>();
switch (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");
return [
new($"{baseFilename}.bca", OutputFileFlags.Required
| OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"bca"),
new($"{baseFilename}.iso", OutputFileFlags.Required),
break;
new($"{baseFilename}-dumpinfo.txt", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"dumpinfo"),
];
}
return logFiles;
return [];
}
#endregion
@@ -207,7 +173,18 @@ namespace MPF.Processors
if (hex == null)
return null;
return Regex.Replace(hex, ".{32}", "$0\n");
// Separate into blocks of 4 hex digits and newlines
var bca = new StringBuilder();
for (int i = 0; i < hex.Length; i++)
{
bca.Append(hex[i]);
if ((i + 1) % 32 == 0)
bca.AppendLine();
else if ((i + 1) % 4 == 0)
bca.Append(' ');
}
return bca.ToString();
}
catch
{

View File

@@ -0,0 +1,92 @@
using System;
using System.IO;
#if NET452_OR_GREATER || NETCOREAPP
using System.IO.Compression;
#endif
namespace MPF.Processors
{
/// <summary>
/// Represents a single output file with custom detection rules
/// </summary>
internal class CustomOutputFile : OutputFile
{
/// <summary>
/// Optional func for determining if a file exists
/// </summary>
private readonly Func<string, bool> _existsFunc;
/// <summary>
/// Create an OutputFile with a single filename
/// </summary>
public CustomOutputFile(string filename, OutputFileFlags flags, Func<string, bool> existsFunc)
: base([filename], flags)
{
_existsFunc = existsFunc;
}
/// <summary>
/// Create an OutputFile with a single filename
/// </summary>
public CustomOutputFile(string filename, OutputFileFlags flags, string artifactKey, Func<string, bool> existsFunc)
: base([filename], flags, artifactKey)
{
_existsFunc = existsFunc;
}
/// <summary>
/// Create an OutputFile with set of filenames
/// </summary>
public CustomOutputFile(string[] filenames, OutputFileFlags flags, Func<string, bool> existsFunc)
: base(filenames, flags)
{
_existsFunc = existsFunc;
}
/// <summary>
/// Create an OutputFile with set of filenames
/// </summary>
public CustomOutputFile(string[] filenames, OutputFileFlags flags, string artifactKey, Func<string, bool> existsFunc)
: base(filenames, flags, artifactKey)
{
_existsFunc = existsFunc;
}
/// <inheritdoc/>
public override bool Exists(string baseDirectory)
{
// Ensure the directory exists
if (!Directory.Exists(baseDirectory))
return false;
foreach (string filename in Filenames)
{
// Check for invalid filenames
if (string.IsNullOrEmpty(filename))
continue;
try
{
string possibleFile = Path.Combine(baseDirectory, filename);
if (_existsFunc(possibleFile))
return true;
}
catch { }
}
return false;
}
#if NET452_OR_GREATER || NETCOREAPP
/// <summary>
/// Indicates if an output file exists in an archive
/// </summary>
/// <param name="archive">Zip archive to check in</param>
public override bool Exists(ZipArchive? archive)
{
// Files aren't extracted so this check can't be done
return false;
}
#endif
}
}

File diff suppressed because it is too large Load Diff

254
MPF.Processors/Generic.cs Normal file
View File

@@ -0,0 +1,254 @@
using System;
using System.Collections.Generic;
using System.IO;
using SabreTools.Hashing;
using SabreTools.Models.Logiqx;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Processors
{
/// <summary>
/// Represents processing generic outputs
/// </summary>
public sealed class Generic : BaseProcessor
{
/// <inheritdoc/>
public Generic(RedumpSystem? system, MediaType? type) : base(system, type) { }
#region BaseProcessor Implementations
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
{
// Ensure that required sections exist
info = Builder.EnsureAllSections(info);
// Get the dumping program and version
info.DumpingInfo!.DumpingProgram = "UNKNOWN PROGRAM";
info.DumpingInfo.DumpingDate = GetDumpingDate(basePath);
// Get the Datafile information
var datafile = GetDatafile(basePath);
if (datafile != null)
info.TracksAndWriteOffsets!.ClrMameProData = ProcessingTool.GenerateDatfile(datafile);
// Extract info based generically on MediaType
switch (Type)
{
case MediaType.CDROM:
case MediaType.GDROM:
info.TracksAndWriteOffsets!.Cuesheet = ProcessingTool.GetFullFile($"{basePath}.cue") ?? string.Empty;
break;
case MediaType.DVD:
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
case MediaType.HDDVD:
case MediaType.BluRay:
case MediaType.UMD:
var firstRom = datafile?.Game?[0]?.Rom?[0];
if (firstRom != null)
{
info.SizeAndChecksums!.Size = long.Parse(firstRom.Size ?? "0");
info.SizeAndChecksums.CRC32 = firstRom.CRC;
info.SizeAndChecksums.MD5 = firstRom.MD5;
info.SizeAndChecksums.SHA1 = firstRom.SHA1;
}
break;
}
}
/// <inheritdoc/>
internal override List<OutputFile> GetOutputFiles(string? baseDirectory, string baseFilename)
{
return [];
}
#endregion
#region Information Extraction Methods
/// <summary>
/// Attempt to get the dumping date from a base path
/// </summary>
private static string? GetDumpingDate(string basePath)
{
DateTime? fileModifiedDate;
if (File.Exists($"{basePath}.iso"))
fileModifiedDate = ProcessingTool.GetFileModifiedDate($"{basePath}.iso");
else if (File.Exists($"{basePath}.cue"))
fileModifiedDate = ProcessingTool.GetFileModifiedDate($"{basePath}.cue");
else if (File.Exists($"{basePath}.bin"))
fileModifiedDate = ProcessingTool.GetFileModifiedDate($"{basePath}.bin");
else if (File.Exists($"{basePath} (Track 1).bin"))
fileModifiedDate = ProcessingTool.GetFileModifiedDate($"{basePath} (Track 1).bin");
else if (File.Exists($"{basePath} (Track 01).bin"))
fileModifiedDate = ProcessingTool.GetFileModifiedDate($"{basePath} (Track 01).bin");
else if (File.Exists($"{basePath}.ccd"))
fileModifiedDate = ProcessingTool.GetFileModifiedDate($"{basePath}.ccd");
else if (File.Exists($"{basePath}.img"))
fileModifiedDate = ProcessingTool.GetFileModifiedDate($"{basePath}.img");
else if (File.Exists($"{basePath}.mds"))
fileModifiedDate = ProcessingTool.GetFileModifiedDate($"{basePath}.mds");
else if (File.Exists($"{basePath}.mdf"))
fileModifiedDate = ProcessingTool.GetFileModifiedDate($"{basePath}.mdf");
else if (File.Exists($"{basePath}.ima"))
fileModifiedDate = ProcessingTool.GetFileModifiedDate($"{basePath}.ima");
else
return null;
return fileModifiedDate?.ToString("yyyy-MM-dd HH:mm:ss");
}
/// <summary>
/// Attempt to generate a datafile from a base path
/// </summary>
private static Datafile? GetDatafile(string basePath)
{
// Single-file outputs
if (File.Exists($"{basePath}.bin"))
{
var rom = GetRom($"{basePath}.bin");
if (rom != null)
return GetDatafile(basePath, rom);
}
else if (File.Exists($"{basePath}.ima"))
{
var rom = GetRom($"{basePath}.ima");
if (rom != null)
return GetDatafile(basePath, rom);
}
else if (File.Exists($"{basePath}.img"))
{
var rom = GetRom($"{basePath}.img");
if (rom != null)
return GetDatafile(basePath, rom);
}
else if (File.Exists($"{basePath}.iso"))
{
var rom = GetRom($"{basePath}.iso");
if (rom != null)
return GetDatafile(basePath, rom);
}
else if (File.Exists($"{basePath}.mdf"))
{
var rom = GetRom($"{basePath}.mdf");
if (rom != null)
return GetDatafile(basePath, rom);
}
// Multi-file outputs
else if (File.Exists($"{basePath} (Track 1).bin"))
{
var roms = new List<Rom>();
// Try Track 0
var track0rom = GetRom($"{basePath} (Track 0).bin");
if (track0rom != null)
roms.Add(track0rom);
// Hash all found tracks
for (int i = 1; i < 10; i++)
{
// Get the rom for the track
var rom = GetRom($"{basePath} (Track {i}).bin");
if (rom == null)
break;
// Add the track to the output
roms.Add(rom);
}
// Try Track A
var trackArom = GetRom($"{basePath} (Track A).bin");
if (trackArom != null)
roms.Add(trackArom);
// Create and return if there are any tracks
if (roms.Count > 0)
return GetDatafile(basePath, roms);
}
else if (File.Exists($"{basePath} (Track 01).bin"))
{
var roms = new List<Rom>();
// Try Track 00
var track00rom = GetRom($"{basePath} (Track 00).bin");
if (track00rom != null)
roms.Add(track00rom);
// Hash all found tracks
for (int i = 1; i < 100; i++)
{
// Get the rom for the track
var rom = GetRom($"{basePath} (Track {i:D2}).bin");
if (rom == null)
break;
// Add the track to the output
roms.Add(rom);
}
// Try Track AA
var trackAArom = GetRom($"{basePath} (Track AA).bin");
if (trackAArom != null)
roms.Add(trackAArom);
// Create and return if there are any tracks
if (roms.Count > 0)
return GetDatafile(basePath, roms);
}
// Unrecognized file input
return null;
}
/// <summary>
/// Generate a Datafile from a base path and a Rom
/// </summary>
private static Datafile? GetDatafile(string basePath, Rom rom)
=> GetDatafile(basePath, [rom]);
/// <summary>
/// Generate a Datafile from a base path and a list of Roms
/// </summary>
private static Datafile? GetDatafile(string basePath, List<Rom> roms)
{
// Return null if no Roms exist
if (roms.Count == 0)
return null;
// Format the new Datafile
return new Datafile
{
Game =
[
new Game
{
Name = Path.GetFileName(basePath),
Rom = [.. roms]
}
]
};
}
/// <summary>
/// Get a Rom object for a single file
/// </summary>
private static Rom? GetRom(string file)
{
// Skip missing or invalid files
if (!File.Exists(file))
return null;
if (!HashTool.GetStandardHashes(file, out long filesize, out var crc32, out var md5, out var sha1))
return null;
// Format the data into a rom object
return new Rom { Name = Path.GetFileName(file), Size = filesize.ToString(), CRC = crc32, MD5 = md5, SHA1 = sha1 };
}
#endregion
}
}

View File

@@ -1,49 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- 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.2.0</VersionPrefix>
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<NoWarn>NU1902;NU1903</NoWarn>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.2.4</VersionPrefix>
<WarningsNotAsErrors>NU5104</WarningsNotAsErrors>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<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>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<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>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<!-- 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(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>
<ItemGroup>
<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>
<!-- Support for old .NET versions -->
<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(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
</ItemGroup>
</Project>
<ItemGroup>
<PackageReference Include="psxt001z.Library" Version="0.21.0-rc2" />
<PackageReference Include="SabreTools.Hashing" Version="1.4.0" />
<PackageReference Include="SabreTools.Models" Version="1.5.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.5.2" />
<PackageReference Include="SabreTools.Serialization" Version="1.7.6" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,250 @@
using System;
using System.IO;
#if NET452_OR_GREATER || NETCOREAPP
using System.IO.Compression;
using System.Linq;
#endif
namespace MPF.Processors
{
/// <summary>
/// Represents attributes about an <see cref="OutputFile"/>
/// </summary>
[Flags]
internal enum OutputFileFlags : ushort
{
/// <summary>
/// Default state
/// </summary>
None = 0x0000,
/// <summary>
/// File is required to exist
/// </summary>
Required = 0x0001,
/// <summary>
/// File is included as an artifact
/// </summary>
Artifact = 0x0002,
/// <summary>
/// File is included as a binary artifact
/// </summary>
Binary = 0x0004,
/// <summary>
/// File can be deleted after processing
/// </summary>
Deleteable = 0x0008,
/// <summary>
/// File can be zipped after processing
/// </summary>
Zippable = 0x0010,
}
/// <summary>
/// Represents a single output file
/// </summary>
internal class OutputFile
{
/// <summary>
/// Set of all filename variants
/// </summary>
public string[] Filenames { get; private set; }
/// <summary>
/// Key used when creating an artifact
/// </summary>
public string? ArtifactKey { get; private set; }
/// <summary>
/// Indicates if the file is required
/// </summary>
public bool IsRequired
{
get
{
#if NET20 || NET35
return (_flags & OutputFileFlags.Required) != 0;
#else
return _flags.HasFlag(OutputFileFlags.Required);
#endif
}
}
/// <summary>
/// Indicates if the file is an artifact
/// </summary>
public bool IsArtifact
{
get
{
#if NET20 || NET35
return (_flags & OutputFileFlags.Artifact) != 0
|| (_flags & OutputFileFlags.Binary) != 0;
#else
return _flags.HasFlag(OutputFileFlags.Artifact)
|| _flags.HasFlag(OutputFileFlags.Binary);
#endif
}
}
/// <summary>
/// Indicates if the file is a binary artifact
/// </summary>
public bool IsBinaryArtifact
{
get
{
#if NET20 || NET35
return (_flags & OutputFileFlags.Binary) != 0;
#else
return _flags.HasFlag(OutputFileFlags.Binary);
#endif
}
}
/// <summary>
/// Indicates if the file is deleteable after processing
/// </summary>
public bool IsDeleteable
{
get
{
#if NET20 || NET35
return (_flags & OutputFileFlags.Deleteable) != 0;
#else
return _flags.HasFlag(OutputFileFlags.Deleteable);
#endif
}
}
/// <summary>
/// Indicates if the file is zippable after processing
/// </summary>
public bool IsZippable
{
get
{
#if NET20 || NET35
return (_flags & OutputFileFlags.Zippable) != 0;
#else
return _flags.HasFlag(OutputFileFlags.Zippable);
#endif
}
}
/// <summary>
/// Represents attributes about the current file
/// </summary>
protected readonly OutputFileFlags _flags;
/// <summary>
/// Create an OutputFile with a single filename
/// </summary>
public OutputFile(string filename, OutputFileFlags flags)
: this([filename], flags)
{
}
/// <summary>
/// Create an OutputFile with a single filename
/// </summary>
public OutputFile(string filename, OutputFileFlags flags, string artifactKey)
: this([filename], flags, artifactKey)
{
}
/// <summary>
/// Create an OutputFile with set of filenames
/// </summary>
public OutputFile(string[] filenames, OutputFileFlags flags)
{
Filenames = filenames;
ArtifactKey = null;
_flags = flags;
// Validate the inputs
if (filenames.Length == 0)
throw new ArgumentException($"{nameof(filenames)} must contain at least one value");
if (IsArtifact && string.IsNullOrEmpty(ArtifactKey))
throw new ArgumentException($"{nameof(flags)} should not contain the Artifact or Binary flag");
}
/// <summary>
/// Create an OutputFile with set of filenames
/// </summary>
public OutputFile(string[] filenames, OutputFileFlags flags, string artifactKey)
{
Filenames = filenames;
ArtifactKey = artifactKey;
_flags = flags;
// Validate the inputs
if (filenames.Length == 0)
throw new ArgumentException($"{nameof(filenames)} must contain at least one value");
if (IsArtifact && string.IsNullOrEmpty(ArtifactKey))
throw new ArgumentException($"{nameof(flags)} should not contain the Artifact or Binary flag");
}
/// <summary>
/// Indicates if an output file exists in a base directory
/// </summary>
/// <param name="baseDirectory">Base directory to check in</param>
public virtual bool Exists(string baseDirectory)
{
// Ensure the directory exists
if (!Directory.Exists(baseDirectory))
return false;
foreach (string filename in Filenames)
{
// Check for invalid filenames
if (string.IsNullOrEmpty(filename))
continue;
try
{
string possibleFile = Path.Combine(baseDirectory, filename);
if (File.Exists(possibleFile))
return true;
}
catch { }
}
return false;
}
#if NET452_OR_GREATER || NETCOREAPP
/// <summary>
/// Indicates if an output file exists in an archive
/// </summary>
/// <param name="archive">Zip archive to check in</param>
public virtual bool Exists(ZipArchive? archive)
{
// If the archive is invalid
if (archive == null)
return false;
foreach (string filename in Filenames)
{
// Check for invalid filenames
if (string.IsNullOrEmpty(filename))
continue;
try
{
// Check all entries on filename alone
if (archive.Entries.Any(e => e.Name == filename))
return true;
}
catch { }
}
return false;
}
#endif
}
}

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using SabreTools.Hashing;
using SabreTools.Models.Logiqx;
using SabreTools.RedumpLib;
@@ -18,40 +17,6 @@ namespace MPF.Processors
#region BaseProcessor Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
if (Type != MediaType.BluRay || 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 GenerateArtifacts(SubmissionInfo info, string basePath)
{
info.Artifacts ??= [];
string? getKeyBasePath = GetCFWBasePath(basePath);
if (File.Exists(getKeyBasePath + ".disc.pic"))
info.Artifacts["discpic"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(getKeyBasePath + ".disc.pic", binary: true)) ?? string.Empty;
if (File.Exists(getKeyBasePath + ".getkey.log"))
info.Artifacts["getkeylog"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(getKeyBasePath + ".getkey.log")) ?? string.Empty;
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
{
@@ -102,34 +67,30 @@ namespace MPF.Processors
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;
}
info.Extras.PIC = SplitString(pic, 32);
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
internal override List<OutputFile> GetOutputFiles(string? baseDirectory, string baseFilename)
{
var logFiles = new List<string>();
string? getKeyBasePath = GetCFWBasePath(basePath);
if (System != RedumpSystem.SonyPlayStation3)
return logFiles;
switch (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 [
new($"{baseFilename}.iso", OutputFileFlags.Required),
new($"{baseFilename}.getkey.log", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"getkey_log"),
new($"{baseFilename}.disc.pic", OutputFileFlags.Required
| OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"disc_pic"),
];
}
return logFiles;
return [];
}
#endregion

View File

@@ -1,11 +1,14 @@
using System;
using System.IO;
#if NET35_OR_GREATER || NETCOREAPP
using System.Linq;
#endif
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using SabreTools.IO.Extensions;
using SabreTools.Models.Logiqx;
using SabreTools.Models.PIC;
using SabreTools.RedumpLib.Data;
@@ -31,14 +34,18 @@ namespace MPF.Processors
return null;
// Convert ASCII to byte via lookup table
int[] hexLookup = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F];
int[] hexLookup =
[
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
];
byte[] byteArray = new byte[hexString.Length / 2];
for (int i = 0; i < hexString.Length; i += 2)
{
// Convert next two chars to ASCII value relative to '0'
int a = Char.ToUpper(hexString[i]) - '0';
int b = Char.ToUpper(hexString[i + 1]) - '0';
int a = Char.ToUpperInvariant(hexString[i]) - '0';
int b = Char.ToUpperInvariant(hexString[i + 1]) - '0';
// Ensure hex string only has '0' through '9' and 'A' through 'F' (case insensitive)
if ((a < 0 || b < 0 || a > 22 || b > 22) || (a > 10 && a < 17) || (b > 10 && b < 17))
@@ -270,20 +277,13 @@ namespace MPF.Processors
if (di?.Units == null || di.Units.Length <= 1)
return false;
// Wrap big-endian reading
static int ReadFromArrayBigEndian(byte[]? bytes, int offset)
{
if (bytes == null)
return default;
#if NET20 || NET35 || NET40
byte[] rev = new byte[0x04];
Array.Copy(bytes, offset, rev, 0, 0x04);
#else
var span = new ReadOnlySpan<byte>(bytes, offset, 0x04);
byte[] rev = span.ToArray();
#endif
Array.Reverse(rev);
return BitConverter.ToInt32(rev, 0);
return bytes.ReadInt32BigEndian(ref offset);
}
// Layerbreak 1 (2+ layers)
@@ -352,6 +352,91 @@ namespace MPF.Processors
#region Region Extraction
/// <summary>
/// Determine the region based on the PlayStation serial code
/// </summary>
/// <param name="serial">PlayStation serial code</param>
/// <returns>Region mapped from name, if possible</returns>
public static Region? GetPlayStationRegion(string? serial)
{
// If we have a fully invalid serial
if (string.IsNullOrEmpty(serial))
return null;
// Standardized "S" serials
if (serial!.StartsWith("S"))
{
// string publisher = serial[0] + serial[1];
// char secondRegion = serial[3];
switch (serial[2])
{
case 'A': return Region.Asia;
case 'C': return Region.China;
case 'E': return Region.Europe;
case 'K': return Region.SouthKorea;
case 'U': return Region.UnitedStatesOfAmerica;
case 'P':
// Region of S_P_ serials may be Japan, Asia, or SouthKorea
return serial[3] switch
{
// Check first two digits of S_PS serial
'S' => (Region?)(serial.Substring(5, 2) switch
{
"46" => Region.SouthKorea,
"51" => Region.Asia,
"56" => Region.SouthKorea,
"55" => Region.Asia,
_ => Region.Japan,
}),
// Check first three digits of S_PM serial
'M' => (Region?)(serial.Substring(5, 3) switch
{
"645" => Region.SouthKorea,
"675" => Region.SouthKorea,
"885" => Region.SouthKorea,
_ => Region.Japan, // Remaining S_PM serials may be Japan or Asia
}),
_ => (Region?)Region.Japan,
};
}
}
// Japan-only special serial
else if (serial.StartsWith("PAPX"))
return Region.Japan;
// Region appears entirely random
else if (serial.StartsWith("PABX"))
return null;
// Region appears entirely random
else if (serial.StartsWith("PBPX"))
return null;
// Japan-only special serial
else if (serial.StartsWith("PCBX"))
return Region.Japan;
// Japan-only special serial
else if (serial.StartsWith("PCXC"))
return Region.Japan;
// Single disc known, Japan
else if (serial.StartsWith("PDBX"))
return Region.Japan;
// Single disc known, Europe
else if (serial.StartsWith("PEBX"))
return Region.Europe;
// Single disc known, USA
else if (serial.StartsWith("PUBX"))
return Region.UnitedStatesOfAmerica;
return null;
}
/// <summary>
/// Determine the region based on the XGD serial character
/// </summary>
@@ -660,7 +745,7 @@ namespace MPF.Processors
if (string.IsNullOrEmpty(inputPIC))
return null;
string cleanPIC = inputPIC!.Trim().Replace("\n", string.Empty);
string cleanPIC = inputPIC!.Trim().Replace("\n", string.Empty).Replace("\r", string.Empty);
if (cleanPIC.Length < 230)
return null;
@@ -731,7 +816,7 @@ namespace MPF.Processors
/// </summary>
/// <param name="dmi">DMI.bin file location</param>
/// <returns>String representation of the XGD1 DMI information, empty string on error</returns>
public static string GetXGD1XMID(string dmi)
public static string GetXMID(string dmi)
{
if (!File.Exists(dmi))
return string.Empty;
@@ -753,7 +838,7 @@ namespace MPF.Processors
/// </summary>
/// <param name="dmi">DMI.bin file location</param>
/// <returns>String representation of the XGD2/3 DMI information, empty string on error</returns>
public static string GetXGD23XeMID(string dmi)
public static string GetXeMID(string dmi)
{
if (!File.Exists(dmi))
return string.Empty;
@@ -836,7 +921,13 @@ namespace MPF.Processors
if (!GetXGDType(ss, out int xgdType))
return false;
if (xgdType == 3 && ss.Skip(32).Take(72).All(x => x == 0))
#if NET20
var checkArr = new byte[72];
Array.Copy(ss, 32, checkArr, 0, 72);
if (xgdType == 3 && Array.Exists(checkArr, x => x != 0))
#else
if (xgdType == 3 && ss.Skip(32).Take(72).Any(x => x != 0))
#endif
{
// Check for a cleaned SSv2
@@ -988,7 +1079,13 @@ namespace MPF.Processors
case 3:
// Determine if XGD3 SS.bin is SSv1 (Kreon) or SSv2 (0800)
bool ssv2 = ss.Skip(32).Take(72).All(x => x == 0);
#if NET20
var checkArr = new byte[72];
Array.Copy(ss, 32, checkArr, 0, 72);
bool ssv2 = Array.Exists(checkArr, x => x != 0);
#else
bool ssv2 = ss.Skip(32).Take(72).Any(x => x != 0);
#endif
if (ssv2)
{
@@ -1089,12 +1186,18 @@ namespace MPF.Processors
startLBA[i] = (1913776 + 0x030000) * 2 - (startPSN ^ 0xFFFFFF) - 0x030000 - 1;
endLBA[i] = (1913776 + 0x030000) * 2 - (endPSN ^ 0xFFFFFF) - 0x030000 - 1;
}
else if (xgdType > 1 && startPSN >= (1913760 + 0x030000))
else if (xgdType == 2 && startPSN >= (1913760 + 0x030000))
{
// Layer 1 of XGD2 or XGD3
// Layer 1 of XGD2
startLBA[i] = (1913760 + 0x030000) * 2 - (startPSN ^ 0xFFFFFF) - 0x030000 - 1;
endLBA[i] = (1913760 + 0x030000) * 2 - (endPSN ^ 0xFFFFFF) - 0x030000 - 1;
}
else if (xgdType == 3 && startPSN >= (2133520 + 0x030000))
{
// Layer 1 of XGD3
startLBA[i] = (2133520 + 0x030000) * 2 - (startPSN ^ 0xFFFFFF) - 0x030000 - 1;
endLBA[i] = (2133520 + 0x030000) * 2 - (endPSN ^ 0xFFFFFF) - 0x030000 - 1;
}
else
{
// Layer 0

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
using System;
using System.IO;
#if NET452_OR_GREATER || NETCOREAPP
using System.IO.Compression;
using System.Linq;
#endif
using System.Text.RegularExpressions;
namespace MPF.Processors
{
/// <summary>
/// Represents a single output file with a Regex-matched name
/// </summary>
internal class RegexOutputFile : OutputFile
{
/// <summary>
/// Create an OutputFile with a single filename
/// </summary>
public RegexOutputFile(string filename, OutputFileFlags flags)
: base([filename], flags)
{
}
/// <summary>
/// Create an OutputFile with a single filename
/// </summary>
public RegexOutputFile(string filename, OutputFileFlags flags, string artifactKey)
: base([filename], flags, artifactKey)
{
}
/// <summary>
/// Create an OutputFile with set of filenames
/// </summary>
public RegexOutputFile(string[] filenames, OutputFileFlags flags)
: base(filenames, flags)
{
}
/// <summary>
/// Create an OutputFile with set of filenames
/// </summary>
public RegexOutputFile(string[] filenames, OutputFileFlags flags, string artifactKey)
: base(filenames, flags, artifactKey)
{
}
/// <inheritdoc/>
public override bool Exists(string baseDirectory)
{
// Ensure the directory exists
if (!Directory.Exists(baseDirectory))
return false;
// Get list of all files in directory
var directoryFiles = Directory.GetFiles(baseDirectory);
foreach (string file in directoryFiles)
{
if (Array.FindIndex(Filenames, pattern => Regex.IsMatch(file, pattern)) > -1)
return true;
}
return false;
}
#if NET452_OR_GREATER || NETCOREAPP
/// <summary>
/// Indicates if an output file exists in an archive
/// </summary>
/// <param name="archive">Zip archive to check in</param>
public override bool Exists(ZipArchive? archive)
{
// If the archive is invalid
if (archive == null)
return false;
// Get list of all files in archive
var archiveFiles = archive.Entries.Select(e => e.Name).ToList();
foreach (string file in archiveFiles)
{
if (Array.Exists(Filenames, pattern => Regex.IsMatch(file, pattern)))
return true;
}
return false;
}
#endif
}
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SabreTools.Hashing;
using SabreTools.Models.Logiqx;
using SabreTools.RedumpLib;
@@ -19,54 +18,6 @@ namespace MPF.Processors
#region BaseProcessor Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
switch (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 GenerateArtifacts(SubmissionInfo info, string basePath)
{
info.Artifacts ??= [];
if (File.Exists($"{basePath}_disc.txt"))
info.Artifacts["disc"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_disc.txt")) ?? string.Empty;
if (File.Exists($"{basePath}_drive.txt"))
info.Artifacts["drive"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_drive.txt")) ?? string.Empty;
if (File.Exists($"{basePath}_mainError.txt"))
info.Artifacts["mainError"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_mainError.txt")) ?? string.Empty;
if (File.Exists($"{basePath}_mainInfo.txt"))
info.Artifacts["mainInfo"] = ProcessingTool.GetBase64(ProcessingTool.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"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_volDesc.txt")) ?? string.Empty;
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
{
@@ -103,15 +54,22 @@ namespace MPF.Processors
info.SizeAndChecksums.SHA1 = sha1;
}
if (GetUMDAuxInfo(basePath + "_disc.txt", out var title, out DiscCategory? umdcat, out var umdversion, out var umdlayer, out long umdsize))
if (GetUMDAuxInfo(basePath + "_disc.txt",
out var title,
out DiscCategory? category,
out string? serial,
out var version,
out var layer,
out long size))
{
info.CommonDiscInfo!.Title = title ?? string.Empty;
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
info.VersionAndEditions!.Version = umdversion ?? string.Empty;
info.SizeAndChecksums!.Size = umdsize;
info.CommonDiscInfo.Category = category ?? DiscCategory.Games;
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = serial ?? string.Empty;
info.VersionAndEditions!.Version = version ?? string.Empty;
info.SizeAndChecksums!.Size = size;
if (!string.IsNullOrEmpty(umdlayer))
info.SizeAndChecksums.Layerbreak = Int64.Parse(umdlayer ?? "-1");
if (!string.IsNullOrEmpty(layer))
info.SizeAndChecksums.Layerbreak = Int64.Parse(layer ?? "-1");
}
break;
@@ -119,30 +77,40 @@ namespace MPF.Processors
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
internal override List<OutputFile> GetOutputFiles(string? baseDirectory, string baseFilename)
{
var logFiles = new List<string>();
switch (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");
return [
new($"{baseFilename}.iso", OutputFileFlags.Required),
if (File.Exists($"{basePath}_PFI.bin"))
logFiles.Add($"{basePath}_PFI.bin");
break;
new($"{baseFilename}_disc.txt", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"disc"),
new($"{baseFilename}_drive.txt", OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"drive"),
new($"{baseFilename}_mainError.txt", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"main_error"),
new($"{baseFilename}_mainInfo.txt", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"main_info"),
new($"{baseFilename}_PFI.bin", OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"pfi"),
new($"{baseFilename}_volDesc.txt", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"vol_desc"),
];
}
return logFiles;
return [];
}
#endregion
@@ -188,9 +156,17 @@ namespace MPF.Processors
/// </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)
private static bool GetUMDAuxInfo(string disc,
out string? title,
out DiscCategory? category,
out string? serial,
out string? version,
out string? layer,
out long size)
{
title = null; umdcat = null; umdversion = null; umdlayer = null; umdsize = -1;
title = null; serial = null; version = null; layer = null;
category = null;
size = -1;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(disc))
@@ -207,20 +183,26 @@ namespace MPF.Processors
break;
if (line.StartsWith("TITLE") && title == null)
title = line.Substring("TITLE: ".Length);
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
umdversion = line.Split(' ')[1];
title = line.Split(' ')[1];
else if (line.StartsWith("DISC_ID") && version == null)
serial = line.Split(' ')[1];
else if (line.StartsWith("DISC_VERSION") && version == null)
version = line.Split(' ')[1];
else if (line.StartsWith("pspUmdTypes"))
umdcat = ProcessingTool.GetUMDCategory(line.Split(' ')[1]);
category = ProcessingTool.GetUMDCategory(line.Split(' ')[1]);
else if (line.StartsWith("L0 length"))
umdlayer = line.Split(' ')[2];
layer = line.Split(' ')[2];
else if (line.StartsWith("FileSize:"))
umdsize = Int64.Parse(line.Split(' ')[1]);
size = Int64.Parse(line.Split(' ')[1]);
}
// If we have a serial, format it
if (!string.IsNullOrEmpty(serial) && serial!.Length > 4)
serial = serial.Substring(0, 4) + "-" + serial.Substring(4);
// 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;
if (Int64.TryParse(layer, out long umdlayerValue) && umdlayerValue * 2048 == size)
layer = null;
return true;
}

View File

@@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SabreTools.Hashing;
using SabreTools.Models.Logiqx;
using SabreTools.RedumpLib;
@@ -18,64 +17,6 @@ namespace MPF.Processors
#region BaseProcessor Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
switch (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 GenerateArtifacts(SubmissionInfo info, string basePath)
{
info.Artifacts ??= [];
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
string? logPath = GetLogName(baseDir);
if (File.Exists(logPath))
info.Artifacts["log"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(logPath!)) ?? string.Empty;
if (File.Exists($"{basePath}.dvd"))
info.Artifacts["dvd"] = ProcessingTool.GetBase64(ProcessingTool.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 void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
{
@@ -83,10 +24,10 @@ namespace MPF.Processors
info = Builder.EnsureAllSections(info);
// Get base directory
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
// Get log filename
string? logPath = GetLogName(baseDir);
string? logPath = GetLogName(baseDirectory);
if (string.IsNullOrEmpty(logPath))
return;
@@ -132,7 +73,7 @@ namespace MPF.Processors
case RedumpSystem.MicrosoftXbox:
// Parse DMI.bin
string xmidString = ProcessingTool.GetXGD1XMID($"{baseDir}DMI.bin");
string xmidString = ProcessingTool.GetXMID(Path.Combine(baseDirectory, "DMI.bin"));
var xmid = SabreTools.Serialization.Wrappers.XMID.Create(xmidString);
if (xmid != null)
{
@@ -156,7 +97,7 @@ namespace MPF.Processors
//string? mediaID = GetMediaID(logPath);
// Parse DMI.bin
string xemidString = ProcessingTool.GetXGD23XeMID($"{baseDir}DMI.bin");
string xemidString = ProcessingTool.GetXeMID(Path.Combine(baseDirectory, "DMI.bin"));
var xemid = SabreTools.Serialization.Wrappers.XeMID.Create(xemidString);
if (xemid != null)
{
@@ -171,62 +112,73 @@ namespace MPF.Processors
break;
}
// Get the output file paths
string dmiPath = Path.Combine(baseDirectory, "DMI.bin");
string pfiPath = Path.Combine(baseDirectory, "PFI.bin");
string ssPath = Path.Combine(baseDirectory, "SS.bin");
// Deal with SS.bin
if (File.Exists($"{baseDir}SS.bin"))
if (File.Exists(ssPath))
{
// Save security sector ranges
string? ranges = ProcessingTool.GetSSRanges($"{baseDir}SS.bin");
string? ranges = ProcessingTool.GetSSRanges(ssPath);
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");
RecreateSS(logPath!, ssPath, Path.Combine(baseDirectory, "RawSS.bin"));
// Run ss_sector_range to get repeatable SS hash
ProcessingTool.CleanSS($"{baseDir}SS.bin", $"{baseDir}SS.bin");
ProcessingTool.CleanSS(ssPath, ssPath);
}
// 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;
if (File.Exists(dmiPath))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = HashTool.GetFileHash(dmiPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
if (File.Exists(pfiPath))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.PFIHash] = HashTool.GetFileHash(pfiPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
if (File.Exists(ssPath))
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSHash] = HashTool.GetFileHash(ssPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
break;
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
internal override List<OutputFile> GetOutputFiles(string? baseDirectory, string baseFilename)
{
var logFiles = new List<string>();
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
switch (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 [
new($"{baseFilename}.dvd", OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"dvd"),
new($"{baseFilename}.iso", OutputFileFlags.Required),
new("DMI.bin", OutputFileFlags.Required
| OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"dmi"),
new RegexOutputFile("[lL]og.txt", OutputFileFlags.Required
| OutputFileFlags.Artifact
| OutputFileFlags.Zippable,
"log"),
new("PFI.bin", OutputFileFlags.Required
| OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"pfi"),
new("RawSS.bin", OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"raw_ss"),
new("SS.bin", OutputFileFlags.Required
| OutputFileFlags.Binary
| OutputFileFlags.Zippable,
"ss"),
];
}
return logFiles;
return [];
}
#endregion
@@ -240,8 +192,8 @@ namespace MPF.Processors
/// <returns>Log path if found, null otherwise</returns>
private static string? GetLogName(string baseDir)
{
if (IsSuccessfulLog($"{baseDir}Log.txt"))
return $"{baseDir}Log.txt";
if (IsSuccessfulLog(Path.Combine(baseDir, "Log.txt")))
return Path.Combine(baseDir, "Log.txt");
// Search for a renamed log file (assume there is only one)
string[] files = Directory.GetFiles(baseDir, "*.txt", SearchOption.TopDirectoryOnly);

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Frontend;
using Xunit;
@@ -47,7 +46,7 @@ namespace MPF.Test.Frontend
var testData = new List<object?[]>() { new object?[] { null, true } };
foreach (DriveType driveType in Enum.GetValues(typeof(DriveType)))
{
if (_mappableDriveTypes.Contains(driveType))
if (Array.IndexOf(_mappableDriveTypes, driveType) > -1)
testData.Add([driveType, false]);
else
testData.Add([driveType, true]);

View File

@@ -20,7 +20,7 @@ namespace MPF.Test.Frontend.Tools
[InlineData("superhero\\blah&foo.bin", "superhero\\blah&foo.bin")]
public void NormalizeOutputPathsTest(string? outputPath, string? expectedPath)
{
if (!string.IsNullOrWhiteSpace(expectedPath))
if (!string.IsNullOrEmpty(expectedPath))
expectedPath = Path.GetFullPath(expectedPath);
string actualPath = FrontendTool.NormalizeOutputPaths(outputPath, true);

View File

@@ -1,39 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0;net9.0</TargetFrameworks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.ExecutionContexts\MPF.ExecutionContexts.csproj" />
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.ExecutionContexts\MPF.ExecutionContexts.csproj" />
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
</ItemGroup>
<ItemGroup>
<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.3.8" />
<PackageReference Include="xunit" Version="2.8.0" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<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.8.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="17.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.5.2" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="1.17.0" />
<PackageReference Include="xunit.assert" Version="2.9.2" />
<PackageReference Include="xunit.core" Version="2.9.2" />
<PackageReference Include="xunit.extensibility.core" Version="2.9.2" />
<PackageReference Include="xunit.extensibility.execution" Version="2.9.2" />
<PackageReference Include="xunit.runner.console" Version="2.9.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
</Project>

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using static MPF.Frontend.InterfaceConstants;
@@ -34,7 +33,7 @@ namespace MPF.UI
/// <summary>
/// Create a DoubleCollection out of a list of integer values
/// </summary>
private static DoubleCollection GetDoubleCollectionFromIntList(IList<int> list)
=> new(list.Select(i => Convert.ToDouble(i)).ToList());
private static DoubleCollection GetDoubleCollectionFromIntList(List<int> list)
=> [.. list.ConvertAll(i => Convert.ToDouble(i))];
}
}

View File

@@ -32,19 +32,11 @@ namespace WPFCustomMessageBox
{
get
{
#if NET35
return _TextBlock_Message!.Text;
#else
return TextBlock_Message.Text;
#endif
return TextBlock_Message!.Text;
}
set
{
#if NET35
_TextBlock_Message!.Text = value;
#else
TextBlock_Message.Text = value;
#endif
TextBlock_Message!.Text = value;
}
}
@@ -52,19 +44,11 @@ namespace WPFCustomMessageBox
{
get
{
#if NET35
return _Label_Ok!.Content.ToString();
#else
return Label_Ok.Content.ToString();
#endif
return Label_Ok!.Content.ToString();
}
set
{
#if NET35
_Label_Ok!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Ok.Content = value.TryAddKeyboardAccellerator();
#endif
Label_Ok!.Content = value.TryAddKeyboardAccellerator();
}
}
@@ -72,19 +56,11 @@ namespace WPFCustomMessageBox
{
get
{
#if NET35
return _Label_Cancel!.Content.ToString();
#else
return Label_Cancel.Content.ToString();
#endif
return Label_Cancel!.Content.ToString();
}
set
{
#if NET35
_Label_Cancel!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Cancel.Content = value.TryAddKeyboardAccellerator();
#endif
Label_Cancel!.Content = value.TryAddKeyboardAccellerator();
}
}
@@ -92,19 +68,11 @@ namespace WPFCustomMessageBox
{
get
{
#if NET35
return _Label_Yes!.Content.ToString();
#else
return Label_Yes.Content.ToString();
#endif
return Label_Yes!.Content.ToString();
}
set
{
#if NET35
_Label_Yes!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Yes.Content = value.TryAddKeyboardAccellerator();
#endif
Label_Yes!.Content = value.TryAddKeyboardAccellerator();
}
}
@@ -112,19 +80,11 @@ namespace WPFCustomMessageBox
{
get
{
#if NET35
return _Label_No!.Content.ToString();
#else
return Label_No.Content.ToString();
#endif
return Label_No!.Content.ToString();
}
set
{
#if NET35
_Label_No!.Content = value.TryAddKeyboardAccellerator();
#else
Label_No.Content = value.TryAddKeyboardAccellerator();
#endif
Label_No!.Content = value.TryAddKeyboardAccellerator();
}
}
@@ -132,16 +92,16 @@ namespace WPFCustomMessageBox
#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 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");
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
@@ -185,11 +145,7 @@ namespace WPFCustomMessageBox
if (image.HasValue)
DisplayImage(image.Value);
else
#if NET35
_Image_MessageBox!.Visibility = Visibility.Collapsed;
#else
Image_MessageBox.Visibility = Visibility.Collapsed;
#endif
Image_MessageBox!.Visibility = Visibility.Collapsed;
}
protected override void OnSourceInitialized(EventArgs e)
@@ -206,75 +162,39 @@ namespace WPFCustomMessageBox
{
case MessageBoxButton.OKCancel:
// Hide all but OK, Cancel
#if NET35
_Button_OK!.Visibility = Visibility.Visible;
_Button_OK.Focus();
_Button_Cancel!.Visibility = Visibility.Visible;
_Button_Yes!.Visibility = Visibility.Collapsed;
_Button_No!.Visibility = Visibility.Collapsed;
#else
Button_OK.Visibility = Visibility.Visible;
Button_OK!.Visibility = Visibility.Visible;
Button_OK.Focus();
Button_Cancel.Visibility = Visibility.Visible;
Button_Cancel!.Visibility = Visibility.Visible;
Button_Yes.Visibility = Visibility.Collapsed;
Button_No.Visibility = Visibility.Collapsed;
#endif
Button_Yes!.Visibility = Visibility.Collapsed;
Button_No!.Visibility = Visibility.Collapsed;
break;
case MessageBoxButton.YesNo:
// Hide all but Yes, No
#if NET35
_Button_Yes!.Visibility = Visibility.Visible;
_Button_Yes.Focus();
_Button_No!.Visibility = Visibility.Visible;
_Button_OK!.Visibility = Visibility.Collapsed;
_Button_Cancel!.Visibility = Visibility.Collapsed;
#else
Button_Yes.Visibility = Visibility.Visible;
Button_Yes!.Visibility = Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = Visibility.Visible;
Button_No!.Visibility = Visibility.Visible;
Button_OK.Visibility = Visibility.Collapsed;
Button_Cancel.Visibility = Visibility.Collapsed;
#endif
Button_OK!.Visibility = Visibility.Collapsed;
Button_Cancel!.Visibility = Visibility.Collapsed;
break;
case MessageBoxButton.YesNoCancel:
// Hide only OK
#if NET35
_Button_Yes!.Visibility = Visibility.Visible;
_Button_Yes.Focus();
_Button_No!.Visibility = Visibility.Visible;
_Button_Cancel!.Visibility = Visibility.Visible;
_Button_OK!.Visibility = Visibility.Collapsed;
#else
Button_Yes.Visibility = Visibility.Visible;
Button_Yes!.Visibility = Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = Visibility.Visible;
Button_Cancel.Visibility = Visibility.Visible;
Button_No!.Visibility = Visibility.Visible;
Button_Cancel!.Visibility = Visibility.Visible;
Button_OK.Visibility = Visibility.Collapsed;
#endif
Button_OK!.Visibility = Visibility.Collapsed;
break;
default:
// Hide all but OK
#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!.Visibility = Visibility.Visible;
Button_OK.Focus();
Button_Yes.Visibility = Visibility.Collapsed;
Button_No.Visibility = Visibility.Collapsed;
Button_Cancel.Visibility = Visibility.Collapsed;
#endif
Button_Yes!.Visibility = Visibility.Collapsed;
Button_No!.Visibility = Visibility.Collapsed;
Button_Cancel!.Visibility = Visibility.Collapsed;
break;
}
}
@@ -302,13 +222,8 @@ namespace WPFCustomMessageBox
break;
}
#if NET35
_Image_MessageBox!.Source = icon.ToImageSource();
_Image_MessageBox.Visibility = Visibility.Visible;
#else
Image_MessageBox.Source = icon.ToImageSource();
Image_MessageBox!.Source = icon.ToImageSource();
Image_MessageBox.Visibility = Visibility.Visible;
#endif
}
private void Button_OK_Click(object sender, RoutedEventArgs e)

View File

@@ -1,6 +1,6 @@
using System.Windows.Controls;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
namespace MPF.UI
{

View File

@@ -1,130 +1,130 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>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>
<OutputType>WinExe</OutputType>
<ApplicationIcon>Images\Icon.ico</ApplicationIcon>
<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>
<VersionPrefix>3.2.0</VersionPrefix>
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net40;net452;net462;net472;net48;netcoreapp3.1;net5.0-windows;net6.0-windows;net7.0-windows;net8.0-windows;net9.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
<OutputType>WinExe</OutputType>
<ApplicationIcon>Images\Icon.ico</ApplicationIcon>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<ImportFrameworkWinFXTargets Condition="$(TargetFramework.StartsWith(`net3`))">true</ImportFrameworkWinFXTargets>
<LangVersion>latest</LangVersion>
<NoWarn>NU1902;NU1903</NoWarn>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<VersionPrefix>3.2.4</VersionPrefix>
<!-- Package Properties -->
<AssemblyName>MPF</AssemblyName>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Frontend 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>
<!-- Package Properties -->
<AssemblyName>MPF</AssemblyName>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Frontend 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>
<!-- Special handling for Aero2 on non-Framework .NET -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net4`))">
<ReferenceWpfAeroTheme>true</ReferenceWpfAeroTheme>
<ReferenceWpfAero2Theme>false</ReferenceWpfAero2Theme>
<ReferenceWpfAeroLiteTheme>false</ReferenceWpfAeroLiteTheme>
<ReferenceWpfClassicTheme>false</ReferenceWpfClassicTheme>
<ReferenceWpfLunaTheme>false</ReferenceWpfLunaTheme>
<ReferenceWpfRoyaleTheme>false</ReferenceWpfRoyaleTheme>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`)) OR $(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
<ReferenceWpfAeroTheme>false</ReferenceWpfAeroTheme>
<ReferenceWpfAero2Theme>true</ReferenceWpfAero2Theme>
<ReferenceWpfAeroLiteTheme>false</ReferenceWpfAeroLiteTheme>
<ReferenceWpfClassicTheme>false</ReferenceWpfClassicTheme>
<ReferenceWpfLunaTheme>false</ReferenceWpfLunaTheme>
<ReferenceWpfRoyaleTheme>false</ReferenceWpfRoyaleTheme>
</PropertyGroup>
<!-- Special handling for Aero2 on non-Framework .NET -->
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net4`))">
<ReferenceWpfAeroTheme>true</ReferenceWpfAeroTheme>
<ReferenceWpfAero2Theme>false</ReferenceWpfAero2Theme>
<ReferenceWpfAeroLiteTheme>false</ReferenceWpfAeroLiteTheme>
<ReferenceWpfClassicTheme>false</ReferenceWpfClassicTheme>
<ReferenceWpfLunaTheme>false</ReferenceWpfLunaTheme>
<ReferenceWpfRoyaleTheme>false</ReferenceWpfRoyaleTheme>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`)) OR $(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`)) OR $(TargetFramework.StartsWith(`net9`))">
<ReferenceWpfAeroTheme>false</ReferenceWpfAeroTheme>
<ReferenceWpfAero2Theme>true</ReferenceWpfAero2Theme>
<ReferenceWpfAeroLiteTheme>false</ReferenceWpfAeroLiteTheme>
<ReferenceWpfClassicTheme>false</ReferenceWpfClassicTheme>
<ReferenceWpfLunaTheme>false</ReferenceWpfLunaTheme>
<ReferenceWpfRoyaleTheme>false</ReferenceWpfRoyaleTheme>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Icon.ico" />
<Resource Include="Images\ring-code-guide-1-layer.png" />
<Resource Include="Images\ring-code-guide-2-layer.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.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 Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
<Reference Include="PresentationFramework.Aero" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.13" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<ItemGroup>
<Page Update="UserControls\UserInput.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Windows\DiscInformationWindow.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Windows\RingCodeGuideWindow.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
</ItemGroup>
<!-- Special handling for Aero2 on non-Framework .NET -->
<Target Name="SelectWpfThemeAssembly" AfterTargets="ResolveAssemblyReferences" Condition="'$(ReferenceWpfAeroTheme)' == 'true' OR '$(ReferenceWpfAero2Theme)' == 'true' OR '$(ReferenceWpfAeroLiteTheme)' == 'true' OR '$(ReferenceWpfClassicTheme)' == 'true' OR '$(ReferenceWpfLunaTheme)' == 'true' OR '$(ReferenceWpfRoyaleTheme)' == 'true'">
<ItemGroup>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Aero'">
<Aliases Condition="'$(WpfAeroThemeAliases)'!=''">$(WpfAeroThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Aero2'">
<Aliases Condition="'$(WpfAero2ThemeAliases)'!=''">$(WpfAero2ThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.AeroLite'">
<Aliases Condition="'$(WpfAeroLiteThemeAliases)'!=''">$(WpfAeroLiteThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Classic'">
<Aliases Condition="'$(WpfClassicThemeAliases)'!=''">$(WpfClassicThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Luna'">
<Aliases Condition="'$(WpfLunaThemeAliases)'!=''">$(WpfLunaThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Royale'">
<Aliases Condition="'$(WpfRoyaleThemeAliases)'!=''">$(WpfRoyaleThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<ReferencePath Remove="@(_WpfThemeAssemblies)" />
<ReferencePath Include="@(_WpfThemeAssemblies)" Condition="('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Aero' AND'$(ReferenceWpfAeroTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Aero2' AND '$(ReferenceWpfAero2Theme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.AeroLite' AND '$(ReferenceWpfAeroLiteTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Classic' AND '$(ReferenceWpfClassicTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Luna' AND '$(ReferenceWpfLunaTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Royale' AND '$(ReferenceWpfRoyaleTheme)'=='true')" />
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>
</Target>
<ItemGroup>
<Resource Include="Images\Icon.ico" />
<Resource Include="Images\ring-code-guide-1-layer.png" />
<Resource Include="Images\ring-code-guide-2-layer.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.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 Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
<Reference Include="PresentationFramework.Aero" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.2.3" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.5.2" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<ItemGroup>
<Page Update="UserControls\UserInput.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Windows\DiscInformationWindow.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Windows\RingCodeGuideWindow.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
</ItemGroup>
<!-- Special handling for Aero2 on non-Framework .NET -->
<Target Name="SelectWpfThemeAssembly" AfterTargets="ResolveAssemblyReferences" Condition="'$(ReferenceWpfAeroTheme)' == 'true' OR '$(ReferenceWpfAero2Theme)' == 'true' OR '$(ReferenceWpfAeroLiteTheme)' == 'true' OR '$(ReferenceWpfClassicTheme)' == 'true' OR '$(ReferenceWpfLunaTheme)' == 'true' OR '$(ReferenceWpfRoyaleTheme)' == 'true'">
<ItemGroup>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Aero'">
<Aliases Condition="'$(WpfAeroThemeAliases)'!=''">$(WpfAeroThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Aero2'">
<Aliases Condition="'$(WpfAero2ThemeAliases)'!=''">$(WpfAero2ThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.AeroLite'">
<Aliases Condition="'$(WpfAeroLiteThemeAliases)'!=''">$(WpfAeroLiteThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Classic'">
<Aliases Condition="'$(WpfClassicThemeAliases)'!=''">$(WpfClassicThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Luna'">
<Aliases Condition="'$(WpfLunaThemeAliases)'!=''">$(WpfLunaThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Royale'">
<Aliases Condition="'$(WpfRoyaleThemeAliases)'!=''">$(WpfRoyaleThemeAliases)</Aliases>
</_WpfThemeAssemblies>
<ReferencePath Remove="@(_WpfThemeAssemblies)" />
<ReferencePath Include="@(_WpfThemeAssemblies)" Condition="('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Aero' AND'$(ReferenceWpfAeroTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Aero2' AND '$(ReferenceWpfAero2Theme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.AeroLite' AND '$(ReferenceWpfAeroLiteTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Classic' AND '$(ReferenceWpfClassicTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Luna' AND '$(ReferenceWpfLunaTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Royale' AND '$(ReferenceWpfRoyaleTheme)'=='true')" />
</ItemGroup>
</Target>
</Project>

View File

@@ -205,196 +205,53 @@ namespace MPF.UI
public void Apply()
{
// Handle application-wide resources
Application.Current.Resources[SystemColors.ActiveBorderBrushKey] = this.ActiveBorderBrush;
Application.Current.Resources[SystemColors.ControlBrushKey] = this.ControlBrush;
Application.Current.Resources[SystemColors.ControlTextBrushKey] = this.ControlTextBrush;
Application.Current.Resources[SystemColors.GrayTextBrushKey] = this.GrayTextBrush;
Application.Current.Resources[SystemColors.WindowBrushKey] = this.WindowBrush;
Application.Current.Resources[SystemColors.WindowTextBrushKey] = this.WindowTextBrush;
Application.Current.Resources[SystemColors.ActiveBorderBrushKey] = ActiveBorderBrush;
Application.Current.Resources[SystemColors.ControlBrushKey] = ControlBrush;
Application.Current.Resources[SystemColors.ControlTextBrushKey] = ControlTextBrush;
Application.Current.Resources[SystemColors.GrayTextBrushKey] = GrayTextBrush;
Application.Current.Resources[SystemColors.WindowBrushKey] = WindowBrush;
Application.Current.Resources[SystemColors.WindowTextBrushKey] = WindowTextBrush;
// Handle Button-specific resources
Application.Current.Resources["Button.Disabled.Background"] = this.Button_Disabled_Background;
Application.Current.Resources["Button.MouseOver.Background"] = this.Button_MouseOver_Background;
Application.Current.Resources["Button.Pressed.Background"] = this.Button_Pressed_Background;
Application.Current.Resources["Button.Static.Background"] = this.Button_Static_Background;
Application.Current.Resources["Button.Disabled.Background"] = Button_Disabled_Background;
Application.Current.Resources["Button.MouseOver.Background"] = Button_MouseOver_Background;
Application.Current.Resources["Button.Pressed.Background"] = Button_Pressed_Background;
Application.Current.Resources["Button.Static.Background"] = Button_Static_Background;
// Handle ComboBox-specific resources
Application.Current.Resources["ComboBox.Disabled.Background"] = this.ComboBox_Disabled_Background;
Application.Current.Resources["ComboBox.Disabled.Editable.Background"] = this.ComboBox_Disabled_Editable_Background;
Application.Current.Resources["ComboBox.Disabled.Editable.Button.Background"] = this.ComboBox_Disabled_Editable_Button_Background;
Application.Current.Resources["ComboBox.MouseOver.Background"] = this.ComboBox_MouseOver_Background;
Application.Current.Resources["ComboBox.MouseOver.Editable.Background"] = this.ComboBox_MouseOver_Editable_Background;
Application.Current.Resources["ComboBox.MouseOver.Editable.Button.Background"] = this.ComboBox_MouseOver_Editable_Button_Background;
Application.Current.Resources["ComboBox.Pressed.Background"] = this.ComboBox_Pressed_Background;
Application.Current.Resources["ComboBox.Pressed.Editable.Background"] = this.ComboBox_Pressed_Editable_Background;
Application.Current.Resources["ComboBox.Pressed.Editable.Button.Background"] = this.ComboBox_Pressed_Editable_Button_Background;
Application.Current.Resources["ComboBox.Static.Background"] = this.ComboBox_Static_Background;
Application.Current.Resources["ComboBox.Static.Editable.Background"] = this.ComboBox_Static_Editable_Background;
Application.Current.Resources["ComboBox.Static.Editable.Button.Background"] = this.ComboBox_Static_Editable_Button_Background;
Application.Current.Resources["ComboBox.Disabled.Background"] = ComboBox_Disabled_Background;
Application.Current.Resources["ComboBox.Disabled.Editable.Background"] = ComboBox_Disabled_Editable_Background;
Application.Current.Resources["ComboBox.Disabled.Editable.Button.Background"] = ComboBox_Disabled_Editable_Button_Background;
Application.Current.Resources["ComboBox.MouseOver.Background"] = ComboBox_MouseOver_Background;
Application.Current.Resources["ComboBox.MouseOver.Editable.Background"] = ComboBox_MouseOver_Editable_Background;
Application.Current.Resources["ComboBox.MouseOver.Editable.Button.Background"] = ComboBox_MouseOver_Editable_Button_Background;
Application.Current.Resources["ComboBox.Pressed.Background"] = ComboBox_Pressed_Background;
Application.Current.Resources["ComboBox.Pressed.Editable.Background"] = ComboBox_Pressed_Editable_Background;
Application.Current.Resources["ComboBox.Pressed.Editable.Button.Background"] = ComboBox_Pressed_Editable_Button_Background;
Application.Current.Resources["ComboBox.Static.Background"] = ComboBox_Static_Background;
Application.Current.Resources["ComboBox.Static.Editable.Background"] = ComboBox_Static_Editable_Background;
Application.Current.Resources["ComboBox.Static.Editable.Button.Background"] = ComboBox_Static_Editable_Button_Background;
// Handle CustomMessageBox-specific resources
Application.Current.Resources["CustomMessageBox.Static.Background"] = this.CustomMessageBox_Static_Background;
Application.Current.Resources["CustomMessageBox.Static.Background"] = CustomMessageBox_Static_Background;
// Handle MenuItem-specific resources
Application.Current.Resources["MenuItem.SubMenu.Background"] = this.MenuItem_SubMenu_Background;
Application.Current.Resources["MenuItem.SubMenu.Border"] = this.MenuItem_SubMenu_Border;
Application.Current.Resources["MenuItem.SubMenu.Background"] = MenuItem_SubMenu_Background;
Application.Current.Resources["MenuItem.SubMenu.Border"] = MenuItem_SubMenu_Border;
// Handle ProgressBar-specific resources
Application.Current.Resources["ProgressBar.Background"] = this.ProgressBar_Background;
Application.Current.Resources["ProgressBar.Background"] = ProgressBar_Background;
// Handle ScrollViewer-specific resources
Application.Current.Resources["ScrollViewer.ScrollBar.Background"] = this.ScrollViewer_ScrollBar_Background;
Application.Current.Resources["ScrollViewer.ScrollBar.Background"] = ScrollViewer_ScrollBar_Background;
// Handle TabItem-specific resources
Application.Current.Resources["TabItem.Selected.Background"] = this.TabItem_Selected_Background;
Application.Current.Resources["TabItem.Static.Background"] = this.TabItem_Static_Background;
Application.Current.Resources["TabItem.Static.Border"] = this.TabItem_Static_Border;
Application.Current.Resources["TabItem.Selected.Background"] = TabItem_Selected_Background;
Application.Current.Resources["TabItem.Static.Background"] = TabItem_Static_Background;
Application.Current.Resources["TabItem.Static.Border"] = TabItem_Static_Border;
// Handle TextBox-specific resources
Application.Current.Resources["TextBox.Static.Background"] = this.TextBox_Static_Background;
}
}
/// <summary>
/// Default light-mode theme
/// </summary>
public class LightModeTheme : Theme
{
public LightModeTheme()
{
// Handle application-wide resources
this.ActiveBorderBrush = null;
this.ControlBrush = null;
this.ControlTextBrush = null;
this.GrayTextBrush = null;
this.WindowBrush = null;
this.WindowTextBrush = null;
// Handle Button-specific resources
this.Button_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF4));
this.Button_MouseOver_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xBE, 0xE6, 0xFD));
this.Button_Pressed_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xC4, 0xE5, 0xF6));
this.Button_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xDD, 0xDD, 0xDD));
// Handle ComboBox-specific resources
this.ComboBox_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
this.ComboBox_Disabled_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
this.ComboBox_Disabled_Editable_Button_Background = Brushes.Transparent;
this.ComboBox_MouseOver_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xEC, 0xF4, 0xFC),
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
new Point(0, 0),
new Point(0, 1));
this.ComboBox_MouseOver_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
this.ComboBox_MouseOver_Editable_Button_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xEB, 0xF4, 0xFC),
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
new Point(0, 0),
new Point(0, 1));
this.ComboBox_Pressed_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xDA, 0xEC, 0xFC),
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
new Point(0, 0),
new Point(0, 1));
this.ComboBox_Pressed_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
this.ComboBox_Pressed_Editable_Button_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xDA, 0xEB, 0xFC),
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
new Point(0, 0),
new Point(0, 1));
this.ComboBox_Static_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
new Point(0, 0),
new Point(0, 1));
this.ComboBox_Static_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
this.ComboBox_Static_Editable_Button_Background = Brushes.Transparent;
// Handle CustomMessageBox-specific resources
this.CustomMessageBox_Static_Background = null;
// Handle MenuItem-specific resources
this.MenuItem_SubMenu_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
// Handle ProgressBar-specific resources
this.ProgressBar_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xE6, 0xE6, 0xE6));
// Handle ScrollViewer-specific resources
this.ScrollViewer_ScrollBar_Background = Brushes.LightGray;
// Handle TabItem-specific resources
this.TabItem_Selected_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
this.TabItem_Static_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
new Point(0, 0),
new Point(0, 1));
this.TabItem_Static_Border = Brushes.DarkGray;
// Handle TextBox-specific resources
this.TextBox_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
}
}
/// <summary>
/// Default dark-mode theme
/// </summary>
public class DarkModeTheme : Theme
{
public DarkModeTheme()
{
// Setup needed brushes
var darkModeBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, 0x20, 0x20, 0x20) };
// Handle application-wide resources
this.ActiveBorderBrush = Brushes.Black;
this.ControlBrush = darkModeBrush;
this.ControlTextBrush = Brushes.White;
this.GrayTextBrush = Brushes.DarkGray;
this.WindowBrush = darkModeBrush;
this.WindowTextBrush = Brushes.White;
// Handle Button-specific resources
this.Button_Disabled_Background = darkModeBrush;
this.Button_MouseOver_Background = darkModeBrush;
this.Button_Pressed_Background = darkModeBrush;
this.Button_Static_Background = darkModeBrush;
// Handle ComboBox-specific resources
this.ComboBox_Disabled_Background = darkModeBrush;
this.ComboBox_Disabled_Editable_Background = darkModeBrush;
this.ComboBox_Disabled_Editable_Button_Background = darkModeBrush;
this.ComboBox_MouseOver_Background = darkModeBrush;
this.ComboBox_MouseOver_Editable_Background = darkModeBrush;
this.ComboBox_MouseOver_Editable_Button_Background = darkModeBrush;
this.ComboBox_Pressed_Background = darkModeBrush;
this.ComboBox_Pressed_Editable_Background = darkModeBrush;
this.ComboBox_Pressed_Editable_Button_Background = darkModeBrush;
this.ComboBox_Static_Background = darkModeBrush;
this.ComboBox_Static_Editable_Background = darkModeBrush;
this.ComboBox_Static_Editable_Button_Background = darkModeBrush;
// Handle CustomMessageBox-specific resources
this.CustomMessageBox_Static_Background = darkModeBrush;
// Handle MenuItem-specific resources
this.MenuItem_SubMenu_Background = darkModeBrush;
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
// Handle ProgressBar-specific resources
this.ProgressBar_Background = darkModeBrush;
// Handle ScrollViewer-specific resources
this.ScrollViewer_ScrollBar_Background = darkModeBrush;
// Handle TabItem-specific resources
this.TabItem_Selected_Background = darkModeBrush;
this.TabItem_Static_Background = darkModeBrush;
this.TabItem_Static_Border = Brushes.DarkGray;
// Handle TextBox-specific resources
this.TextBox_Static_Background = darkModeBrush;
Application.Current.Resources["TextBox.Static.Background"] = TextBox_Static_Background;
}
}
}

View File

@@ -0,0 +1,88 @@
using System;
using System.Windows.Media;
namespace MPF.UI.Themes
{
/// <summary>
/// Custom colour theme
/// </summary>
public sealed class CustomTheme : Theme
{
public CustomTheme(string? backgroundColor, string? foregroundColor)
{
// Convert hex colors to byte values
byte backgroundR;
byte backgroundG;
byte backgroundB;
byte foregroundR;
byte foregroundG;
byte foregroundB;
try
{
backgroundR = Convert.ToByte(backgroundColor!.Substring(0, 2), 16);
backgroundG = Convert.ToByte(backgroundColor!.Substring(2, 2), 16);
backgroundB = Convert.ToByte(backgroundColor!.Substring(4, 2), 16);
foregroundR = Convert.ToByte(foregroundColor!.Substring(0, 2), 16);
foregroundG = Convert.ToByte(foregroundColor!.Substring(2, 2), 16);
foregroundB = Convert.ToByte(foregroundColor!.Substring(4, 2), 16);
}
catch
{
return;
}
// Setup needed brushes
var backgroundBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, backgroundR, backgroundG, backgroundB) };
var foregroundBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, foregroundR, foregroundG, foregroundB) };
// Handle application-wide resources
ActiveBorderBrush = foregroundBrush;
ControlBrush = backgroundBrush;
ControlTextBrush = foregroundBrush;
GrayTextBrush = foregroundBrush;
WindowBrush = backgroundBrush;
WindowTextBrush = foregroundBrush;
// Handle Button-specific resources
Button_Disabled_Background = backgroundBrush;
Button_MouseOver_Background = backgroundBrush;
Button_Pressed_Background = backgroundBrush;
Button_Static_Background = backgroundBrush;
// Handle ComboBox-specific resources
ComboBox_Disabled_Background = backgroundBrush;
ComboBox_Disabled_Editable_Background = backgroundBrush;
ComboBox_Disabled_Editable_Button_Background = backgroundBrush;
ComboBox_MouseOver_Background = backgroundBrush;
ComboBox_MouseOver_Editable_Background = backgroundBrush;
ComboBox_MouseOver_Editable_Button_Background = backgroundBrush;
ComboBox_Pressed_Background = backgroundBrush;
ComboBox_Pressed_Editable_Background = backgroundBrush;
ComboBox_Pressed_Editable_Button_Background = backgroundBrush;
ComboBox_Static_Background = backgroundBrush;
ComboBox_Static_Editable_Background = backgroundBrush;
ComboBox_Static_Editable_Button_Background = backgroundBrush;
// Handle CustomMessageBox-specific resources
CustomMessageBox_Static_Background = backgroundBrush;
// Handle MenuItem-specific resources
MenuItem_SubMenu_Background = backgroundBrush;
MenuItem_SubMenu_Border = Brushes.DarkGray;
// Handle ProgressBar-specific resources
ProgressBar_Background = backgroundBrush;
// Handle ScrollViewer-specific resources
ScrollViewer_ScrollBar_Background = backgroundBrush;
// Handle TabItem-specific resources
TabItem_Selected_Background = backgroundBrush;
TabItem_Static_Background = backgroundBrush;
TabItem_Static_Border = Brushes.DarkGray;
// Handle TextBox-specific resources
TextBox_Static_Background = backgroundBrush;
}
}
}

View File

@@ -0,0 +1,65 @@
using System.Windows.Media;
namespace MPF.UI.Themes
{
/// <summary>
/// Default dark-mode theme
/// </summary>
public sealed class DarkModeTheme : Theme
{
public DarkModeTheme()
{
// Setup needed brushes
var darkModeBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, 0x20, 0x20, 0x20) };
// Handle application-wide resources
ActiveBorderBrush = Brushes.Black;
ControlBrush = darkModeBrush;
ControlTextBrush = Brushes.White;
GrayTextBrush = Brushes.DarkGray;
WindowBrush = darkModeBrush;
WindowTextBrush = Brushes.White;
// Handle Button-specific resources
Button_Disabled_Background = darkModeBrush;
Button_MouseOver_Background = darkModeBrush;
Button_Pressed_Background = darkModeBrush;
Button_Static_Background = darkModeBrush;
// Handle ComboBox-specific resources
ComboBox_Disabled_Background = darkModeBrush;
ComboBox_Disabled_Editable_Background = darkModeBrush;
ComboBox_Disabled_Editable_Button_Background = darkModeBrush;
ComboBox_MouseOver_Background = darkModeBrush;
ComboBox_MouseOver_Editable_Background = darkModeBrush;
ComboBox_MouseOver_Editable_Button_Background = darkModeBrush;
ComboBox_Pressed_Background = darkModeBrush;
ComboBox_Pressed_Editable_Background = darkModeBrush;
ComboBox_Pressed_Editable_Button_Background = darkModeBrush;
ComboBox_Static_Background = darkModeBrush;
ComboBox_Static_Editable_Background = darkModeBrush;
ComboBox_Static_Editable_Button_Background = darkModeBrush;
// Handle CustomMessageBox-specific resources
CustomMessageBox_Static_Background = darkModeBrush;
// Handle MenuItem-specific resources
MenuItem_SubMenu_Background = darkModeBrush;
MenuItem_SubMenu_Border = Brushes.DarkGray;
// Handle ProgressBar-specific resources
ProgressBar_Background = darkModeBrush;
// Handle ScrollViewer-specific resources
ScrollViewer_ScrollBar_Background = darkModeBrush;
// Handle TabItem-specific resources
TabItem_Selected_Background = darkModeBrush;
TabItem_Static_Background = darkModeBrush;
TabItem_Static_Border = Brushes.DarkGray;
// Handle TextBox-specific resources
TextBox_Static_Background = darkModeBrush;
}
}
}

View File

@@ -0,0 +1,87 @@
using System.Windows;
using System.Windows.Media;
namespace MPF.UI.Themes
{
/// <summary>
/// Default light-mode theme
/// </summary>
public sealed class LightModeTheme : Theme
{
public LightModeTheme()
{
// Handle application-wide resources
ActiveBorderBrush = null;
ControlBrush = null;
ControlTextBrush = null;
GrayTextBrush = null;
WindowBrush = null;
WindowTextBrush = null;
// Handle Button-specific resources
Button_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF4));
Button_MouseOver_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xBE, 0xE6, 0xFD));
Button_Pressed_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xC4, 0xE5, 0xF6));
Button_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xDD, 0xDD, 0xDD));
// Handle ComboBox-specific resources
ComboBox_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
ComboBox_Disabled_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
ComboBox_Disabled_Editable_Button_Background = Brushes.Transparent;
ComboBox_MouseOver_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xEC, 0xF4, 0xFC),
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
new Point(0, 0),
new Point(0, 1));
ComboBox_MouseOver_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
ComboBox_MouseOver_Editable_Button_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xEB, 0xF4, 0xFC),
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
new Point(0, 0),
new Point(0, 1));
ComboBox_Pressed_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xDA, 0xEC, 0xFC),
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
new Point(0, 0),
new Point(0, 1));
ComboBox_Pressed_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
ComboBox_Pressed_Editable_Button_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xDA, 0xEB, 0xFC),
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
new Point(0, 0),
new Point(0, 1));
ComboBox_Static_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
new Point(0, 0),
new Point(0, 1));
ComboBox_Static_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
ComboBox_Static_Editable_Button_Background = Brushes.Transparent;
// Handle CustomMessageBox-specific resources
CustomMessageBox_Static_Background = null;
// Handle MenuItem-specific resources
MenuItem_SubMenu_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
MenuItem_SubMenu_Border = Brushes.DarkGray;
// Handle ProgressBar-specific resources
ProgressBar_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xE6, 0xE6, 0xE6));
// Handle ScrollViewer-specific resources
ScrollViewer_ScrollBar_Background = Brushes.LightGray;
// Handle TabItem-specific resources
TabItem_Selected_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
TabItem_Static_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
new Point(0, 0),
new Point(0, 1));
TabItem_Static_Border = Brushes.DarkGray;
// Handle TextBox-specific resources
TextBox_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
}
}
}

View File

@@ -35,10 +35,10 @@ namespace MPF.UI.UserControls
#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");
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
@@ -60,24 +60,13 @@ namespace MPF.UI.UserControls
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
OutputViewer!.SizeChanged += OutputViewerSizeChanged;
Output!.TextChanged += OnTextChanged;
ClearButton!.Click += OnClearButton;
SaveButton!.Click += OnSaveButton;
// Update the internal state
#if NET35
_Output.Document = Document;
#else
Output.Document = Document;
#endif
}
#region Logging
@@ -107,8 +96,8 @@ namespace MPF.UI.UserControls
public LogLine(string text, LogLevel logLevel)
{
this.Text = text;
this.LogLevel = logLevel;
Text = text;
LogLevel = logLevel;
}
/// <summary>
@@ -117,7 +106,7 @@ namespace MPF.UI.UserControls
/// <returns>Brush representing the color</returns>
public Brush GetForegroundColor()
{
return this.LogLevel switch
return LogLevel switch
{
LogLevel.SECRET => Brushes.Blue,
LogLevel.ERROR => Brushes.Red,
@@ -132,7 +121,7 @@ namespace MPF.UI.UserControls
/// <returns>Run object based on internal values</returns>
public Run GenerateRun()
{
return new Run { Text = this.Text, Foreground = GetForegroundColor() };
return new Run { Text = Text, Foreground = GetForegroundColor() };
}
}
@@ -214,11 +203,7 @@ namespace MPF.UI.UserControls
/// <summary>
/// Scroll the current view to the bottom
/// </summary>
#if NET35
public void ScrollToBottom() => _OutputViewer!.ScrollToBottom();
#else
public void ScrollToBottom() => OutputViewer.ScrollToBottom();
#endif
public void ScrollToBottom() => OutputViewer!.ScrollToBottom();
#endregion

View File

@@ -1,5 +1,8 @@
using System;
using System.IO;
#if NET40
using System.Threading.Tasks;
#endif
using System.Windows;
using System.Windows.Controls;
using MPF.Frontend.ViewModels;
@@ -25,18 +28,18 @@ namespace MPF.UI.Windows
#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");
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");
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
@@ -80,38 +83,19 @@ namespace MPF.UI.Windows
public void AddEventHandlers()
{
// Main buttons
#if NET35
_CheckDumpButton!.Click += OnCheckDumpClick;
_CancelButton!.Click += OnCancelClick;
#else
CheckDumpButton.Click += OnCheckDumpClick;
CancelButton.Click += OnCancelClick;
#endif
CheckDumpButton!.Click += OnCheckDumpClick;
CancelButton!.Click += OnCancelClick;
// User Area Click
#if NET35
_InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
#else
InputPathBrowseButton.Click += InputPathBrowseButtonClick;
#endif
InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
// 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
SystemTypeComboBox!.SelectionChanged += SystemTypeComboBoxSelectionChanged;
MediaTypeComboBox!.SelectionChanged += MediaTypeComboBoxSelectionChanged;
DumpingProgramComboBox!.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
// User Area TextChanged
#if NET35
_InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
#else
InputPathTextBox.TextChanged += InputPathTextBoxTextChanged;
#endif
InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
}
/// <summary>
@@ -181,7 +165,7 @@ namespace MPF.UI.Windows
/// </summary>
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
/// <returns>Dialog open result</returns>
public (bool?, SubmissionInfo?) ShowDiscInformationWindow(SubmissionInfo? submissionInfo)
public bool? ShowDiscInformationWindow(ref SubmissionInfo? submissionInfo)
{
var discInformationWindow = new DiscInformationWindow(CheckDumpViewModel.Options, submissionInfo)
{
@@ -192,14 +176,14 @@ namespace MPF.UI.Windows
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
discInformationWindow.Closed += delegate { this.Activate(); };
discInformationWindow.Closed += delegate { 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!);
return result;
}
#endregion
@@ -209,17 +193,9 @@ namespace MPF.UI.Windows
/// <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);

View File

@@ -24,34 +24,34 @@ namespace MPF.UI.Windows
#region Settings
private Button? _InputPathBrowseButton => ItemHelper.FindChild<Button>(this, "InputPathBrowseButton");
private TextBox? _InputPathTextBox => ItemHelper.FindChild<TextBox>(this, "InputPathTextBox");
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");
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");
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");
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
@@ -95,47 +95,24 @@ namespace MPF.UI.Windows
public void AddEventHandlers()
{
// Main buttons
#if NET35
_CreateIRDButton!.Click += OnCreateIRDClick;
_CancelButton!.Click += OnCancelClick;
#else
CreateIRDButton.Click += OnCreateIRDClick;
CancelButton.Click += OnCancelClick;
#endif
CreateIRDButton!.Click += OnCreateIRDClick;
CancelButton!.Click += OnCancelClick;
// 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
InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
LogPathBrowseButton!.Click += LogPathBrowseButtonClick;
KeyPathBrowseButton!.Click += KeyPathBrowseButtonClick;
PICPathBrowseButton!.Click += PICPathBrowseButtonClick;
// 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
InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
LogPathTextBox!.TextChanged += LogPathTextBoxTextChanged;
KeyPathTextBox!.TextChanged += KeyPathTextBoxTextChanged;
KeyTextBox!.TextChanged += KeyTextBoxTextChanged;
DiscIDTextBox!.TextChanged += DiscIDTextBoxTextChanged;
PICPathTextBox!.TextChanged += PICPathTextBoxTextChanged;
PICTextBox!.TextChanged += PICTextBoxTextChanged;
LayerbreakTextBox!.TextChanged += LayerbreakTextBoxTextChanged;
}
/// <summary>
@@ -330,15 +307,10 @@ namespace MPF.UI.Windows
/// </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
if (KeyExpander != null) KeyExpander.IsExpanded = false;
if (DiscIDExpander != null) DiscIDExpander.IsExpanded = false;
if (PICExpander != null) PICExpander.IsExpanded = false;
string tempStatus = CreateIRDViewModel.CreateIRDStatus;
bool[] enabledFields = CreateIRDViewModel.DisableUIFields();
CreateIRDViewModel.CreateIRDStatus = "Creating IRD... Please Wait";

View File

@@ -237,7 +237,7 @@
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"
<controls:UserInput x:Name="BethesdaIDTextBox" Label="Bethesda 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}"/>

View File

@@ -1,5 +1,4 @@
using System.Linq;
using System.Windows;
using System.Windows;
using System.Windows.Controls;
using MPF.Frontend;
using MPF.Frontend.ViewModels;
@@ -19,104 +18,104 @@ namespace MPF.UI.Windows
#region Common Info
private Grid? _LanguageSelectionGrid => ItemHelper.FindChild<Grid>(this, "LanguageSelectionGrid");
private Grid? LanguageSelectionGrid => ItemHelper.FindChild<Grid>(this, "LanguageSelectionGrid");
#endregion
#region Additional Info
private UserInput? _CommentsTextBox => ItemHelper.FindChild<UserInput>(this, "CommentsTextBox");
private UserInput? _DiscIDTextBox => ItemHelper.FindChild<UserInput>(this, "DiscIDTextBox");
private UserInput? _DiscKeyTextBox => ItemHelper.FindChild<UserInput>(this, "DiscKeyTextBox");
private UserInput? CommentsTextBox => ItemHelper.FindChild<UserInput>(this, "CommentsTextBox");
private UserInput? DiscIDTextBox => ItemHelper.FindChild<UserInput>(this, "DiscIDTextBox");
private UserInput? DiscKeyTextBox => ItemHelper.FindChild<UserInput>(this, "DiscKeyTextBox");
#endregion
#region Contents
private UserInput? _ExtrasTextBox => ItemHelper.FindChild<UserInput>(this, "ExtrasTextBox");
private UserInput? _GameFootageTextBox => ItemHelper.FindChild<UserInput>(this, "GameFootageTextBox");
private UserInput? _GamesTextBox => ItemHelper.FindChild<UserInput>(this, "GamesTextBox");
private UserInput? _GeneralContent => ItemHelper.FindChild<UserInput>(this, "GeneralContent");
private UserInput? _NetYarozeGamesTextBox => ItemHelper.FindChild<UserInput>(this, "NetYarozeGamesTextBox");
private UserInput? _PatchesTextBox => ItemHelper.FindChild<UserInput>(this, "PatchesTextBox");
private UserInput? _PlayableDemosTextBox => ItemHelper.FindChild<UserInput>(this, "PlayableDemosTextBox");
private UserInput? _RollingDemosTextBox => ItemHelper.FindChild<UserInput>(this, "RollingDemosTextBox");
private UserInput? _SavegamesTextBox => ItemHelper.FindChild<UserInput>(this, "SavegamesTextBox");
private UserInput? _TechDemosTextBox => ItemHelper.FindChild<UserInput>(this, "TechDemosTextBox");
private UserInput? _VideosTextBox => ItemHelper.FindChild<UserInput>(this, "VideosTextBox");
private UserInput? ExtrasTextBox => ItemHelper.FindChild<UserInput>(this, "ExtrasTextBox");
private UserInput? GameFootageTextBox => ItemHelper.FindChild<UserInput>(this, "GameFootageTextBox");
private UserInput? GamesTextBox => ItemHelper.FindChild<UserInput>(this, "GamesTextBox");
private UserInput? GeneralContent => ItemHelper.FindChild<UserInput>(this, "GeneralContent");
private UserInput? NetYarozeGamesTextBox => ItemHelper.FindChild<UserInput>(this, "NetYarozeGamesTextBox");
private UserInput? PatchesTextBox => ItemHelper.FindChild<UserInput>(this, "PatchesTextBox");
private UserInput? PlayableDemosTextBox => ItemHelper.FindChild<UserInput>(this, "PlayableDemosTextBox");
private UserInput? RollingDemosTextBox => ItemHelper.FindChild<UserInput>(this, "RollingDemosTextBox");
private UserInput? SavegamesTextBox => ItemHelper.FindChild<UserInput>(this, "SavegamesTextBox");
private UserInput? TechDemosTextBox => ItemHelper.FindChild<UserInput>(this, "TechDemosTextBox");
private UserInput? VideosTextBox => ItemHelper.FindChild<UserInput>(this, "VideosTextBox");
#endregion
#region Ringcodes
private GroupBox? _L0Info => ItemHelper.FindChild<GroupBox>(this, "L0Info");
private UserInput? _L0MasteringRing => ItemHelper.FindChild<UserInput>(this, "L0MasteringRing");
private UserInput? _L0MasteringSID => ItemHelper.FindChild<UserInput>(this, "L0MasteringSID");
private UserInput? _L0Toolstamp => ItemHelper.FindChild<UserInput>(this, "L0Toolstamp");
private UserInput? _L0MouldSID => ItemHelper.FindChild<UserInput>(this, "L0MouldSID");
private UserInput? _L0AdditionalMould => ItemHelper.FindChild<UserInput>(this, "L0AdditionalMould");
private GroupBox? _L1Info => ItemHelper.FindChild<GroupBox>(this, "L1Info");
private UserInput? _L1MasteringRing => ItemHelper.FindChild<UserInput>(this, "L1MasteringRing");
private UserInput? _L1MasteringSID => ItemHelper.FindChild<UserInput>(this, "L1MasteringSID");
private UserInput? _L1Toolstamp => ItemHelper.FindChild<UserInput>(this, "L1Toolstamp");
private UserInput? _L1MouldSID => ItemHelper.FindChild<UserInput>(this, "L1MouldSID");
private UserInput? _L1AdditionalMould => ItemHelper.FindChild<UserInput>(this, "L1AdditionalMould");
private GroupBox? _L2Info => ItemHelper.FindChild<GroupBox>(this, "L2Info");
private UserInput? _L2MasteringRing => ItemHelper.FindChild<UserInput>(this, "L2MasteringRing");
private UserInput? _L2MasteringSID => ItemHelper.FindChild<UserInput>(this, "L2MasteringSID");
private UserInput? _L2Toolstamp => ItemHelper.FindChild<UserInput>(this, "L2Toolstamp");
private GroupBox? _L3Info => ItemHelper.FindChild<GroupBox>(this, "L3Info");
private UserInput? _L3MasteringRing => ItemHelper.FindChild<UserInput>(this, "L3MasteringRing");
private UserInput? _L3MasteringSID => ItemHelper.FindChild<UserInput>(this, "L3MasteringSID");
private UserInput? _L3Toolstamp => ItemHelper.FindChild<UserInput>(this, "L3Toolstamp");
private GroupBox? L0Info => ItemHelper.FindChild<GroupBox>(this, "L0Info");
private UserInput? L0MasteringRing => ItemHelper.FindChild<UserInput>(this, "L0MasteringRing");
private UserInput? L0MasteringSID => ItemHelper.FindChild<UserInput>(this, "L0MasteringSID");
private UserInput? L0Toolstamp => ItemHelper.FindChild<UserInput>(this, "L0Toolstamp");
private UserInput? L0MouldSID => ItemHelper.FindChild<UserInput>(this, "L0MouldSID");
private UserInput? L0AdditionalMould => ItemHelper.FindChild<UserInput>(this, "L0AdditionalMould");
private GroupBox? L1Info => ItemHelper.FindChild<GroupBox>(this, "L1Info");
private UserInput? L1MasteringRing => ItemHelper.FindChild<UserInput>(this, "L1MasteringRing");
private UserInput? L1MasteringSID => ItemHelper.FindChild<UserInput>(this, "L1MasteringSID");
private UserInput? L1Toolstamp => ItemHelper.FindChild<UserInput>(this, "L1Toolstamp");
private UserInput? L1MouldSID => ItemHelper.FindChild<UserInput>(this, "L1MouldSID");
private UserInput? L1AdditionalMould => ItemHelper.FindChild<UserInput>(this, "L1AdditionalMould");
private GroupBox? L2Info => ItemHelper.FindChild<GroupBox>(this, "L2Info");
private UserInput? L2MasteringRing => ItemHelper.FindChild<UserInput>(this, "L2MasteringRing");
private UserInput? L2MasteringSID => ItemHelper.FindChild<UserInput>(this, "L2MasteringSID");
private UserInput? L2Toolstamp => ItemHelper.FindChild<UserInput>(this, "L2Toolstamp");
private GroupBox? L3Info => ItemHelper.FindChild<GroupBox>(this, "L3Info");
private UserInput? L3MasteringRing => ItemHelper.FindChild<UserInput>(this, "L3MasteringRing");
private UserInput? L3MasteringSID => ItemHelper.FindChild<UserInput>(this, "L3MasteringSID");
private UserInput? L3Toolstamp => ItemHelper.FindChild<UserInput>(this, "L3Toolstamp");
#endregion
#region Read-Only Info
private UserInput? _FullyMatchedID => ItemHelper.FindChild<UserInput>(this, "FullyMatchedID");
private UserInput? _PartiallyMatchedIDs => ItemHelper.FindChild<UserInput>(this, "PartiallyMatchedIDs");
private UserInput? _HashData => ItemHelper.FindChild<UserInput>(this, "HashData");
private UserInput? _HashDataSize => ItemHelper.FindChild<UserInput>(this, "HashDataSize");
private UserInput? _HashDataCRC => ItemHelper.FindChild<UserInput>(this, "HashDataCRC");
private UserInput? _HashDataMD5 => ItemHelper.FindChild<UserInput>(this, "HashDataMD5");
private UserInput? _HashDataSHA1 => ItemHelper.FindChild<UserInput>(this, "HashDataSHA1");
private UserInput? _HashDataLayerbreak1 => ItemHelper.FindChild<UserInput>(this, "HashDataLayerbreak1");
private UserInput? _HashDataLayerbreak2 => ItemHelper.FindChild<UserInput>(this, "HashDataLayerbreak2");
private UserInput? _HashDataLayerbreak3 => ItemHelper.FindChild<UserInput>(this, "HashDataLayerbreak3");
private UserInput? _AntiModchip => ItemHelper.FindChild<UserInput>(this, "AntiModchip");
private UserInput? _DiscOffset => ItemHelper.FindChild<UserInput>(this, "DiscOffset");
private UserInput? _DMIHash => ItemHelper.FindChild<UserInput>(this, "DMIHash");
private UserInput? _EDC => ItemHelper.FindChild<UserInput>(this, "EDC");
private UserInput? _ErrorsCount => ItemHelper.FindChild<UserInput>(this, "ErrorsCount");
private UserInput? _EXEDateBuildDate => ItemHelper.FindChild<UserInput>(this, "EXEDateBuildDate");
private UserInput? _Filename => ItemHelper.FindChild<UserInput>(this, "Filename");
private UserInput? _Header => ItemHelper.FindChild<UserInput>(this, "Header");
private UserInput? _InternalName => ItemHelper.FindChild<UserInput>(this, "InternalName");
private UserInput? _InternalSerialName => ItemHelper.FindChild<UserInput>(this, "InternalSerialName");
private UserInput? _Multisession => ItemHelper.FindChild<UserInput>(this, "Multisession");
private UserInput? _LibCrypt => ItemHelper.FindChild<UserInput>(this, "LibCrypt");
private UserInput? _LibCryptData => ItemHelper.FindChild<UserInput>(this, "LibCryptData");
private UserInput? _PFIHash => ItemHelper.FindChild<UserInput>(this, "PFIHash");
private UserInput? _PIC => ItemHelper.FindChild<UserInput>(this, "PIC");
private UserInput? _PVD => ItemHelper.FindChild<UserInput>(this, "PVD");
private UserInput? _RingNonZeroDataStart => ItemHelper.FindChild<UserInput>(this, "RingNonZeroDataStart");
private UserInput? _SecuROMData => ItemHelper.FindChild<UserInput>(this, "SecuROMData");
private UserInput? _SSHash => ItemHelper.FindChild<UserInput>(this, "SSHash");
private UserInput? _SecuritySectorRanges => ItemHelper.FindChild<UserInput>(this, "SecuritySectorRanges");
private UserInput? _SSVersion => ItemHelper.FindChild<UserInput>(this, "SSVersion");
private UserInput? _UniversalHash => ItemHelper.FindChild<UserInput>(this, "UniversalHash");
private UserInput? _VolumeLabel => ItemHelper.FindChild<UserInput>(this, "VolumeLabel");
private UserInput? _XeMID => ItemHelper.FindChild<UserInput>(this, "XeMID");
private UserInput? _XMID => ItemHelper.FindChild<UserInput>(this, "XMID");
private UserInput? FullyMatchedID => ItemHelper.FindChild<UserInput>(this, "FullyMatchedID");
private UserInput? PartiallyMatchedIDs => ItemHelper.FindChild<UserInput>(this, "PartiallyMatchedIDs");
private UserInput? HashData => ItemHelper.FindChild<UserInput>(this, "HashData");
private UserInput? HashDataSize => ItemHelper.FindChild<UserInput>(this, "HashDataSize");
private UserInput? HashDataCRC => ItemHelper.FindChild<UserInput>(this, "HashDataCRC");
private UserInput? HashDataMD5 => ItemHelper.FindChild<UserInput>(this, "HashDataMD5");
private UserInput? HashDataSHA1 => ItemHelper.FindChild<UserInput>(this, "HashDataSHA1");
private UserInput? HashDataLayerbreak1 => ItemHelper.FindChild<UserInput>(this, "HashDataLayerbreak1");
private UserInput? HashDataLayerbreak2 => ItemHelper.FindChild<UserInput>(this, "HashDataLayerbreak2");
private UserInput? HashDataLayerbreak3 => ItemHelper.FindChild<UserInput>(this, "HashDataLayerbreak3");
private UserInput? AntiModchip => ItemHelper.FindChild<UserInput>(this, "AntiModchip");
private UserInput? DiscOffset => ItemHelper.FindChild<UserInput>(this, "DiscOffset");
private UserInput? DMIHash => ItemHelper.FindChild<UserInput>(this, "DMIHash");
private UserInput? EDC => ItemHelper.FindChild<UserInput>(this, "EDC");
private UserInput? ErrorsCount => ItemHelper.FindChild<UserInput>(this, "ErrorsCount");
private UserInput? EXEDateBuildDate => ItemHelper.FindChild<UserInput>(this, "EXEDateBuildDate");
private UserInput? Filename => ItemHelper.FindChild<UserInput>(this, "Filename");
private UserInput? Header => ItemHelper.FindChild<UserInput>(this, "Header");
private UserInput? InternalName => ItemHelper.FindChild<UserInput>(this, "InternalName");
private UserInput? InternalSerialName => ItemHelper.FindChild<UserInput>(this, "InternalSerialName");
private UserInput? Multisession => ItemHelper.FindChild<UserInput>(this, "Multisession");
private UserInput? LibCrypt => ItemHelper.FindChild<UserInput>(this, "LibCrypt");
private UserInput? LibCryptData => ItemHelper.FindChild<UserInput>(this, "LibCryptData");
private UserInput? PFIHash => ItemHelper.FindChild<UserInput>(this, "PFIHash");
private UserInput? PIC => ItemHelper.FindChild<UserInput>(this, "PIC");
private UserInput? PVD => ItemHelper.FindChild<UserInput>(this, "PVD");
private UserInput? RingNonZeroDataStart => ItemHelper.FindChild<UserInput>(this, "RingNonZeroDataStart");
private UserInput? SecuROMData => ItemHelper.FindChild<UserInput>(this, "SecuROMData");
private UserInput? SSHash => ItemHelper.FindChild<UserInput>(this, "SSHash");
private UserInput? SecuritySectorRanges => ItemHelper.FindChild<UserInput>(this, "SecuritySectorRanges");
private UserInput? SSVersion => ItemHelper.FindChild<UserInput>(this, "SSVersion");
private UserInput? UniversalHash => ItemHelper.FindChild<UserInput>(this, "UniversalHash");
private UserInput? VolumeLabel => ItemHelper.FindChild<UserInput>(this, "VolumeLabel");
private UserInput? XeMID => ItemHelper.FindChild<UserInput>(this, "XeMID");
private UserInput? XMID => ItemHelper.FindChild<UserInput>(this, "XMID");
#endregion
#region Accept / Cancel
private Button? _AcceptButton => ItemHelper.FindChild<Button>(this, "AcceptButton");
private Button? _CancelButton => ItemHelper.FindChild<Button>(this, "CancelButton");
private Button? _RingCodeGuideButton => ItemHelper.FindChild<Button>(this, "RingCodeGuideButton");
private Button? AcceptButton => ItemHelper.FindChild<Button>(this, "AcceptButton");
private Button? CancelButton => ItemHelper.FindChild<Button>(this, "CancelButton");
private Button? RingCodeGuideButton => ItemHelper.FindChild<Button>(this, "RingCodeGuideButton");
#endregion
@@ -156,15 +155,9 @@ namespace MPF.UI.Windows
}
// Add handlers
#if NET35
_AcceptButton!.Click += OnAcceptClick;
_CancelButton!.Click += OnCancelClick;
_RingCodeGuideButton!.Click += OnRingCodeGuideClick;
#else
AcceptButton.Click += OnAcceptClick;
CancelButton.Click += OnCancelClick;
RingCodeGuideButton.Click += OnRingCodeGuideClick;
#endif
AcceptButton!.Click += OnAcceptClick;
CancelButton!.Click += OnCancelClick;
RingCodeGuideButton!.Click += OnRingCodeGuideClick;
// Update UI with new values
ManipulateFields(options, submissionInfo);
@@ -198,90 +191,44 @@ namespace MPF.UI.Windows
private void EnableTabsInInputFields()
{
// Additional Information
#if NET35
_CommentsTextBox!.Tab = true;
#else
CommentsTextBox.Tab = true;
#endif
CommentsTextBox!.Tab = true;
// Contents
#if NET35
_GeneralContent!.Tab = true;
_GamesTextBox!.Tab = true;
_NetYarozeGamesTextBox!.Tab = true;
_PlayableDemosTextBox!.Tab = true;
_RollingDemosTextBox!.Tab = true;
_TechDemosTextBox!.Tab = true;
_GameFootageTextBox!.Tab = true;
_VideosTextBox!.Tab = true;
_PatchesTextBox!.Tab = true;
_SavegamesTextBox!.Tab = true;
_ExtrasTextBox!.Tab = true;
#else
GeneralContent.Tab = true;
GamesTextBox.Tab = true;
NetYarozeGamesTextBox.Tab = true;
PlayableDemosTextBox.Tab = true;
RollingDemosTextBox.Tab = true;
TechDemosTextBox.Tab = true;
GameFootageTextBox.Tab = true;
VideosTextBox.Tab = true;
PatchesTextBox.Tab = true;
SavegamesTextBox.Tab = true;
ExtrasTextBox.Tab = true;
#endif
GeneralContent!.Tab = true;
GamesTextBox!.Tab = true;
NetYarozeGamesTextBox!.Tab = true;
PlayableDemosTextBox!.Tab = true;
RollingDemosTextBox!.Tab = true;
TechDemosTextBox!.Tab = true;
GameFootageTextBox!.Tab = true;
VideosTextBox!.Tab = true;
PatchesTextBox!.Tab = true;
SavegamesTextBox!.Tab = true;
ExtrasTextBox!.Tab = true;
// L0
#if NET35
_L0MasteringRing!.Tab = true;
_L0MasteringSID!.Tab = true;
_L0Toolstamp!.Tab = true;
_L0MouldSID!.Tab = true;
_L0AdditionalMould!.Tab = true;
#else
L0MasteringRing.Tab = true;
L0MasteringSID.Tab = true;
L0Toolstamp.Tab = true;
L0MouldSID.Tab = true;
L0AdditionalMould.Tab = true;
#endif
L0MasteringRing!.Tab = true;
L0MasteringSID!.Tab = true;
L0Toolstamp!.Tab = true;
L0MouldSID!.Tab = true;
L0AdditionalMould!.Tab = true;
// L1
#if NET35
_L1MasteringRing!.Tab = true;
_L1MasteringSID!.Tab = true;
_L1Toolstamp!.Tab = true;
_L1MouldSID!.Tab = true;
_L1AdditionalMould!.Tab = true;
#else
L1MasteringRing.Tab = true;
L1MasteringSID.Tab = true;
L1Toolstamp.Tab = true;
L1MouldSID.Tab = true;
L1AdditionalMould.Tab = true;
#endif
L1MasteringRing!.Tab = true;
L1MasteringSID!.Tab = true;
L1Toolstamp!.Tab = true;
L1MouldSID!.Tab = true;
L1AdditionalMould!.Tab = true;
// L2
#if NET35
_L2MasteringRing!.Tab = true;
_L2MasteringSID!.Tab = true;
_L2Toolstamp!.Tab = true;
#else
L2MasteringRing.Tab = true;
L2MasteringSID.Tab = true;
L2Toolstamp.Tab = true;
#endif
L2MasteringRing!.Tab = true;
L2MasteringSID!.Tab = true;
L2Toolstamp!.Tab = true;
// L3
#if NET35
_L3MasteringRing!.Tab = true;
_L3MasteringSID!.Tab = true;
_L3Toolstamp!.Tab = true;
#else
L3MasteringRing.Tab = true;
L3MasteringSID.Tab = true;
L3Toolstamp.Tab = true;
#endif
L3MasteringRing!.Tab = true;
L3MasteringSID!.Tab = true;
L3Toolstamp!.Tab = true;
}
/// <summary>
@@ -295,89 +242,15 @@ namespace MPF.UI.Windows
if (submissionInfo == null)
return;
#if NET35
if (submissionInfo.FullyMatchedID == null)
_FullyMatchedID!.Visibility = Visibility.Collapsed;
FullyMatchedID!.Visibility = Visibility.Collapsed;
if (submissionInfo.PartiallyMatchedIDs == null)
_PartiallyMatchedIDs!.Visibility = Visibility.Collapsed;
PartiallyMatchedIDs!.Visibility = Visibility.Collapsed;
else
_PartiallyMatchedIDs!.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs.Select(i => i.ToString()).ToArray());
if (string.IsNullOrEmpty(submissionInfo.TracksAndWriteOffsets?.ClrMameProData))
_HashData!.Visibility = Visibility.Collapsed;
if (submissionInfo.SizeAndChecksums?.Size == null || submissionInfo.SizeAndChecksums.Size == 0)
_HashDataSize!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.SizeAndChecksums?.CRC32))
_HashDataCRC!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.SizeAndChecksums?.MD5))
_HashDataMD5!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.SizeAndChecksums?.SHA1))
_HashDataSHA1!.Visibility = Visibility.Collapsed;
if (submissionInfo.SizeAndChecksums?.Layerbreak == null || submissionInfo.SizeAndChecksums.Layerbreak == 0)
_HashDataLayerbreak1!.Visibility = Visibility.Collapsed;
if (submissionInfo.SizeAndChecksums?.Layerbreak2 == null || submissionInfo.SizeAndChecksums.Layerbreak2 == 0)
_HashDataLayerbreak2!.Visibility = Visibility.Collapsed;
if (submissionInfo.SizeAndChecksums?.Layerbreak3 == null || submissionInfo.SizeAndChecksums.Layerbreak3 == 0)
_HashDataLayerbreak3!.Visibility = Visibility.Collapsed;
if (submissionInfo.CopyProtection?.AntiModchip == null)
_AntiModchip!.Visibility = Visibility.Collapsed;
if (submissionInfo.TracksAndWriteOffsets?.OtherWriteOffsets == null)
_DiscOffset!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.Keys?.Contains(SiteCode.DMIHash) != true)
_DMIHash!.Visibility = Visibility.Collapsed;
if (submissionInfo.EDC?.EDC == null)
_EDC!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.ErrorsCount))
_ErrorsCount!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
_EXEDateBuildDate!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
_Filename!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.Header))
_Header!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
_InternalName!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalSerialName) != true)
_InternalSerialName!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Multisession) != true)
_Multisession!.Visibility = Visibility.Collapsed;
if (submissionInfo.CopyProtection?.LibCrypt == null)
_LibCrypt!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.LibCryptData))
_LibCryptData!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
_PFIHash!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.PIC))
_PIC!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.PVD))
_PVD!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
_RingNonZeroDataStart!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.SecuROMData))
_SecuROMData!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
_SSHash!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.SecuritySectorRanges))
_SecuritySectorRanges!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
_SSVersion!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) != true)
_UniversalHash!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.VolumeLabel) != true)
_VolumeLabel!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XeMID) != true)
_XeMID!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
_XMID!.Visibility = Visibility.Collapsed;
#else
if (submissionInfo.FullyMatchedID == null)
FullyMatchedID.Visibility = Visibility.Collapsed;
if (submissionInfo.PartiallyMatchedIDs == null)
PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
else
PartiallyMatchedIDs.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs.Select(i => i.ToString()).ToArray());
PartiallyMatchedIDs!.Text = string.Join(", ", [.. submissionInfo.PartiallyMatchedIDs.ConvertAll(i => i.ToString())]);
if (string.IsNullOrEmpty(submissionInfo.TracksAndWriteOffsets?.ClrMameProData))
HashData!.Visibility = Visibility.Collapsed;
if (submissionInfo.SizeAndChecksums?.Size == null)
if (submissionInfo.SizeAndChecksums?.Size == null || submissionInfo.SizeAndChecksums.Size == 0)
HashDataSize!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.SizeAndChecksums?.CRC32))
HashDataCRC!.Visibility = Visibility.Collapsed;
@@ -392,56 +265,55 @@ namespace MPF.UI.Windows
if (submissionInfo.SizeAndChecksums?.Layerbreak3 == null || submissionInfo.SizeAndChecksums.Layerbreak3 == 0)
HashDataLayerbreak3!.Visibility = Visibility.Collapsed;
if (submissionInfo.CopyProtection?.AntiModchip == null)
AntiModchip.Visibility = Visibility.Collapsed;
AntiModchip!.Visibility = Visibility.Collapsed;
if (submissionInfo.TracksAndWriteOffsets?.OtherWriteOffsets == null)
DiscOffset.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.Keys?.Contains(SiteCode.DMIHash) != true)
DMIHash.Visibility = Visibility.Collapsed;
DiscOffset!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.DMIHash) != true)
DMIHash!.Visibility = Visibility.Collapsed;
if (submissionInfo.EDC?.EDC == null)
EDC.Visibility = Visibility.Collapsed;
EDC!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.ErrorsCount))
ErrorsCount.Visibility = Visibility.Collapsed;
ErrorsCount!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
EXEDateBuildDate.Visibility = Visibility.Collapsed;
EXEDateBuildDate!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
Filename.Visibility = Visibility.Collapsed;
Filename!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.Header))
Header.Visibility = Visibility.Collapsed;
Header!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
InternalName.Visibility = Visibility.Collapsed;
InternalName!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalSerialName) != true)
InternalSerialName.Visibility = Visibility.Collapsed;
InternalSerialName!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Multisession) != true)
Multisession.Visibility = Visibility.Collapsed;
Multisession!.Visibility = Visibility.Collapsed;
if (submissionInfo.CopyProtection?.LibCrypt == null)
LibCrypt.Visibility = Visibility.Collapsed;
LibCrypt!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.LibCryptData))
LibCryptData.Visibility = Visibility.Collapsed;
LibCryptData!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
PFIHash.Visibility = Visibility.Collapsed;
PFIHash!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.PIC))
PIC.Visibility = Visibility.Collapsed;
PIC!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.PVD))
PVD.Visibility = Visibility.Collapsed;
PVD!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
RingNonZeroDataStart.Visibility = Visibility.Collapsed;
RingNonZeroDataStart!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.SecuROMData))
SecuROMData.Visibility = Visibility.Collapsed;
SecuROMData!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
SSHash.Visibility = Visibility.Collapsed;
SSHash!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.SecuritySectorRanges))
SecuritySectorRanges.Visibility = Visibility.Collapsed;
SecuritySectorRanges!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
SSVersion.Visibility = Visibility.Collapsed;
SSVersion!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) != true)
UniversalHash.Visibility = Visibility.Collapsed;
UniversalHash!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.VolumeLabel) != true)
VolumeLabel.Visibility = Visibility.Collapsed;
VolumeLabel!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XeMID) != true)
XeMID.Visibility = Visibility.Collapsed;
XeMID!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
XMID.Visibility = Visibility.Collapsed;
#endif
XMID!.Visibility = Visibility.Collapsed;
}
/// <summary>
@@ -458,37 +330,19 @@ namespace MPF.UI.Windows
{
case DiscType.CD:
case DiscType.GDROM:
#if NET35
_L0Info!.Header = "Data Side";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Mould SID";
_L0AdditionalMould!.Label = "Additional Mould";
#else
L0Info.Header = "Data Side";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Mould SID";
L0AdditionalMould.Label = "Additional Mould";
#endif
L0Info!.Header = "Data Side";
L0MasteringRing!.Label = "Mastering Ring";
L0MasteringSID!.Label = "Mastering SID";
L0Toolstamp!.Label = "Toolstamp/Mastering Code";
L0MouldSID!.Label = "Mould SID";
L0AdditionalMould!.Label = "Additional Mould";
#if NET35
_L1Info!.Header = "Label Side";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Mastering SID";
_L1MouldSID!.Label = "Mould SID";
_L1AdditionalMould!.Label = "Additional Mould";
#else
L1Info.Header = "Label Side";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Mastering SID";
L1MouldSID.Label = "Mould SID";
L1AdditionalMould.Label = "Additional Mould";
#endif
L1Info!.Header = "Label Side";
L1MasteringRing!.Label = "Mastering Ring";
L1MasteringSID!.Label = "Mastering SID";
L1Toolstamp!.Label = "Mastering SID";
L1MouldSID!.Label = "Mould SID";
L1AdditionalMould!.Label = "Additional Mould";
break;
case DiscType.DVD5:
@@ -510,212 +364,103 @@ namespace MPF.UI.Windows
// Quad-layer discs
if (submissionInfo?.SizeAndChecksums?.Layerbreak3 != default(long))
{
#if NET35
_L2Info!.Visibility = Visibility.Visible;
_L3Info!.Visibility = Visibility.Visible;
#else
L2Info.Visibility = Visibility.Visible;
L3Info.Visibility = Visibility.Visible;
#endif
L2Info!.Visibility = Visibility.Visible;
L3Info!.Visibility = Visibility.Visible;
#if NET35
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Data Side Mould SID";
_L0AdditionalMould!.Label = "Data Side Additional Mould";
#else
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
#endif
L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing!.Label = "Mastering Ring";
L0MasteringSID!.Label = "Mastering SID";
L0Toolstamp!.Label = "Toolstamp/Mastering Code";
L0MouldSID!.Label = "Data Side Mould SID";
L0AdditionalMould!.Label = "Data Side Additional Mould";
#if NET35
_L1Info!.Header = "Layer 1";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Label Side Mould SID";
_L1AdditionalMould!.Label = "Label Side Additional Mould";
#else
L1Info.Header = "Layer 1";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
#endif
L1Info!.Header = "Layer 1";
L1MasteringRing!.Label = "Mastering Ring";
L1MasteringSID!.Label = "Mastering SID";
L1Toolstamp!.Label = "Toolstamp/Mastering Code";
L1MouldSID!.Label = "Label Side Mould SID";
L1AdditionalMould!.Label = "Label Side Additional Mould";
#if NET35
_L2Info!.Header = "Layer 2";
_L2MasteringRing!.Label = "Mastering Ring";
_L2MasteringSID!.Label = "Mastering SID";
_L2Toolstamp!.Label = "Toolstamp/Mastering Code";
#else
L2Info.Header = "Layer 2";
L2MasteringRing.Label = "Mastering Ring";
L2MasteringSID.Label = "Mastering SID";
L2Toolstamp.Label = "Toolstamp/Mastering Code";
#endif
L2Info!.Header = "Layer 2";
L2MasteringRing!.Label = "Mastering Ring";
L2MasteringSID!.Label = "Mastering SID";
L2Toolstamp!.Label = "Toolstamp/Mastering Code";
#if NET35
_L3Info!.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
_L3MasteringRing!.Label = "Mastering Ring";
_L3MasteringSID!.Label = "Mastering SID";
_L3Toolstamp!.Label = "Toolstamp/Mastering Code";
#else
L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
L3MasteringRing.Label = "Mastering Ring";
L3MasteringSID.Label = "Mastering SID";
L3Toolstamp.Label = "Toolstamp/Mastering Code";
#endif
L3Info!.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
L3MasteringRing!.Label = "Mastering Ring";
L3MasteringSID!.Label = "Mastering SID";
L3Toolstamp!.Label = "Toolstamp/Mastering Code";
}
// Triple-layer discs
else if (submissionInfo?.SizeAndChecksums?.Layerbreak2 != default(long))
{
#if NET35
_L2Info!.Visibility = Visibility.Visible;
#else
L2Info.Visibility = Visibility.Visible;
#endif
L2Info!.Visibility = Visibility.Visible;
#if NET35
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Data Side Mould SID";
_L0AdditionalMould!.Label = "Data Side Additional Mould";
#else
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
#endif
L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing!.Label = "Mastering Ring";
L0MasteringSID!.Label = "Mastering SID";
L0Toolstamp!.Label = "Toolstamp/Mastering Code";
L0MouldSID!.Label = "Data Side Mould SID";
L0AdditionalMould!.Label = "Data Side Additional Mould";
#if NET35
_L1Info!.Header = "Layer 1";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Label Side Mould SID";
_L1AdditionalMould!.Label = "Label Side Additional Mould";
#else
L1Info.Header = "Layer 1";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
#endif
L1Info!.Header = "Layer 1";
L1MasteringRing!.Label = "Mastering Ring";
L1MasteringSID!.Label = "Mastering SID";
L1Toolstamp!.Label = "Toolstamp/Mastering Code";
L1MouldSID!.Label = "Label Side Mould SID";
L1AdditionalMould!.Label = "Label Side Additional Mould";
#if NET35
_L2Info!.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
_L2MasteringRing!.Label = "Mastering Ring";
_L2MasteringSID!.Label = "Mastering SID";
_L2Toolstamp!.Label = "Toolstamp/Mastering Code";
#else
L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
L2MasteringRing.Label = "Mastering Ring";
L2MasteringSID.Label = "Mastering SID";
L2Toolstamp.Label = "Toolstamp/Mastering Code";
#endif
L2Info!.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
L2MasteringRing!.Label = "Mastering Ring";
L2MasteringSID!.Label = "Mastering SID";
L2Toolstamp!.Label = "Toolstamp/Mastering Code";
}
// Double-layer discs
else if (submissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
{
#if NET35
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Data Side Mould SID";
_L0AdditionalMould!.Label = "Data Side Additional Mould";
#else
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
#endif
L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing!.Label = "Mastering Ring";
L0MasteringSID!.Label = "Mastering SID";
L0Toolstamp!.Label = "Toolstamp/Mastering Code";
L0MouldSID!.Label = "Data Side Mould SID";
L0AdditionalMould!.Label = "Data Side Additional Mould";
#if NET35
_L1Info!.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Label Side Mould SID";
_L1AdditionalMould!.Label = "Label Side Additional Mould";
#else
L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
#endif
L1Info!.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
L1MasteringRing!.Label = "Mastering Ring";
L1MasteringSID!.Label = "Mastering SID";
L1Toolstamp!.Label = "Toolstamp/Mastering Code";
L1MouldSID!.Label = "Label Side Mould SID";
L1AdditionalMould!.Label = "Label Side Additional Mould";
}
// Single-layer discs
else
{
#if NET35
_L0Info!.Header = "Data Side";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Mould SID";
_L0AdditionalMould!.Label = "Additional Mould";
#else
L0Info.Header = "Data Side";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Mould SID";
L0AdditionalMould.Label = "Additional Mould";
#endif
L0Info!.Header = "Data Side";
L0MasteringRing!.Label = "Mastering Ring";
L0MasteringSID!.Label = "Mastering SID";
L0Toolstamp!.Label = "Toolstamp/Mastering Code";
L0MouldSID!.Label = "Mould SID";
L0AdditionalMould!.Label = "Additional Mould";
#if NET35
_L1Info!.Header = "Label Side";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Mould SID";
_L1AdditionalMould!.Label = "Additional Mould";
#else
L1Info.Header = "Label Side";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Mould SID";
L1AdditionalMould.Label = "Additional Mould";
#endif
L1Info!.Header = "Label Side";
L1MasteringRing!.Label = "Mastering Ring";
L1MasteringSID!.Label = "Mastering SID";
L1Toolstamp!.Label = "Toolstamp/Mastering Code";
L1MouldSID!.Label = "Mould SID";
L1AdditionalMould!.Label = "Additional Mould";
}
break;
// All other media we assume to have no rings
default:
#if NET35
_L0Info!.Visibility = Visibility.Collapsed;
_L1Info!.Visibility = Visibility.Collapsed;
_L2Info!.Visibility = Visibility.Collapsed;
_L3Info!.Visibility = Visibility.Collapsed;
#else
L0Info.Visibility = Visibility.Collapsed;
L1Info.Visibility = Visibility.Collapsed;
L2Info.Visibility = Visibility.Collapsed;
L3Info.Visibility = Visibility.Collapsed;
#endif
L0Info!.Visibility = Visibility.Collapsed;
L1Info!.Visibility = Visibility.Collapsed;
L2Info!.Visibility = Visibility.Collapsed;
L3Info!.Visibility = Visibility.Collapsed;
break;
}
}
@@ -730,29 +475,16 @@ namespace MPF.UI.Windows
switch (system)
{
case RedumpSystem.NintendoWiiU:
#if NET35
_DiscKeyTextBox!.Visibility = Visibility.Visible;
#else
DiscKeyTextBox.Visibility = Visibility.Visible;
#endif
DiscKeyTextBox!.Visibility = Visibility.Visible;
break;
case RedumpSystem.SonyPlayStation2:
#if NET35
_LanguageSelectionGrid!.Visibility = Visibility.Visible;
#else
LanguageSelectionGrid.Visibility = Visibility.Visible;
#endif
LanguageSelectionGrid!.Visibility = Visibility.Visible;
break;
case RedumpSystem.SonyPlayStation3:
#if NET35
_DiscKeyTextBox!.Visibility = Visibility.Visible;
_DiscIDTextBox!.Visibility = Visibility.Visible;
#else
DiscKeyTextBox.Visibility = Visibility.Visible;
DiscIDTextBox.Visibility = Visibility.Visible;
#endif
DiscKeyTextBox!.Visibility = Visibility.Visible;
DiscIDTextBox!.Visibility = Visibility.Visible;
break;
}
}

View File

@@ -1,8 +1,12 @@
using System;
using System.IO;
#if NET40
using System.Threading.Tasks;
#endif
using System.Windows;
using System.Windows.Controls;
using MPF.Frontend.ViewModels;
using MPF.UI.Themes;
using MPF.UI.UserControls;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
@@ -24,41 +28,41 @@ namespace MPF.UI.Windows
#region Top Menu Bar
private MenuItem? _AboutMenuItem => ItemHelper.FindChild<MenuItem>(this, "AboutMenuItem");
private MenuItem? _AppExitMenuItem => ItemHelper.FindChild<MenuItem>(this, "AppExitMenuItem");
private MenuItem? _CheckForUpdatesMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckForUpdatesMenuItem");
private MenuItem? _DebugViewMenuItem => ItemHelper.FindChild<MenuItem>(this, "DebugViewMenuItem");
private MenuItem? _CheckDumpMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckDumpMenuItem");
private MenuItem? _CreateIRDMenuItem => ItemHelper.FindChild<MenuItem>(this, "CreateIRDMenuItem");
private MenuItem? _OptionsMenuItem => ItemHelper.FindChild<MenuItem>(this, "OptionsMenuItem");
private MenuItem? AboutMenuItem => ItemHelper.FindChild<MenuItem>(this, "AboutMenuItem");
private MenuItem? AppExitMenuItem => ItemHelper.FindChild<MenuItem>(this, "AppExitMenuItem");
private MenuItem? CheckForUpdatesMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckForUpdatesMenuItem");
private MenuItem? DebugViewMenuItem => ItemHelper.FindChild<MenuItem>(this, "DebugViewMenuItem");
private MenuItem? CheckDumpMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckDumpMenuItem");
private MenuItem? CreateIRDMenuItem => ItemHelper.FindChild<MenuItem>(this, "CreateIRDMenuItem");
private MenuItem? OptionsMenuItem => ItemHelper.FindChild<MenuItem>(this, "OptionsMenuItem");
#endregion
#region Settings
private ComboBox? _DriveLetterComboBox => ItemHelper.FindChild<ComboBox>(this, "DriveLetterComboBox");
private ComboBox? _DriveSpeedComboBox => ItemHelper.FindChild<ComboBox>(this, "DriveSpeedComboBox");
private ComboBox? _DumpingProgramComboBox => ItemHelper.FindChild<ComboBox>(this, "DumpingProgramComboBox");
private CheckBox? _EnableParametersCheckBox => ItemHelper.FindChild<CheckBox>(this, "EnableParametersCheckBox");
private ComboBox? _MediaTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "MediaTypeComboBox");
private Button? _OutputPathBrowseButton => ItemHelper.FindChild<Button>(this, "OutputPathBrowseButton");
private TextBox? _OutputPathTextBox => ItemHelper.FindChild<TextBox>(this, "OutputPathTextBox");
private ComboBox? _SystemTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "SystemTypeComboBox");
private ComboBox? DriveLetterComboBox => ItemHelper.FindChild<ComboBox>(this, "DriveLetterComboBox");
private ComboBox? DriveSpeedComboBox => ItemHelper.FindChild<ComboBox>(this, "DriveSpeedComboBox");
private ComboBox? DumpingProgramComboBox => ItemHelper.FindChild<ComboBox>(this, "DumpingProgramComboBox");
private CheckBox? EnableParametersCheckBox => ItemHelper.FindChild<CheckBox>(this, "EnableParametersCheckBox");
private ComboBox? MediaTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "MediaTypeComboBox");
private Button? OutputPathBrowseButton => ItemHelper.FindChild<Button>(this, "OutputPathBrowseButton");
private TextBox? OutputPathTextBox => ItemHelper.FindChild<TextBox>(this, "OutputPathTextBox");
private ComboBox? SystemTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "SystemTypeComboBox");
#endregion
#region Controls
private Button? _CopyProtectScanButton => ItemHelper.FindChild<Button>(this, "CopyProtectScanButton");
private Button? _MediaScanButton => ItemHelper.FindChild<Button>(this, "MediaScanButton");
private Button? _StartStopButton => ItemHelper.FindChild<Button>(this, "StartStopButton");
private Button? _UpdateVolumeLabel => ItemHelper.FindChild<Button>(this, "UpdateVolumeLabel");
private Button? CopyProtectScanButton => ItemHelper.FindChild<Button>(this, "CopyProtectScanButton");
private Button? MediaScanButton => ItemHelper.FindChild<Button>(this, "MediaScanButton");
private Button? StartStopButton => ItemHelper.FindChild<Button>(this, "StartStopButton");
private Button? UpdateVolumeLabel => ItemHelper.FindChild<Button>(this, "UpdateVolumeLabel");
#endregion
#region Status
private LogOutput? _LogOutput => ItemHelper.FindChild<LogOutput>(this, "LogOutput");
private LogOutput? LogOutput => ItemHelper.FindChild<LogOutput>(this, "LogOutput");
#endregion
@@ -101,17 +105,9 @@ namespace MPF.UI.Windows
// Display the debug option in the menu, if necessary
if (MainViewModel.Options.ShowDebugViewMenuItem)
#if NET35
_DebugViewMenuItem!.Visibility = Visibility.Visible;
#else
DebugViewMenuItem.Visibility = Visibility.Visible;
#endif
DebugViewMenuItem!.Visibility = Visibility.Visible;
#if NET35
MainViewModel.Init(_LogOutput!.EnqueueLog, DisplayUserMessage, ShowDiscInformationWindow);
#else
MainViewModel.Init(LogOutput.EnqueueLog, DisplayUserMessage, ShowDiscInformationWindow);
#endif
MainViewModel.Init(LogOutput!.EnqueueLog, DisplayUserMessage, ShowDiscInformationWindow);
// Set the UI color scheme according to the options
ApplyTheme();
@@ -136,62 +132,31 @@ namespace MPF.UI.Windows
public void AddEventHandlers()
{
// Menu Bar Click
#if NET35
_AboutMenuItem!.Click += AboutClick;
_AppExitMenuItem!.Click += AppExitClick;
_CheckForUpdatesMenuItem!.Click += CheckForUpdatesClick;
_DebugViewMenuItem!.Click += DebugViewClick;
_CheckDumpMenuItem!.Click += CheckDumpMenuItemClick;
_CreateIRDMenuItem!.Click += CreateIRDMenuItemClick;
_OptionsMenuItem!.Click += OptionsMenuItemClick;
#else
AboutMenuItem.Click += AboutClick;
AppExitMenuItem.Click += AppExitClick;
CheckForUpdatesMenuItem.Click += CheckForUpdatesClick;
DebugViewMenuItem.Click += DebugViewClick;
CheckDumpMenuItem.Click += CheckDumpMenuItemClick;
CreateIRDMenuItem.Click += CreateIRDMenuItemClick;
OptionsMenuItem.Click += OptionsMenuItemClick;
#endif
AboutMenuItem!.Click += AboutClick;
AppExitMenuItem!.Click += AppExitClick;
CheckForUpdatesMenuItem!.Click += CheckForUpdatesClick;
DebugViewMenuItem!.Click += DebugViewClick;
CheckDumpMenuItem!.Click += CheckDumpMenuItemClick;
CreateIRDMenuItem!.Click += CreateIRDMenuItemClick;
OptionsMenuItem!.Click += OptionsMenuItemClick;
// User Area Click
#if NET35
_CopyProtectScanButton!.Click += CopyProtectScanButtonClick;
_EnableParametersCheckBox!.Click += EnableParametersCheckBoxClick;
_MediaScanButton!.Click += MediaScanButtonClick;
_UpdateVolumeLabel!.Click += UpdateVolumeLabelClick;
_OutputPathBrowseButton!.Click += OutputPathBrowseButtonClick;
_StartStopButton!.Click += StartStopButtonClick;
#else
CopyProtectScanButton.Click += CopyProtectScanButtonClick;
EnableParametersCheckBox.Click += EnableParametersCheckBoxClick;
MediaScanButton.Click += MediaScanButtonClick;
UpdateVolumeLabel.Click += UpdateVolumeLabelClick;
OutputPathBrowseButton.Click += OutputPathBrowseButtonClick;
StartStopButton.Click += StartStopButtonClick;
#endif
CopyProtectScanButton!.Click += CopyProtectScanButtonClick;
EnableParametersCheckBox!.Click += EnableParametersCheckBoxClick;
MediaScanButton!.Click += MediaScanButtonClick;
UpdateVolumeLabel!.Click += UpdateVolumeLabelClick;
OutputPathBrowseButton!.Click += OutputPathBrowseButtonClick;
StartStopButton!.Click += StartStopButtonClick;
// User Area SelectionChanged
#if NET35
_SystemTypeComboBox!.SelectionChanged += SystemTypeComboBoxSelectionChanged;
_MediaTypeComboBox!.SelectionChanged += MediaTypeComboBoxSelectionChanged;
_DriveLetterComboBox!.SelectionChanged += DriveLetterComboBoxSelectionChanged;
_DriveSpeedComboBox!.SelectionChanged += DriveSpeedComboBoxSelectionChanged;
_DumpingProgramComboBox!.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
#else
SystemTypeComboBox.SelectionChanged += SystemTypeComboBoxSelectionChanged;
MediaTypeComboBox.SelectionChanged += MediaTypeComboBoxSelectionChanged;
DriveLetterComboBox.SelectionChanged += DriveLetterComboBoxSelectionChanged;
DriveSpeedComboBox.SelectionChanged += DriveSpeedComboBoxSelectionChanged;
DumpingProgramComboBox.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
#endif
SystemTypeComboBox!.SelectionChanged += SystemTypeComboBoxSelectionChanged;
MediaTypeComboBox!.SelectionChanged += MediaTypeComboBoxSelectionChanged;
DriveLetterComboBox!.SelectionChanged += DriveLetterComboBoxSelectionChanged;
DriveSpeedComboBox!.SelectionChanged += DriveSpeedComboBoxSelectionChanged;
DumpingProgramComboBox!.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
// User Area TextChanged
#if NET35
_OutputPathTextBox!.TextChanged += OutputPathTextBoxTextChanged;
#else
OutputPathTextBox.TextChanged += OutputPathTextBoxTextChanged;
#endif
OutputPathTextBox!.TextChanged += OutputPathTextBoxTextChanged;
}
/// <summary>
@@ -236,7 +201,7 @@ namespace MPF.UI.Windows
/// <param name="showIfSame">True to show the box even if it's the same, false to only show if it's different</param>
public void CheckForUpdates(bool showIfSame)
{
(bool different, string message, var url) = MainViewModel.CheckForUpdates();
MainViewModel.CheckForUpdates(out bool different, out string message, out var url);
// If we have a new version, put it in the clipboard
if (different && !string.IsNullOrEmpty(url))
@@ -286,8 +251,8 @@ namespace MPF.UI.Windows
public void ShowDebugDiscInfoWindow()
{
var submissionInfo = MainViewModel.CreateDebugSubmissionInfo();
var result = ShowDiscInformationWindow(submissionInfo);
Formatter.ProcessSpecialFields(result.Item2);
_ = ShowDiscInformationWindow(ref submissionInfo);
Formatter.ProcessSpecialFields(submissionInfo);
}
/// <summary>
@@ -295,7 +260,7 @@ namespace MPF.UI.Windows
/// </summary>
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
/// <returns>Dialog open result</returns>
public (bool?, SubmissionInfo?) ShowDiscInformationWindow(SubmissionInfo? submissionInfo)
public bool? ShowDiscInformationWindow(ref SubmissionInfo? submissionInfo)
{
if (MainViewModel.Options.ShowDiscEjectReminder)
CustomMessageBox.Show(this, "It is now safe to eject the disc", "Eject", MessageBoxButton.OK, MessageBoxImage.Information);
@@ -309,14 +274,14 @@ namespace MPF.UI.Windows
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
discInformationWindow.Closed += delegate { this.Activate(); };
discInformationWindow.Closed += delegate { 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!);
return result;
}
/// <summary>
@@ -325,7 +290,7 @@ namespace MPF.UI.Windows
public void ShowCheckDumpWindow()
{
// Hide MainWindow while Check GUI is open
this.Hide();
Hide();
var checkDumpWindow = new CheckDumpWindow(this)
{
@@ -336,10 +301,11 @@ namespace MPF.UI.Windows
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
checkDumpWindow.Closed += delegate {
checkDumpWindow.Closed += delegate
{
// Unhide Main window after Check window has been closed
this.Show();
this.Activate();
Show();
Activate();
};
checkDumpWindow.Show();
}
@@ -350,7 +316,7 @@ namespace MPF.UI.Windows
public void ShowCreateIRDWindow()
{
// Hide MainWindow while Create IRD UI is open
this.Hide();
Hide();
var createIRDWindow = new CreateIRDWindow(this)
{
@@ -361,10 +327,11 @@ namespace MPF.UI.Windows
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
createIRDWindow.Closed += delegate {
createIRDWindow.Closed += delegate
{
// Unhide Main window after Create IRD window has been closed
this.Show();
this.Activate();
Show();
Activate();
};
createIRDWindow.Show();
}
@@ -384,7 +351,7 @@ namespace MPF.UI.Windows
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
optionsWindow.Closed += delegate { this.Activate(); };
optionsWindow.Closed += delegate { Activate(); };
optionsWindow.Closed += OnOptionsUpdated;
optionsWindow.Show();
}
@@ -397,13 +364,66 @@ namespace MPF.UI.Windows
Theme theme;
if (MainViewModel.Options.EnableDarkMode)
theme = new DarkModeTheme();
else if (MainViewModel.Options.EnablePurpMode)
theme = new CustomTheme("111111", "9A5EC0");
else if (IsHexColor(MainViewModel.Options.CustomBackgroundColor) && IsHexColor(MainViewModel.Options.CustomTextColor))
theme = new CustomTheme(MainViewModel.Options.CustomBackgroundColor, MainViewModel.Options.CustomTextColor);
else
theme = new LightModeTheme();
theme.Apply();
}
#endregion
/// <summary>
/// Check whether a string represents a valid hex color
/// </summary>
public static bool IsHexColor(string? color)
{
if (string.IsNullOrWhiteSpace(color))
return false;
if (color!.Length == 7 && color[0] == '#')
color = color.Substring(1);
if (color.Length != 6)
return false;
for (int i = 0; i < color.Length; i++)
{
switch (color[i])
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
continue;
default:
break;
}
}
return true;
}
#endregion
#region Event Handlers
@@ -479,21 +499,13 @@ namespace MPF.UI.Windows
/// <summary>
/// Handler for CopyProtectScanButton Click event
/// </summary>
#if NET40
public void CopyProtectScanButtonClick(object sender, RoutedEventArgs e)
#else
public async void CopyProtectScanButtonClick(object sender, RoutedEventArgs e)
#endif
{
#if NET40
var (output, error) = MainViewModel.ScanAndShowProtection();
#else
var (output, error) = await MainViewModel.ScanAndShowProtection();
#endif
var output = await MainViewModel.ScanAndShowProtection();
if (!MainViewModel.LogPanelExpanded)
{
if (!string.IsNullOrEmpty(output) && string.IsNullOrEmpty(error))
if (!string.IsNullOrEmpty(output))
CustomMessageBox.Show(this, output, "Detected Protection(s)", MessageBoxButton.OK, MessageBoxImage.Information);
else
CustomMessageBox.Show(this, "An exception occurred, see the log for details", "Error!", MessageBoxButton.OK, MessageBoxImage.Error);

View File

@@ -179,10 +179,11 @@
ItemsSource="{Binding InternalPrograms}" SelectedItem="{Binding Options.InternalProgram, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
Style="{DynamicResource CustomComboBoxStyle}" />
<Label Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Output Path" />
<Label Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Output Path"
ToolTip="Variables allowed:&#x0a; &#37;SYSTEM&#37;&#9;(System name, long)&#x0a; &#37;SYS&#37;&#9;&#9;(System name, short)&#x0a; &#37;MEDIA&#37;&#9;(Media type)&#x0a; &#37;PROGRAM&#37;&#9;(Program name, long)&#x0a; &#37;PROG&#37;&#9;(Program name, short)&#x0a; &#37;LABEL&#37;&#9;(Volume label)&#x0a; &#37;DATE&#37;&#9;(Current date)&#x0a; &#37;DATETIME&#37;&#9;(Current date and time)" />
<TextBox x:Name="DefaultOutputPathTextBox" Grid.Row="4" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
Text="{Binding Options.DefaultOutputPath}" VerticalContentAlignment="Center"
ToolTip="Variables allowed:&#x0a; &#37;SYSTEM&#37;&#9;(System name, long)&#x0a; &#37;SYS&#37;&#9;&#9;(System name, short)&#x0a; &#37;MEDIA&#37;&#9;(Media type)&#x0a; &#37;PROGRAM&#37;&#9;(Program name, long)&#x0a; &#37;PROG&#37;&#9;(Program name, short)&#x0a; &#37;LABEL&#37;&#9;(Volume label)&#x0a; &#37;DATE&#37;&#9;(Current date)&#x0a; &#37;DATETIME&#37;&#9;(Current date and time)"/>
ToolTip="Variables allowed:&#x0a; &#37;SYSTEM&#37;&#9;(System name, long)&#x0a; &#37;SYS&#37;&#9;&#9;(System name, short)&#x0a; &#37;MEDIA&#37;&#9;(Media type)&#x0a; &#37;PROGRAM&#37;&#9;(Program name, long)&#x0a; &#37;PROG&#37;&#9;(Program name, short)&#x0a; &#37;LABEL&#37;&#9;(Volume label)&#x0a; &#37;DATE&#37;&#9;(Current date)&#x0a; &#37;DATETIME&#37;&#9;(Current date and time)" />
<Button x:Name="DefaultOutputPathButton" Grid.Row="4" Grid.Column="2" Height="22" Width="22" Content="..."
Style="{DynamicResource CustomButtonStyle}" />
</Grid>

View File

@@ -1,11 +1,15 @@
using System;
using System.IO;
#if NET40
using System.Threading;
#endif
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using MPF.Frontend;
using MPF.Frontend.ViewModels;
using SabreTools.RedumpLib.Web;
using WPFCustomMessageBox;
#pragma warning disable IDE1006 // Naming Styles
@@ -24,15 +28,15 @@ namespace MPF.UI.Windows
#if NET35
private System.Windows.Controls.Button? _AaruPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "AaruPathButton");
private System.Windows.Controls.Button? _AcceptButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "AcceptButton");
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
private System.Windows.Controls.Button? _DefaultOutputPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "DefaultOutputPathButton");
private System.Windows.Controls.Button? _DiscImageCreatorPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "DiscImageCreatorPathButton");
private System.Windows.Controls.Button? _RedumperPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "RedumperPathButton");
private System.Windows.Controls.Button? _RedumpLoginTestButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "RedumpLoginTestButton");
private PasswordBox? _RedumpPasswordBox => ItemHelper.FindChild<PasswordBox>(this, "RedumpPasswordBox");
private System.Windows.Controls.TextBox? _RedumpUsernameTextBox => ItemHelper.FindChild<System.Windows.Controls.TextBox>(this, "RedumpUsernameTextBox");
private System.Windows.Controls.Button? AaruPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "AaruPathButton");
private System.Windows.Controls.Button? AcceptButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "AcceptButton");
private System.Windows.Controls.Button? CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
private System.Windows.Controls.Button? DefaultOutputPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "DefaultOutputPathButton");
private System.Windows.Controls.Button? DiscImageCreatorPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "DiscImageCreatorPathButton");
private System.Windows.Controls.Button? RedumperPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "RedumperPathButton");
private System.Windows.Controls.Button? RedumpLoginTestButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "RedumpLoginTestButton");
private PasswordBox? RedumpPasswordBox => ItemHelper.FindChild<PasswordBox>(this, "RedumpPasswordBox");
private System.Windows.Controls.TextBox? RedumpUsernameTextBox => ItemHelper.FindChild<System.Windows.Controls.TextBox>(this, "RedumpUsernameTextBox");
#endif
@@ -63,34 +67,18 @@ namespace MPF.UI.Windows
DataContext = new OptionsViewModel(options);
// Set initial value for binding
#if NET35
_RedumpPasswordBox!.Password = options.RedumpPassword;
#else
RedumpPasswordBox.Password = options.RedumpPassword;
#endif
RedumpPasswordBox!.Password = options.RedumpPassword;
// Add handlers
#if NET35
_AaruPathButton!.Click += BrowseForPathClick;
_DiscImageCreatorPathButton!.Click += BrowseForPathClick;
_RedumperPathButton!.Click += BrowseForPathClick;
_DefaultOutputPathButton!.Click += BrowseForPathClick;
AaruPathButton!.Click += BrowseForPathClick;
DiscImageCreatorPathButton!.Click += BrowseForPathClick;
RedumperPathButton!.Click += BrowseForPathClick;
DefaultOutputPathButton!.Click += BrowseForPathClick;
_AcceptButton!.Click += OnAcceptClick;
_CancelButton!.Click += OnCancelClick;
_RedumpPasswordBox!.PasswordChanged += OnPasswordChanged;
_RedumpLoginTestButton!.Click += OnRedumpTestClick;
#else
AaruPathButton.Click += BrowseForPathClick;
DiscImageCreatorPathButton.Click += BrowseForPathClick;
RedumperPathButton.Click += BrowseForPathClick;
DefaultOutputPathButton.Click += BrowseForPathClick;
AcceptButton.Click += OnAcceptClick;
CancelButton.Click += OnCancelClick;
RedumpPasswordBox.PasswordChanged += OnPasswordChanged;
RedumpLoginTestButton.Click += OnRedumpTestClick;
#endif
AcceptButton!.Click += OnAcceptClick;
CancelButton!.Click += OnCancelClick;
RedumpPasswordBox!.PasswordChanged += OnPasswordChanged;
RedumpLoginTestButton!.Click += OnRedumpTestClick;
}
/// <summary>
@@ -101,7 +89,7 @@ namespace MPF.UI.Windows
base.OnContentRendered(e);
// Set the window title
OptionsViewModel.Title = this.Title;
OptionsViewModel.Title = Title;
}
#region UI Commands
@@ -198,13 +186,10 @@ namespace MPF.UI.Windows
/// <summary>
/// Test Redump credentials for validity
/// </summary>
private async Task ValidateRedumpCredentials()
private async Task<bool?> ValidateRedumpCredentials()
{
#if NET35
(bool? success, string? message) = await OptionsViewModel.TestRedumpLogin(_RedumpUsernameTextBox!.Text, _RedumpPasswordBox!.Password);
#else
(bool? success, string? message) = await OptionsViewModel.TestRedumpLogin(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password);
#endif
bool? success = await RedumpClient.ValidateCredentials(RedumpUsernameTextBox!.Text, RedumpPasswordBox!.Password);
string message = OptionsViewModel.GetRedumpLoginResult(success);
if (success == true)
CustomMessageBox.Show(this, message, "Success", MessageBoxButton.OK, MessageBoxImage.Information);
@@ -212,6 +197,8 @@ namespace MPF.UI.Windows
CustomMessageBox.Show(this, message, "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
else
CustomMessageBox.Show(this, message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return success;
}
#endregion
@@ -258,26 +245,15 @@ namespace MPF.UI.Windows
/// </summary>
private void OnPasswordChanged(object sender, EventArgs e)
{
#if NET35
OptionsViewModel.Options.RedumpPassword = _RedumpPasswordBox!.Password;
#else
OptionsViewModel.Options.RedumpPassword = RedumpPasswordBox.Password;
#endif
OptionsViewModel.Options.RedumpPassword = RedumpPasswordBox!.Password;
}
/// <summary>
/// Test Redump credentials for validity
/// </summary>
#if NET40
private void OnRedumpTestClick(object sender, EventArgs e)
{
var validateTask = ValidateRedumpCredentials();
validateTask.Wait();
}
#else
private async void OnRedumpTestClick(object sender, EventArgs e) => await ValidateRedumpCredentials();
#endif
private async void OnRedumpTestClick(object sender, EventArgs e)
=> await ValidateRedumpCredentials();
#endregion
#endregion
}
}

View File

@@ -14,7 +14,7 @@ namespace MPF.UI.Windows
{
try
{
this.DialogResult = false;
DialogResult = false;
}
catch { }
@@ -26,7 +26,7 @@ namespace MPF.UI.Windows
/// </summary>
public void MinimizeButtonClick(object sender, RoutedEventArgs e)
{
this.WindowState = WindowState.Minimized;
WindowState = WindowState.Minimized;
}
/// <summary>
@@ -35,7 +35,7 @@ namespace MPF.UI.Windows
public void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
DragMove();
}
#endregion

View File

@@ -38,6 +38,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Processors", "MPF.Proce
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.ExecutionContexts", "MPF.ExecutionContexts\MPF.ExecutionContexts.csproj", "{75609610-CE85-486E-8F42-9525A52B80BF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MPF.CLI", "MPF.CLI\MPF.CLI.csproj", "{A825771B-98AC-44A5-8076-C9AB3D469751}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -68,6 +70,10 @@ Global
{75609610-CE85-486E-8F42-9525A52B80BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{75609610-CE85-486E-8F42-9525A52B80BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{75609610-CE85-486E-8F42-9525A52B80BF}.Release|Any CPU.Build.0 = Release|Any CPU
{A825771B-98AC-44A5-8076-C9AB3D469751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A825771B-98AC-44A5-8076-C9AB3D469751}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A825771B-98AC-44A5-8076-C9AB3D469751}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A825771B-98AC-44A5-8076-C9AB3D469751}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@@ -4,6 +4,7 @@ Redumper/Aaru/DiscImageCreator UI in C#
[![Build status](https://ci.appveyor.com/api/projects/status/3ldav3v0c373jeqa?svg=true)](https://ci.appveyor.com/project/mnadareski/MPF)
[![UI Build](https://github.com/SabreTools/MPF/actions/workflows/build_ui.yml/badge.svg)](https://github.com/SabreTools/MPF/actions/workflows/build_ui.yml)
[![CLI Build](https://github.com/SabreTools/MPF/actions/workflows/build_cli.yml/badge.svg)](https://github.com/SabreTools/MPF/actions/workflows/build_cli.yml)
[![Check Build](https://github.com/SabreTools/MPF/actions/workflows/build_check.yml/badge.svg)](https://github.com/SabreTools/MPF/actions/workflows/build_check.yml)
This is a community project, so if you have some time and knowledge to give, we'll be glad to add you as a contributor to this project. If you have any suggestions, issues, bugs, or crashes, please look at the [Issues](https://github.com/SabreTools/MPF/issues) page first to see if it has been reported before and try out the latest AppVeyor WIP build below to see if it has already been addressed. If it hasn't, please open an issue that's as descriptive as you can be. Help me make this a better program for everyone :)
@@ -14,11 +15,11 @@ For the most recent stable build, download the latest release here: [Releases Pa
For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/MPF/releases/tag/rolling)
## Media Preservation Frontend UI (MPF)
## Media Preservation Frontend UI (MPF.UI)
MPF is the main, UI-centric application of the MPF suite. This program allows users to use Redumper, Aaru, or DiscImageCreator in a more user-friendly way. Each backend dumping program is supported as fully as possible to ensure that all information is captured on output. There are many customization options and quality of life settings that can be access through the Options menu.
### Support Limitations
### UI Support Limitations
The main UI has some known limitations that are documented in code and in some prior support tickets:
@@ -32,40 +33,62 @@ The main UI has some known limitations that are documented in code and in some p
- Consider using a third-party scanning tool, such as Protection ID, if this is not sufficient for your needs
- See [Compatibility Notes](https://github.com/SabreTools/BinaryObjectScanner?tab=readme-ov-file#compatibility-notes) for more details
## Media Preservation Frontend CLI (MPF.CLI)
MPF.CLI is a commandline-only program that allows users to use Redumper, Aaru, or DiscImageCreator in a more user-friendly way. Each backend dumping program is supported as fully as possible to ensure that all information is captured on output. There are many customization options and quality of life settings that can be access through the `config.json` file.
### CLI Support Limitations
The main CLI has some known limitations that are documented in code and in some prior support tickets:
- No programs are bundled by default
- This is the result of the extremely varied builds that are allowed
- For those who require broader archive/installer compatibility for protection scanning (Windows-only), please use the x86 builds as there are some specific scanning libraries that only work with that build
- This is actively being worked on as part of [Binary Object Scanner](https://github.com/SabreTools/BinaryObjectScanner)
- Please consider contributing if you have experience in dealing with multiple archive and installer types
- Consider using a third-party scanning tool, such as Protection ID, if this is not sufficient for your needs
- See [Compatibility Notes](https://github.com/SabreTools/BinaryObjectScanner?tab=readme-ov-file#compatibility-notes) for more details
## Media Preservation Frontend Checker (MPF.Check)
MPF.Check is a commandline-only program that allows users to generate submission information from their personal rips. This program supports the outputs from Redumper, Aaru, DiscImageCreator, Cleanrip, and UmdImageCreator. Running this program without any parameters will display the help text, including all supported parameters.
## System Requirements
Both MPF UI and MPF.Check have the same system requirements for running, with the exception that MPF UI is Windows-only.
MPF.UI, MPF.CLI, and MPF.Check have the same system requirements for running, with the exception that MPF.UI is Windows-only.
- [Supported OS versions for .NET 8](https://github.com/dotnet/core/blob/main/release-notes/8.0/supported-os.md)
- Requires [.NET 8.0 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) if built without bundled runtime
- [Supported OS versions for .NET 9](https://github.com/dotnet/core/blob/main/release-notes/9.0/supported-os.md)
- Requires [.NET 9.0 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) if built without bundled runtime
Ensure that your operating system and runtimes are as up-to-date as possible, since some features may rely on those updates.
## Build Instructions
To build for .NET 8.0, ensure that the [.NET 8.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) (or later) is installed and included in your `PATH`. Then, run the following commands from command prompt, Powershell, Terminal, or shell:
To build for .NET 9.0, ensure that the [.NET 9.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) (or later) is installed and included in your `PATH`. Then, run the following commands from command prompt, Powershell, Terminal, or shell:
**MPF UI (Windows only):**
**MPF.UI (Windows only):**
```bash
dotnet build MPF/MPF.csproj --framework net8.0-windows --runtime [win-x86|win-x64]
dotnet build MPF/MPF.csproj --framework net9.0-windows --runtime [win-x86|win-x64]
```
**MPF.CLI (Windows, OSX, Linux):**
```bash
dotnet build MPF.CLI/MPF.CLI.csproj --framework net9.0 --runtime [win-x86|win-x64|win-arm64|linux-x64|linux-arm64|osx-x64|osx-arm64]
```
**MPF.Check (Windows, OSX, Linux):**
```bash
dotnet build MPF.Check/MPF.Check.csproj --framework net8.0 --runtime [win-x86|win-x64|linux-x64|osx-x64]
dotnet build MPF.Check/MPF.Check.csproj --framework net9.0 --runtime [win-x86|win-x64|win-arm64|linux-x64|linux-arm64|osx-x64|osx-arm64]
```
Choose one of `win-x86`, `win-x64`, `linux-x64`, or `osx-x64` depending on the machine you are targeting.
Choose one of `win-x86`, `win-x64`, `win-arm64`, `linux-x64`, `linux-arm64`, `osx-x64`, or `osx-arm64` depending on the machine you are targeting.
### Build Scripts
Windows users may run `publish-win.ps1` and Linux users may run `publish-nix.sh` to perform a full release build. Both scripts will build and package all variants of MPF UI and MPF.Check with commandline switches to control what is included.
Windows users may run `publish-win.ps1` and Linux users may run `publish-nix.sh` to perform a full release build. Both scripts will build and package all variants of MPF.UI, MPF.CLI, and MPF.Check with commandline switches to control what is included.
- `publish-win.ps1` requires [7-zip commandline](https://www.7-zip.org/download.html) and [Git for Windows](https://git-scm.com/downloads) to be installed and in `PATH`
- `publish-nix.sh` requires `zip` and `git` to be installed and in `PATH`

View File

@@ -1,5 +1,5 @@
# version format
version: 3.2.0-{build}
version: 3.2.4-{build}
# pull request template
pull_requests:

View File

@@ -1,7 +1,7 @@
#!/bin/bash
# This batch file assumes the following:
# - .NET 8.0 (or newer) SDK is installed and in PATH
# - .NET 9.0 (or newer) SDK is installed and in PATH
# - zip is installed and in PATH
# - Git is installed and in PATH
# - The relevant commandline programs are already downloaded
@@ -42,24 +42,31 @@ BUILD_FOLDER=$PWD
# Set the current commit hash
COMMIT=$(git log --pretty=%H -1)
# Create the build matrix arrays
UI_FRAMEWORKS=("net8.0-windows")
UI_RUNTIMES=("win-x86" "win-x64")
CHECK_FRAMEWORKS=("net8.0")
CHECK_RUNTIMES=("win-x86" "win-x64" "linux-x64" "osx-x64")
# Output the selected options
echo "Selected Options:"
echo " Use all frameworks (-u) $USE_ALL"
echo " Include programs (-p) $INCLUDE_PROGRAMS"
echo " No build (-b) $NO_BUILD"
echo " No archive (-a) $NO_ARCHIVE"
echo " "
# Use expanded lists, if requested
# Create the build matrix arrays
UI_FRAMEWORKS=("net9.0-windows")
UI_RUNTIMES=("win-x86" "win-x64")
CHECK_FRAMEWORKS=("net9.0")
CHECK_RUNTIMES=("win-x86" "win-x64" "win-arm64" "linux-x64" "linux-arm64" "osx-x64" "osx-arm64")
# Use expanded framework lists, if requested
if [ $USE_ALL = true ]; then
UI_FRAMEWORKS=("net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0-windows" "net6.0-windows" "net7.0-windows" "net8.0-windows")
UI_RUNTIMES=("win-x86" "win-x64")
CHECK_FRAMEWORKS=("net20" "net35" "net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0")
CHECK_RUNTIMES=("win-x86" "win-x64" "win-arm64" "linux-x64" "linux-arm64" "osx-x64")
UI_FRAMEWORKS=("net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0-windows" "net6.0-windows" "net7.0-windows" "net8.0-windows" "net9.0-windows")
CHECK_FRAMEWORKS=("net20" "net35" "net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0" "net9.0")
fi
# Create the filter arrays
SINGLE_FILE_CAPABLE=("net5.0" "net5.0-windows" "net6.0" "net6.0-windows" "net7.0" "net7.0-windows" "net8.0" "net8.0-windows")
VALID_CROSS_PLATFORM_FRAMEWORKS=("netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0")
VALID_CROSS_PLATFORM_RUNTIMES=("win-arm64" "linux-x64" "linux-arm64" "osx-x64")
SINGLE_FILE_CAPABLE=("net5.0" "net5.0-windows" "net6.0" "net6.0-windows" "net7.0" "net7.0-windows" "net8.0" "net8.0-windows" "net9.0" "net9.0-windows")
VALID_APPLE_FRAMEWORKS=("net6.0" "net7.0" "net8.0" "net9.0")
VALID_CROSS_PLATFORM_FRAMEWORKS=("netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0" "net9.0")
VALID_CROSS_PLATFORM_RUNTIMES=("win-arm64" "linux-x64" "linux-arm64" "osx-x64" "osx-arm64")
# Only build if requested
if [ $NO_BUILD = false ]; then
@@ -67,12 +74,28 @@ if [ $NO_BUILD = false ]; then
echo "Restoring Nuget packages"
dotnet restore
# Create Nuget Packages
dotnet pack MPF.ExecutionContexts/MPF.ExecutionContexts.csproj --output $BUILD_FOLDER
dotnet pack MPF.Processors/MPF.Processors.csproj --output $BUILD_FOLDER
# Build UI
for FRAMEWORK in "${UI_FRAMEWORKS[@]}"; do
for RUNTIME in "${UI_RUNTIMES[@]}"; do
# Output the current build
echo "===== Build UI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
echo "Skipped due to invalid combination"
continue
fi
fi
# If we have Apple silicon but an unsupported framework
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [ $RUNTIME = "osx-arm64" ]; then
echo "Skipped due to no Apple Silicon support"
continue
fi
fi
@@ -94,12 +117,63 @@ if [ $NO_BUILD = false ]; then
done
done
# Build Check
# Build CLI
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
# Output the current build
echo "===== Build CLI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
echo "Skipped due to invalid combination"
continue
fi
fi
# If we have Apple silicon but an unsupported framework
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [ $RUNTIME = "osx-arm64" ]; then
echo "Skipped due to no Apple Silicon support"
continue
fi
fi
# Only .NET 5 and above can publish to a single file
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]; then
# Only include Debug if building all
if [ $USE_ALL = true ]; then
dotnet publish MPF.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
fi
dotnet publish MPF.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
else
# Only include Debug if building all
if [ $USE_ALL = true ]; then
dotnet publish MPF.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
fi
dotnet publish MPF.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
fi
done
done
# Build Check
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
# Output the current build
echo "===== Build Check - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
echo "Skipped due to invalid combination"
continue
fi
fi
# If we have Apple silicon but an unsupported framework
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [ $RUNTIME = "osx-arm64" ]; then
echo "Skipped due to no Apple Silicon support"
continue
fi
fi
@@ -127,6 +201,25 @@ if [ $NO_ARCHIVE = false ]; then
# Create UI archives
for FRAMEWORK in "${UI_FRAMEWORKS[@]}"; do
for RUNTIME in "${UI_RUNTIMES[@]}"; do
# Output the current build
echo "===== Archive UI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
echo "Skipped due to invalid combination"
continue
fi
fi
# If we have Apple silicon but an unsupported framework
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [ $RUNTIME = "osx-arm64" ]; then
echo "Skipped due to no Apple Silicon support"
continue
fi
fi
# Only include Debug if building all
if [ $USE_ALL = true ]; then
cd $BUILD_FOLDER/MPF.UI/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
@@ -145,12 +238,56 @@ if [ $NO_ARCHIVE = false ]; then
done
done
# Create Check archives
# Create CLI archives
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
# Output the current build
echo "===== Archive CLI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
echo "Skipped due to invalid combination"
continue
fi
fi
# If we have Apple silicon but an unsupported framework
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [ $RUNTIME = "osx-arm64" ]; then
echo "Skipped due to no Apple Silicon support"
continue
fi
fi
# Only include Debug if building all
if [ $USE_ALL = true ]; then
cd $BUILD_FOLDER/MPF.CLI/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
zip -r $BUILD_FOLDER/MPF.CLI_${FRAMEWORK}_${RUNTIME}_debug.zip .
fi
cd $BUILD_FOLDER/MPF.CLI/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
zip -r $BUILD_FOLDER/MPF.CLI_${FRAMEWORK}_${RUNTIME}_release.zip .
done
done
# Create Check archives
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
# Output the current build
echo "===== Archive Check - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
echo "Skipped due to invalid combination"
continue
fi
fi
# If we have Apple silicon but an unsupported framework
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
if [ $RUNTIME = "osx-arm64" ]; then
echo "Skipped due to no Apple Silicon support"
continue
fi
fi

View File

@@ -1,5 +1,5 @@
# This batch file assumes the following:
# - .NET 8.0 (or newer) SDK is installed and in PATH
# - .NET 9.0 (or newer) SDK is installed and in PATH
# - 7-zip commandline (7z.exe) is installed and in PATH
# - Git for Windows is installed and in PATH
# - The relevant commandline programs are already downloaded
@@ -33,24 +33,31 @@ $BUILD_FOLDER = $PSScriptRoot
# Set the current commit hash
$COMMIT = git log --pretty=format:"%H" -1
# Create the build matrix arrays
$UI_FRAMEWORKS = @('net8.0-windows')
$UI_RUNTIMES = @('win-x86', 'win-x64')
$CHECK_FRAMEWORKS = @('net8.0')
$CHECK_RUNTIMES = @('win-x86', 'win-x64', 'linux-x64', 'osx-x64')
# Output the selected options
Write-Host "Selected Options:"
Write-Host " Use all frameworks (-UseAll) $USE_ALL"
Write-Host " Include programs (-IncludePrograms) $INCLUDE_PROGRAMS"
Write-Host " No build (-NoBuild) $NO_BUILD"
Write-Host " No archive (-NoArchive) $NO_ARCHIVE"
Write-Host " "
# Use expanded lists, if requested
# Create the build matrix arrays
$UI_FRAMEWORKS = @('net9.0-windows')
$UI_RUNTIMES = @('win-x86', 'win-x64')
$CHECK_FRAMEWORKS = @('net9.0')
$CHECK_RUNTIMES = @('win-x86', 'win-x64', 'win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64')
# Use expanded framework lists, if requested
if ($USE_ALL.IsPresent) {
$UI_FRAMEWORKS = @('net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0-windows', 'net6.0-windows', 'net7.0-windows', 'net8.0-windows')
$UI_RUNTIMES = @('win-x86', 'win-x64')
$CHECK_FRAMEWORKS = @('net20', 'net35', 'net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
$CHECK_RUNTIMES = @('win-x86', 'win-x64', 'win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64')
$UI_FRAMEWORKS = @('net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0-windows', 'net6.0-windows', 'net7.0-windows', 'net8.0-windows', 'net9.0-windows')
$CHECK_FRAMEWORKS = @('net20', 'net35', 'net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0', 'net9.0')
}
# Create the filter arrays
$SINGLE_FILE_CAPABLE = @('net5.0', 'net5.0-windows', 'net6.0', 'net6.0-windows', 'net7.0', 'net7.0-windows', 'net8.0', 'net8.0-windows')
$VALID_CROSS_PLATFORM_FRAMEWORKS = @('netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
$VALID_CROSS_PLATFORM_RUNTIMES = @('win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64')
$SINGLE_FILE_CAPABLE = @('net5.0', 'net5.0-windows', 'net6.0', 'net6.0-windows', 'net7.0', 'net7.0-windows', 'net8.0', 'net8.0-windows', 'net9.0', 'net9.0-windows')
$VALID_APPLE_FRAMEWORKS = @('net6.0', 'net7.0', 'net8.0', 'net9.0')
$VALID_CROSS_PLATFORM_FRAMEWORKS = @('netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0', 'net9.0')
$VALID_CROSS_PLATFORM_RUNTIMES = @('win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64')
# Only build if requested
if (!$NO_BUILD.IsPresent) {
@@ -58,11 +65,25 @@ if (!$NO_BUILD.IsPresent) {
Write-Host "Restoring Nuget packages"
dotnet restore
# Create Nuget Packages
dotnet pack MPF.ExecutionContexts\MPF.ExecutionContexts.csproj --output $BUILD_FOLDER
dotnet pack MPF.Processors\MPF.Processors.csproj --output $BUILD_FOLDER
# Build UI
foreach ($FRAMEWORK in $UI_FRAMEWORKS) {
foreach ($RUNTIME in $UI_RUNTIMES) {
# Output the current build
Write-Host "===== Build UI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
Write-Host "Skipped due to invalid combination"
continue
}
# If we have Apple silicon but an unsupported framework
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
Write-Host "Skipped due to no Apple Silicon support"
continue
}
@@ -84,11 +105,57 @@ if (!$NO_BUILD.IsPresent) {
}
}
# Build CLI
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
foreach ($RUNTIME in $CHECK_RUNTIMES) {
# Output the current build
Write-Host "===== Build CLI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
Write-Host "Skipped due to invalid combination"
continue
}
# If we have Apple silicon but an unsupported framework
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
Write-Host "Skipped due to no Apple Silicon support"
continue
}
# Only .NET 5 and above can publish to a single file
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK) {
# Only include Debug if building all
if ($USE_ALL.IsPresent) {
dotnet publish MPF.CLI\MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
}
dotnet publish MPF.CLI\MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
}
else {
# Only include Debug if building all
if ($USE_ALL.IsPresent) {
dotnet publish MPF.CLI\MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
}
dotnet publish MPF.CLI\MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
}
}
}
# Build Check
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
foreach ($RUNTIME in $CHECK_RUNTIMES) {
# Output the current build
Write-Host "===== Build Check - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
Write-Host "Skipped due to invalid combination"
continue
}
# If we have Apple silicon but an unsupported framework
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
Write-Host "Skipped due to no Apple Silicon support"
continue
}
@@ -116,6 +183,21 @@ if (!$NO_ARCHIVE.IsPresent) {
# Create UI archives
foreach ($FRAMEWORK in $UI_FRAMEWORKS) {
foreach ($RUNTIME in $UI_RUNTIMES) {
# Output the current build
Write-Host "===== Archive UI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
Write-Host "Skipped due to invalid combination"
continue
}
# If we have Apple silicon but an unsupported framework
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
Write-Host "Skipped due to no Apple Silicon support"
continue
}
# Only include Debug if building all
if ($USE_ALL.IsPresent) {
Set-Location -Path $BUILD_FOLDER\MPF.UI\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
@@ -137,11 +219,49 @@ if (!$NO_ARCHIVE.IsPresent) {
}
}
# Create CLI archives
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
foreach ($RUNTIME in $CHECK_RUNTIMES) {
# Output the current build
Write-Host "===== Archive CLI - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
Write-Host "Skipped due to invalid combination"
continue
}
# If we have Apple silicon but an unsupported framework
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
Write-Host "Skipped due to no Apple Silicon support"
continue
}
# Only include Debug if building all
if ($USE_ALL.IsPresent) {
Set-Location -Path $BUILD_FOLDER\MPF.CLI\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\MPF.CLI_${FRAMEWORK}_${RUNTIME}_debug.zip *
}
Set-Location -Path $BUILD_FOLDER\MPF.CLI\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
7z a -tzip $BUILD_FOLDER\MPF.CLI_${FRAMEWORK}_${RUNTIME}_release.zip *
}
}
# Create Check archives
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
foreach ($RUNTIME in $CHECK_RUNTIMES) {
# Output the current build
Write-Host "===== Archive Check - $FRAMEWORK, $RUNTIME ====="
# If we have an invalid combination of framework and runtime
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
Write-Host "Skipped due to invalid combination"
continue
}
# If we have Apple silicon but an unsupported framework
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
Write-Host "Skipped due to no Apple Silicon support"
continue
}