Compare commits

..

351 Commits
3.2.3 ... 3.4.0

Author SHA1 Message Date
Matt Nadareski
ee7cde6360 Bump version 2025-09-25 08:05:10 -04:00
Matt Nadareski
8e9edf43ac Update RedumpLib to 1.7.4 2025-09-24 13:41:27 -04:00
Matt Nadareski
51115430cb Update BinaryObjectScanner to 3.4.2 2025-09-24 13:20:29 -04:00
Matt Nadareski
7cf108828e Update changelog 2025-09-22 15:33:55 -04:00
Matt Nadareski
020390af65 Cleanup last commits, add tests 2025-09-22 15:30:48 -04:00
HeroponRikiBestest
e04ceb953c Add filters to handle Release Control output. (#892)
* Add filters to handle matroschka output.

* Apply RC-contained filtering to new framework

* Attempt to refine logic to fit framework
2025-09-22 15:23:24 -04:00
Matt Nadareski
363b018cb7 Further flesh out framework 2025-09-22 13:44:21 -04:00
Matt Nadareski
cf025522ef Add context-sensitive protections helper method 2025-09-22 13:41:11 -04:00
Matt Nadareski
0fb8bf5c29 Update BinaryObjectScanner to 3.4.0 2025-09-22 10:15:40 -04:00
Matt Nadareski
e280745eee Add RedumperRefineSectorMode setting (fixes #890) 2025-09-18 08:27:42 -04:00
fuzzball
fb306750e6 Support new versioning format in redumper (#889)
* Support new versioning format

* Update changelist
2025-09-16 07:13:54 -04:00
Deterous
da5a514482 Update redumper to build 655 (#888) 2025-09-12 07:35:01 -04:00
Deterous
fc288e1c46 Update RedumpLib, detect Playmaji Polymega system (#886)
* Update RedumpLib, detect Playmaji Polymega system

* Update libs for Processors

* Add IO and Matching to Processors csproj
2025-09-05 11:09:22 -04:00
Deterous
102acb9ebf Fix UIC processing logic (#885)
* Fix UIC title field

* the sabre approved way

* Better UIC test_disc.txt

* Fix UIC category logic

* Fix GetUMDCategoryTest

* Final test tweak
2025-09-04 08:27:13 -04:00
Deterous
b76b2a69f5 Update DIC to 20250901 (Windows/Linux only) (#884) 2025-09-02 07:24:07 -04:00
Deterous
254ad6cfd0 Support multisession cache files (#882)
* redumper multisession .cache

* Changelist

* Fix tests
2025-08-25 08:00:34 -04:00
Matt Nadareski
f432f438ab Update RedumpLib to 1.7.1 2025-08-23 09:58:16 -04:00
Matt Nadareski
e890243830 Update test Nuget packages 2025-08-23 09:56:25 -04:00
Matt Nadareski
6493b462f1 Minor cleanup 2025-08-23 09:47:17 -04:00
Deterous
54c0716ea9 Update redumper to build 653 (#880)
* Update redumper to build 653

* nit
2025-08-23 08:43:35 -04:00
Matt Nadareski
7fbb5133d7 Update LibIRD 2025-08-19 10:41:53 -04:00
Matt Nadareski
993a0fd7d3 Update RedumpLib and LibIRD 2025-08-19 07:23:02 -04:00
fuzzball
47878fee1f Fix for C2 error doubling issue (#877)
* Expand error detection

* Update changelist

---------

Co-authored-by: Matt Nadareski <mnadareski@outlook.com>
2025-08-15 10:37:15 -04:00
Matt Nadareski
4cb8a31505 Update Aaru to build 5.4.1 (fixes #879) 2025-08-14 09:02:25 -04:00
Matt Nadareski
0685972842 Fix inconsistency with newlines 2025-07-23 11:49:54 -04:00
Matt Nadareski
5b1cc3c715 Add CopyUpdateUrlToClipboard option 2025-07-23 07:51:18 -04:00
Matt Nadareski
572f0d5095 Treat all UMD as DL visually 2025-07-22 14:57:03 -04:00
Matt Nadareski
23bafad3db Update RedumpLib to 1.6.9 2025-07-21 12:14:37 -04:00
Matt Nadareski
c7b77e4bd7 Reduce preprocessing in DumpEnvironment 2025-07-19 12:36:03 -04:00
Matt Nadareski
755eee4441 Put a try/catch around GenerateArtifacts 2025-07-19 12:25:20 -04:00
Matt Nadareski
fca2c53d6c Let the processor always deal with unsupported 2025-07-19 09:06:28 -04:00
Matt Nadareski
8f6f5f6ef0 UI consistency when parameters are editable (fixes #876) 2025-07-18 15:09:21 -04:00
Matt Nadareski
2e9970ee6a Always trust the output files for processing 2025-07-18 13:20:45 -04:00
Matt Nadareski
4fa1273111 Remove media type from Check Dump UI 2025-07-18 13:15:40 -04:00
Matt Nadareski
d3993a48e4 Bump version 2025-07-18 08:20:54 -04:00
Deterous
d522bb6c76 Update redumper to build 631 (#875)
* Update redumper to build 631

* add flag to tests
2025-07-16 08:04:27 -04:00
Matt Nadareski
816672a817 Slightly better IRD organization 2025-07-15 13:06:05 -04:00
Matt Nadareski
f8171da306 Better handle mixed-separator paths 2025-07-15 08:00:55 -04:00
Matt Nadareski
ce073e5fbf Simplify code from previous commit 2025-07-14 13:13:25 -04:00
Matt Nadareski
28205e42f0 Strip errant whitespace during path normalization (fixes #874) 2025-07-14 12:58:44 -04:00
Matt Nadareski
0160366530 Add new help and version flags for CLI and Check (fixes #873) 2025-07-12 16:04:54 -04:00
Matt Nadareski
663477d408 Update RedumpLib to 1.6.8 (fixes #872) 2025-07-11 12:49:12 -04:00
Matt Nadareski
bd2e012eef Net Yaroze only for PS1 2025-07-11 12:09:06 -04:00
Matt Nadareski
a514374169 Remove legacy codes from info window (fixes #871) 2025-07-11 12:05:03 -04:00
Matt Nadareski
f73c0730e2 Update changelog 2025-07-09 07:23:38 -04:00
fuzzball
a658c80de7 Empty should be null (#868) 2025-07-09 07:23:09 -04:00
Matt Nadareski
b806bc6cd1 Fix unnecessary null assignment 2025-06-26 10:35:47 -04:00
Matt Nadareski
cf9675f620 Only use serial for PS3/4/5 if no custom volume label 2025-06-26 10:34:49 -04:00
Matt Nadareski
ea18051709 Swap PS1/2 back to original name handling 2025-06-26 08:38:46 -04:00
Matt Nadareski
4fdf8e5dde Use reasonable default names for PlayStation systems (fixes #866) 2025-06-26 08:30:14 -04:00
Deterous
a4616d139d Better handling of Xbox/360, redumper build 613 (#865)
* Better handling of bad SS, redumper build 613

* Add missing helper function

* Fix new func signature

* Fix var name

* Nulls

* Don't get vollabel for xbox/360

* formatting

* formatting v2
2025-06-23 21:03:42 -04:00
Matt Nadareski
0f09a9c913 Address nullable default value 2025-06-18 09:40:00 -04:00
Matt Nadareski
48ffd6f40c Set some default values for CLI (fixes #863) 2025-06-18 09:34:35 -04:00
Matt Nadareski
6612c8ea4d Remove dead code in DIC processor 2025-06-18 09:13:44 -04:00
Matt Nadareski
68d1a0664a IsAudio cleanup cleanup 2025-06-18 09:08:37 -04:00
Matt Nadareski
86d8590789 IsAudio cleanup 2025-06-18 08:52:51 -04:00
Matt Nadareski
e23427d7c9 Update redumper to build 611 2025-06-17 21:00:52 -04:00
Matt Nadareski
cd19a2e4a0 Use the correct base path for Check 2025-06-17 17:55:10 -04:00
Matt Nadareski
000e7d88a8 Fix start index in Check 2025-06-17 17:18:12 -04:00
Matt Nadareski
e3beb1ef77 Rename disc info to media info 2025-06-17 16:35:03 -04:00
Matt Nadareski
a3a75b1c2d Fix missed test updates 2025-06-17 15:52:48 -04:00
Matt Nadareski
11850a8d6b Enable Check to determine media type automatically 2025-06-17 15:36:23 -04:00
Matt Nadareski
bf6b58d64b Fill out DetermineMediaType for DiscImageCreator 2025-06-17 14:26:13 -04:00
Matt Nadareski
1789334625 Fill out DetermineMediaType for Aaru 2025-06-17 13:05:35 -04:00
Matt Nadareski
9520f58240 Fill out DetermineMediaType for Redumper 2025-06-17 11:37:37 -04:00
Matt Nadareski
48e5e01729 Add DetermineMediaType scaffolding and tests 2025-06-17 09:45:16 -04:00
Matt Nadareski
8c7959fb08 Reduce media-specific checks where unnecessary 2025-06-17 08:51:57 -04:00
Matt Nadareski
8ef7543cf1 Ignore volume labels with path separators 2025-06-16 09:26:56 -04:00
Matt Nadareski
f9e39ee4be Further clarify configuration requirements for CLI (fixes #861) 2025-06-16 07:43:51 -04:00
Matt Nadareski
0a4621c963 Handle layers for PS3CFW (fixes #859) 2025-06-13 20:35:32 -04:00
Matt Nadareski
04b4f528ba Fix changelog location 2025-06-12 08:34:49 -04:00
Matt Nadareski
f024e49c21 Bump version 2025-06-12 08:33:55 -04:00
Deterous
7ca404f9dd Update redumper to build 610 (#858) 2025-06-12 07:50:51 -04:00
Matt Nadareski
18fc162cd8 Fix size retrieval for Aaru 2025-06-09 09:28:04 -04:00
Matt Nadareski
b577bbae37 Create multi-size icon (fixes #857) 2025-06-06 08:38:44 -04:00
Matt Nadareski
02f9509b86 Simplify Options naming where possible 2025-06-02 10:19:48 -04:00
Matt Nadareski
8b00e3deed Decouple retieval from login 2025-06-02 09:35:26 -04:00
Matt Nadareski
3ffea60402 Remove unnecessary options property 2025-06-02 09:08:15 -04:00
Matt Nadareski
f8eab60ecc Fix tests from last commit 2025-06-01 20:54:16 -04:00
Matt Nadareski
9e8d46fb03 Fix Redump default options (fixes #855) 2025-06-01 20:49:30 -04:00
Matt Nadareski
3831922f29 Add currently-hidden RetrieveMatchInformation option 2025-06-01 20:36:18 -04:00
Matt Nadareski
7f7416c053 Update tooltips/labels for controversial options 2025-05-30 12:48:23 -04:00
Matt Nadareski
529ada1b8b Fix issues with last commit 2025-05-30 12:43:25 -04:00
Deterous
6260f1a1fe Update redumper, MPF options, SS sanity check (#854)
* Update redumper, add DriveType option

* Fix typo

* Don't save ss file if invalid SS

* Update changelist

* LongName for DriveType

* Fix typo

* Fix tests

* Redo tests

* Fix test typo

* Remove additional space from test

* Simplify redumper program options

* semicolon
2025-05-30 12:40:22 -04:00
Matt Nadareski
a7f6e09226 Update RedumpLib to 1.6.7 (fixes #853) 2025-05-23 12:54:27 -04:00
Matt Nadareski
4b49815a7a Include Aaru in automatic builds 2025-05-23 12:16:51 -04:00
Matt Nadareski
14c17bbdf7 Bump version 2025-05-23 12:02:56 -04:00
Matt Nadareski
0fe30d2d4f Remove unnecessary conditional 2025-05-22 12:11:01 -04:00
Matt Nadareski
1c3f65e7f9 Handle error count resets in Redumper (fixes #852) 2025-05-22 12:07:50 -04:00
Matt Nadareski
62ff0f955a Reset Redump errors when an INFO block is found 2025-05-22 11:24:20 -04:00
Deterous
a942e6142a Minor Check UI improvements (#851)
* Minor Check UI improvements

* Move Check warning below status box

* Hover text on status message

* Full status in tooltip

* Add ellipsis to status text

* null ref assignment

* OneWay

* trigger StatusFirstLine property changed

* Better status property block

* wrong op
2025-05-22 10:46:53 -04:00
Matt Nadareski
f637e629a3 Add and use status label for Check window 2025-05-22 08:55:18 -04:00
Deterous
72b00953d8 Don't hash bad SS (#850)
* Don't hash bad SS

* fix build

* proper namespace
2025-05-22 08:31:24 -04:00
Matt Nadareski
cfac607c7d Use null for 0 speed internally 2025-05-22 08:21:06 -04:00
Matt Nadareski
e9a84e5429 Further fix speed dropdown with Redumper (fixes #849) 2025-05-22 07:56:57 -04:00
Matt Nadareski
2f985d0526 Update redumper to build 585 2025-05-21 11:39:26 -04:00
Matt Nadareski
e1ace14753 Fix speed dropdown with Redumper (fixes #849) 2025-05-21 09:17:19 -04:00
Matt Nadareski
9c29967f65 Fix bracket formatting from last commit 2025-05-19 11:56:15 -04:00
Deterous
5612d7989d Relax PS3CFW output filename requirements (#848)
* Relax PS3CFW output filename requirements

* don't use Linq

* better regex

* literal backslash

* requested changes

* fix

* Update MPF.Processors/PS3CFW.cs

Co-authored-by: Matt Nadareski <mnadareski@outlook.com>

---------

Co-authored-by: Matt Nadareski <mnadareski@outlook.com>
2025-05-19 11:54:37 -04:00
Matt Nadareski
f872a6be29 Minor cleanup from last commit 2025-05-19 08:48:09 -04:00
Deterous
59e7ecfa8a Better audio-only check (#847)
* Better IsAudio()

* null deref

* Don't set fields if empty

* Update Changelist

* Requested changes

* fix build

* fix build again

* string literal

* C# is dumb

* Revert to original

* revert further

* remove extra comma
2025-05-19 08:23:25 -04:00
Deterous
79cce37cf6 Add newlines to pkg info printing (#846)
* Add newlines to pkg info printing

* newline for ps4 too
2025-05-16 11:00:04 -04:00
Matt Nadareski
4016b3fc19 Serialize JSON a slightly different way 2025-05-12 21:44:47 -04:00
Matt Nadareski
e5bcada606 Simplify WriteOutputData path handling 2025-05-12 21:37:48 -04:00
Matt Nadareski
60d31363e3 Explicitly unset Redumper speed, if needed 2025-05-08 07:46:49 -04:00
Matt Nadareski
22a125a838 One more note about hidden settings 2025-05-05 21:29:17 -04:00
Matt Nadareski
0a86781d61 Clarification on some options 2025-05-05 21:14:35 -04:00
Matt Nadareski
c5649ecdbb Add Check warnings around overwriting and parsing 2025-05-05 13:05:35 -04:00
Matt Nadareski
df22336ba0 Support checking Redumper DAT from zip; add tests 2025-05-05 12:45:02 -04:00
Matt Nadareski
2d7350cbaf Close the log archive, if it exists 2025-05-05 11:49:33 -04:00
Deterous
6a56a58b7c Update redumper to build 565 (#842) 2025-05-04 19:54:09 -04:00
Matt Nadareski
a97c94885c Fix lines wiped before displayed 2025-05-04 11:23:24 -04:00
Matt Nadareski
9548993982 Tweak to previous commit for some build versions 2025-05-04 11:18:28 -04:00
Matt Nadareski
1476972c26 Pre-clean file path in CLI interactive mode 2025-05-04 11:14:46 -04:00
Matt Nadareski
85cde67d0a Update RedumpLib to 1.6.6 (fixes #841) 2025-05-01 11:00:04 -04:00
Matt Nadareski
bfaf24c237 Update to DIC 20250501 2025-05-01 10:27:31 -04:00
Matt Nadareski
adfa8a9a0c Normalize Shift-JIS characters, when possible (fixes #827) 2025-05-01 09:58:48 -04:00
Matt Nadareski
3d68d880f6 Update Nuget packages 2025-05-01 09:40:42 -04:00
Matt Nadareski
77bc7e7b1a Fix package reference layout 2025-05-01 09:35:41 -04:00
Matt Nadareski
23837ee555 Update RedumpLib to 1.6.5 (fixes #809, fixes #834) 2025-04-30 21:03:01 -04:00
Matt Nadareski
0553d1256a Allow exiting from interactive modes 2025-04-30 20:32:49 -04:00
Matt Nadareski
68f0c6cf35 Avoid redundant redundancy 2025-04-30 20:27:42 -04:00
Matt Nadareski
32d3996432 Make interactive mode cleaner 2025-04-30 20:14:04 -04:00
Matt Nadareski
5af02fe508 It's not dumping, it's checking 2025-04-30 16:52:40 -04:00
Matt Nadareski
eba6a7ce4a Fix menu title in Check interactive 2025-04-30 16:50:04 -04:00
Matt Nadareski
7faedc92e4 Add interactive mode to Check 2025-04-30 16:49:28 -04:00
Matt Nadareski
3de48fc24e Clean up case-sensitivity 2025-04-30 16:29:34 -04:00
Matt Nadareski
9400523c4f Add padding above root interactive node label 2025-04-30 16:22:04 -04:00
Matt Nadareski
48c945f194 Add interactive mode to CLI (fixes #819) 2025-04-30 16:19:19 -04:00
Matt Nadareski
d9a8050beb Fix removing empty subdirectories 2025-04-30 15:15:27 -04:00
Matt Nadareski
6ec179f0ed Comment download scripts more 2025-04-30 15:01:30 -04:00
Matt Nadareski
620851af73 Fix minor packing issues 2025-04-30 14:43:16 -04:00
Matt Nadareski
f5c4b7ebdf Enable packing programs for CLI 2025-04-30 14:19:44 -04:00
Matt Nadareski
364ae1b837 Replace old download implementations (fixes #840) 2025-04-30 13:54:44 -04:00
Matt Nadareski
b4b864cd9f Fix issue with some downloads 2025-04-30 13:47:41 -04:00
Matt Nadareski
acc2f30bd2 Fix most issues in new download code 2025-04-30 13:41:36 -04:00
Matt Nadareski
dc0675d8c6 Add cleanup to download functions 2025-04-30 12:03:06 -04:00
Matt Nadareski
33ce0e4024 Add initial extraction to new functions 2025-04-30 12:00:41 -04:00
Matt Nadareski
fa8fdac0ce Trailing commas caused issues 2025-04-30 11:49:11 -04:00
Matt Nadareski
a4d876d273 Fix elif 2025-04-30 11:39:36 -04:00
Matt Nadareski
3cc779ba77 Unify download maps 2025-04-30 11:38:36 -04:00
Matt Nadareski
5c8f4b9cac Smarter mapping of URLs to runtimes 2025-04-30 11:29:00 -04:00
Matt Nadareski
fb2b926986 Move function definitions 2025-04-30 11:02:58 -04:00
Matt Nadareski
3442a1f5b2 Add commented placeholders instead of omitting 2025-04-30 09:58:30 -04:00
Matt Nadareski
1fb9039985 Make more paths configurable 2025-04-30 09:54:49 -04:00
Matt Nadareski
0032066363 Add all separate downloads 2025-04-30 09:28:13 -04:00
Matt Nadareski
c1ed042190 Start defining all download URLs 2025-04-30 09:16:28 -04:00
Matt Nadareski
6c0f2415b7 Short-circuit CLI first-run experience 2025-04-30 09:02:36 -04:00
Matt Nadareski
ca898c4689 Always write full configuration file (fixes #839) 2025-04-30 08:55:37 -04:00
Matt Nadareski
3be4378b61 Normalize file path in CLI (fixes #826) 2025-04-30 08:53:20 -04:00
Deterous
c1641d7461 Allow max speed dumping (#838)
(0) in options
2025-04-19 08:16:05 -04:00
Deterous
aefb5a3778 Change redumper default commands (#837)
* Allow "0" drive speed

* Default speed 1 for unknown system

* default speed of 1

* No skeleton

* No base command by default

* Change speed in redumper tests

* Dump no longer dumping command

* Re-allow quick exiting always

* No base command is a dumping command

* Only one mode command per redumper call

* Proper capitalization for None

* Readd disc as default command mode
2025-04-18 09:05:33 -04:00
Deterous
4557017fc5 Quality of life fixes (#836)
* Account for N-type discs in Redumper processor

* MediaType prefix

* Delete after zipping, ask before closing during dump

* Proper closing function

* proper using

* nullable object

* YesNoCancel isn't a thing for CustomMessageBox

* Ctrl-Z
2025-04-17 11:37:32 -04:00
Deterous
d77e9c1612 Update redumper to build 549 (#835)
* Update redumper to build 549

* Add more redumper tests

* Add type

* Support .flip and .cache outputs

* Fix typo

* Fix test name

* Update tests

* Update tests again

* Don't check dmi/pfi/ss hashes against redump

* Fix incorrect flag

* Get last SCSI error count

* Fix test

* Fix test again

* Fix test finally

* Fix test final

* Review

* SACD doesn't need skeleton

* Proper SACD system handling

* Final final

* delete
2025-04-17 08:39:10 -04:00
bikerspade
455fb8228a Fix typo for L1Toolstamp!.Label (#828) 2025-02-27 12:02:15 -05:00
Matt Nadareski
679b40de7c Bump version 2025-01-03 11:44:35 -05:00
Matt Nadareski
fb2b1b7c54 Fix system detection logging 2025-01-03 09:52:42 -05:00
Matt Nadareski
ac649af511 Update to DIC 20250101 (fixes #807) 2025-01-01 22:40:00 -05:00
Matt Nadareski
4fb0e38f54 Handle SCSI error count (fixes #806) 2024-12-31 23:15:43 -05:00
Matt Nadareski
a84904e374 Fix misunderstanding on perfect offset 2024-12-31 21:53:14 -05:00
Matt Nadareski
7b8becfa40 Update RedumpLib to 1.6.4 (fixes #805) 2024-12-31 21:41:32 -05:00
Matt Nadareski
ba0fefca42 Fix short name test 2024-12-31 15:10:43 -05:00
Matt Nadareski
d7736055a0 Fix BinaryObjectScanner update 2024-12-31 13:45:25 -05:00
Matt Nadareski
8e37932b86 Add and use internal program short names 2024-12-31 13:43:57 -05:00
Matt Nadareski
d2c58d4c39 Update BinaryObjectScanner to 3.3.4 2024-12-31 13:35:04 -05:00
Matt Nadareski
34f3449231 Update copyright 2024-12-30 21:43:48 -05:00
Matt Nadareski
31ee29a716 Remove unnecessary action step 2024-12-30 21:42:14 -05:00
Matt Nadareski
c1f2fd5f19 Be consistent with filename extensions (fixes #804) 2024-12-29 14:12:04 -05:00
Matt Nadareski
172f9afbcf Update changelog 2024-12-28 23:03:10 -05:00
Deterous
19dd4f43e4 Minor UI changes to DIW (#803)
* Change DIW userinput ratio to 3:7

* Add groupboxes around contents and readonly tabs in DIW

* Use real PVD and PIC in debug data

* rearrange groupbox / stackpanel

* group boxes
2024-12-28 23:02:06 -05:00
Matt Nadareski
0ae7352c5e Update RedumpLib to 1.6.3 2024-12-28 13:59:59 -05:00
Matt Nadareski
e7c11d40ac Force showing tooltips on disabled items 2024-12-28 13:19:01 -05:00
Matt Nadareski
dccd78a01e Force path update if default changed 2024-12-28 13:09:35 -05:00
Deterous
fb05409c5d Minor UI and other fixes (#802)
* Wider DIW, rounded message boxes

* Don't log drive speed

* Change margins

* Allow changing DIW size

* Revert change to DIW sizing

* Change left column size in DIW

* Increase textbox ratio in UserInput

* Fix non-userinput margins

* Allow custom message box to move from image

* Change changelist

* PS4 pkg date is useless
2024-12-28 11:43:53 -05:00
Matt Nadareski
1c9b94341a Wrap some PhysicalTool method calls 2024-12-28 00:35:12 -05:00
Matt Nadareski
d3e208a332 Partially clean up PS3CFW 2024-12-27 22:44:06 -05:00
Deterous
6b8cfb4256 Add pure-getkey output names for PS3CFW (#801)
* Add pure-getkey output names for PS3CFW

* Don't assume . prefix for getkey.log

* zip CUE files generated by MultiMAN if they exist

* iso and cue can be case sensitive

* Add extra file to cfwps3 tests
2024-12-27 22:31:20 -05:00
Matt Nadareski
f60f26065b Don't add special field keys with no reason 2024-12-27 21:58:12 -05:00
Deterous
2ed14a7688 Improve PS4/PS5 parsing (#799)
* Improve PS4/PS5 parsing

* requested changes

---------

Co-authored-by: Matt Nadareski <mnadareski@outlook.com>
2024-12-27 21:29:33 -05:00
Matt Nadareski
071b91f414 Reenable XGD1 PVD reporting 2024-12-27 20:57:39 -05:00
Matt Nadareski
0ad99cb0f5 Hide empty partially matched list (fixes #800) 2024-12-27 19:58:07 -05:00
Deterous
e51aabddae Minor UI changes (#798)
* Minor changes to read-only field boxes

* re-add textwrapping to hashdata

* rounded border

* fix build

* fix border

* shift title buttons left

* Add fake column

* Make all of the title bar draggable

* Rounded all windows

* color all windows

* fix xml namespace

* Use 8px corner radius

* revert workflow change

* Changes changelist comment

* Remove toplevel grid

* Add dropshadow and scrollbar foreground color

* Increase border size, remove test scrollbar theme

* Give textboxes the custom scrollviewer style

* Remove test styles

* Revert incidental whitespace spaces

* dont revert intended change
2024-12-27 07:47:17 -05:00
Matt Nadareski
9390699114 Missed one 2024-12-25 21:44:17 -05:00
Matt Nadareski
93d2c2e8b0 Clean up unused disabled warnings 2024-12-25 21:42:10 -05:00
Matt Nadareski
50d969f08f Unify DisplayUserMessage definitions 2024-12-25 21:39:05 -05:00
Deterous
44e87948c7 Parse redumper BIG.DAT info (#797)
* Parse redumper BIG.DAT info

* Make protection comment bold

* Add tests for GetPlayStation2Protection
2024-12-24 10:41:47 -05:00
Deterous
dea011213a More fields behind RedumpCompatible option (#795)
* More fields behind RedumpCompatible option

* Dereference of a possibly null reference
2024-12-23 23:50:40 -05:00
Matt Nadareski
5b2500637b Update changelog 2024-12-23 23:20:02 -05:00
Deterous
82ede4dac2 Tab input only needed for ringcodes (#794) 2024-12-23 23:19:31 -05:00
Matt Nadareski
032ffe75f4 Do not display invalid credentials on empty 2024-12-23 20:23:25 -05:00
Matt Nadareski
ce9c6d1f52 Wrap log compression in a thread 2024-12-23 15:35:52 -05:00
Matt Nadareski
8be145127d Use Invoke explicitly for delegates 2024-12-23 10:25:50 -05:00
Matt Nadareski
0ce928349c Use internal options instead of external 2024-12-23 10:06:44 -05:00
Matt Nadareski
79e4f4a142 Pass options to process user info separately 2024-12-23 00:54:56 -05:00
Matt Nadareski
4bd8fc476c Avoid unnecessary null checks 2024-12-23 00:05:48 -05:00
Deterous
5b82cdacda Improve system detection (#792)
* Improve Sega system detection

* Fix namespace

* Ignore exception

* Add Sega CD header

* Improve system detection

* Amiga CD check

* Improve 3DO and Mac detection

* Remove unused parameter

* Try determine system even when drive inactive

* Better logic for inactive drives

* scope issues

* GameWave and VIS

* Fix Neo Geo CD system check

* use var

* put physicaltool behind try catch

* better use var
2024-12-22 20:26:47 -05:00
Matt Nadareski
4106a6261b Slightly increase nesting of file pre-dump checks 2024-12-21 23:23:03 -05:00
Matt Nadareski
6b006958a6 Slightly reduce nesting of file pre-dump checks 2024-12-21 23:16:31 -05:00
Deterous
068b92ebac Check for partial dumps (#791)
* Check for partial dumps

* fix bugs

* Change program checking order

* comments bad

* Adds CheckExistingFiles tests

* Correct test results
2024-12-21 23:01:16 -05:00
Matt Nadareski
4cf2b8a83d Allow check and IRD most of the time 2024-12-21 20:33:19 -05:00
Matt Nadareski
333043ccae Account for menu items for disable/enable 2024-12-21 20:30:23 -05:00
Matt Nadareski
0ff1753aa2 Disable all UI elements on protect scan 2024-12-21 13:33:12 -05:00
Matt Nadareski
9ad917e508 Remove unused progress bar (fixes #788) 2024-12-21 12:15:30 -05:00
Deterous
f3ac31ce1b More pendantic GUI changes (#790) 2024-12-21 11:19:28 -05:00
Deterous
7fdf671273 Pedantic GUI changes (#789) 2024-12-21 10:51:56 -05:00
Matt Nadareski
d5dee49f6b Adjust row count in protection options 2024-12-21 10:15:25 -05:00
Matt Nadareski
acdc13efa1 Enable tabs in input fields by default 2024-12-21 01:07:13 -05:00
Matt Nadareski
8446980919 Retire and replace blue secret text 2024-12-20 23:56:23 -05:00
Matt Nadareski
e5e7bf49fc Fix log line for default system use 2024-12-20 23:11:29 -05:00
Matt Nadareski
8b73ffc4a2 Ensure parameters checkbox is enabled to start 2024-12-20 22:38:08 -05:00
Matt Nadareski
ce6ed1e01b Disable more UI elements when editing 2024-12-20 22:33:24 -05:00
Matt Nadareski
bc0cd60fc1 Separate params checkbox from input 2024-12-20 22:31:10 -05:00
Matt Nadareski
17f850ecb2 Use IBM PC as default system out of the box 2024-12-20 22:07:56 -05:00
Matt Nadareski
ad8a623f16 Selectively rebuild program list 2024-12-20 21:46:03 -05:00
Matt Nadareski
b025898d7b Fix output name null edge case 2024-12-20 21:35:49 -05:00
Matt Nadareski
b604aefd5e Simplify output name assembly logic 2024-12-20 21:30:31 -05:00
Matt Nadareski
1656b50029 Add README files for two libraries 2024-12-20 12:12:47 -05:00
Matt Nadareski
572b8cfa6b Remove reference to removed file 2024-12-20 11:55:42 -05:00
Matt Nadareski
c5cd623e75 Remove vestigial configuration file 2024-12-20 11:51:56 -05:00
Matt Nadareski
ff2dbd7b81 Fix important typo 2024-12-20 11:37:34 -05:00
Matt Nadareski
19cedfc5e8 Allow symbols to be packed 2024-12-20 11:20:09 -05:00
Matt Nadareski
3df93fb59d Preserve program metadata on publish 2024-12-20 11:02:33 -05:00
Matt Nadareski
62ffd3f6d3 Include native libraries for self-extract 2024-12-20 10:54:54 -05:00
Matt Nadareski
69994774f5 Update changelog 2024-12-20 10:12:30 -05:00
Matt Nadareski
3a1fc93aac Better SecuROM handling for DIC and Redumper 2024-12-20 10:11:23 -05:00
Matt Nadareski
6f8d27b9c4 Fix now-incorrect test values 2024-12-19 14:03:16 -05:00
Matt Nadareski
42680b9915 Use simpler cuesheet reader in Redumper 2024-12-19 13:57:48 -05:00
Matt Nadareski
9033f3a798 Minor formatting tweaks; add TODO 2024-12-19 12:08:19 -05:00
Matt Nadareski
717590bf41 Remove use of psxt001 2024-12-19 12:04:13 -05:00
Matt Nadareski
4f48ccc4c6 Ensure .NET versions are installed for testing 2024-12-19 10:55:31 -05:00
Deterous
ca59d71e7d Print PS4/PS5 app.pkg info (#785)
* Print PS4/PS5 app.pkg info

* namespace

* Use a filestream

* Use appPkgHeaderDeserializer obj

* null check
2024-12-19 10:23:50 -05:00
Matt Nadareski
9207627fb0 Introduce maximum log length 2024-12-19 00:49:41 -05:00
Matt Nadareski
216c6de970 Fully remove processing queue code 2024-12-19 00:37:56 -05:00
Matt Nadareski
cebbeb264f (EXPERIMENTAL) Skip log line queue 2024-12-19 00:13:09 -05:00
Matt Nadareski
e971ee8550 Use explicit InvokeAsync when possible 2024-12-18 23:30:14 -05:00
Matt Nadareski
76029d02ea Simplify ProcessLogLine method 2024-12-18 23:21:58 -05:00
Matt Nadareski
0cdf2fc520 Fix access level for log document 2024-12-18 23:20:28 -05:00
Matt Nadareski
5ac0055e16 Fix access level for log queue 2024-12-18 23:18:02 -05:00
Matt Nadareski
7b7e8912a5 Remove non-working last line log code 2024-12-18 23:16:14 -05:00
Matt Nadareski
e055572c67 Remove vestigial progress indicators 2024-12-18 23:00:45 -05:00
Matt Nadareski
c4d981350f Simplify direct package references 2024-12-18 22:55:53 -05:00
Matt Nadareski
5035f2dcc2 Update packages 2024-12-18 22:13:58 -05:00
Deterous
7c41fe0933 Verify SecuROM sector count (#784)
* Verify securom sector count

* Make securom enum internal

* Fix bugs

* Don't set explicit var in test

* Deal with review

* Fix mistake

* Oops

* Proper DIC securom sector count

* fix names
2024-12-18 09:37:00 -05:00
Deterous
2b4fa33e81 Use proper base path for redumper output check (#783) 2024-12-17 00:05:13 -05:00
Deterous
e8f50a84b8 Enable handling non-SS Xbox discs (#782)
* Enable handling non-SS Xbox discs

* Fix manufacturer size
2024-12-16 23:09:36 -05:00
Matt Nadareski
7c0923881e Update BOS to 3.3.2 2024-12-14 23:05:57 -05:00
Matt Nadareski
eeacb7b3b2 Slight formatting tweaks 2024-12-14 20:48:28 -05:00
Deterous
d3039af3e1 Fix bug with volume labels (#781) 2024-12-14 20:31:44 -05:00
Deterous
eaa77dbeed Better deal with volume labels (#780)
* Better deal with volume labels

* Add missing using

* use nullable string

* Deal with review comments

* Fix issues

* Better deal with Xbox/Xbox360 labels

* Use FirstOrDefault

* Add todo
2024-12-14 19:58:09 -05:00
Deterous
55696f1f47 Add DumpingParameters for DIC and Redumper (#778)
* DumpingParameters for DIC and Redumper

* Update changelog

* fix typo

* Fix test

---------

Co-authored-by: Matt Nadareski <mnadareski@outlook.com>
2024-12-12 11:41:48 -05:00
Matt Nadareski
7d22341d2f Fix issue with odd quoting (fixes #779) 2024-12-12 11:35:54 -05:00
Matt Nadareski
53c6e47fa8 Simplify prefix filtering 2024-12-11 03:57:25 -05:00
Matt Nadareski
7b7da4db1f Add packer filtering tests 2024-12-10 14:51:09 -05:00
Matt Nadareski
b9d8e0c125 Always omit EA CD-Key 2024-12-10 14:11:48 -05:00
Matt Nadareski
1e2f1280be Remove now-redundant option 2024-12-10 13:07:51 -05:00
Matt Nadareski
3339ff1c14 Always filter out game engines and packers 2024-12-10 12:00:10 -05:00
Matt Nadareski
1e729459a6 Remove unnecessary re-splitting of path (fixes #772) 2024-12-07 22:08:26 -05:00
Matt Nadareski
2d547bd67f Add launch config for CLI 2024-12-07 21:45:06 -05:00
Matt Nadareski
2bb854e7a5 Add safety check around empty config files 2024-12-06 23:58:21 -05:00
Matt Nadareski
b8e923207f Add alternate config location (fixes #768) 2024-12-06 23:55:27 -05:00
Matt Nadareski
3d96379aba Remove unused gated using statement 2024-12-06 20:58:40 -05:00
Matt Nadareski
8c5437983a Update README to remove AppVeyor references 2024-12-06 20:36:15 -05:00
Matt Nadareski
7cad841f39 Migrate to using publish script for GHA 2024-12-06 15:59:46 -05:00
Matt Nadareski
b4e04314a7 Add gated program downloads to publish scripts 2024-12-06 15:56:13 -05:00
Matt Nadareski
f1506b284c Add debug flag to publish scripts 2024-12-06 14:56:50 -05:00
Matt Nadareski
3fde773018 Clean up original Drive tests 2024-12-06 00:45:41 -05:00
Matt Nadareski
2fb8613a85 Remove unnecessary namespace prefixes 2024-12-06 00:35:25 -05:00
Matt Nadareski
94d59242b1 Update protection tool tests 2024-12-06 00:27:39 -05:00
Matt Nadareski
09179810ee Add FrontendTool tests 2024-12-05 23:49:24 -05:00
Matt Nadareski
2061af06c1 Ensure Redumper support matrix is consistent 2024-12-05 23:00:28 -05:00
Matt Nadareski
075a95ef4c Ensure consistency in frontend code 2024-12-05 22:45:53 -05:00
Matt Nadareski
c8a402ee64 Update RedumpLib to 1.6.1 2024-12-05 22:19:23 -05:00
Matt Nadareski
a5d7b218b7 Add non-tools frontend tests 2024-12-05 21:51:25 -05:00
Matt Nadareski
ebb83a6a1e Unify queue handling in processing queue 2024-12-05 21:40:53 -05:00
Matt Nadareski
0c327872b5 Increase sleep time in queue 2024-12-05 21:08:15 -05:00
Matt Nadareski
6ef9debac4 Add tests around default values 2024-12-05 16:39:43 -05:00
Matt Nadareski
10c7906e9d Ensure dumping commands are tested 2024-12-05 15:37:36 -05:00
Matt Nadareski
b922857578 Fix required Int32 array input 2024-12-05 15:21:52 -05:00
Matt Nadareski
dca5235a4a Wire up input boundaries 2024-12-05 15:19:38 -05:00
Matt Nadareski
07e747f7d3 Add Int32 array input type 2024-12-05 15:14:20 -05:00
Matt Nadareski
da67c58727 Add unused min/max parameters for numeric inputs 2024-12-05 15:05:54 -05:00
Matt Nadareski
0524ad7ac2 Add DIC parameters tests 2024-12-05 15:02:08 -05:00
Matt Nadareski
2a16be2c07 Replace Redumper flag values with input types 2024-12-05 13:29:51 -05:00
Matt Nadareski
a5a048fcb0 Add quotes option to string input 2024-12-05 13:22:17 -05:00
Matt Nadareski
b2ffa2025b Add Redumper parameters tests 2024-12-05 12:32:48 -05:00
Matt Nadareski
0d11e03507 Replace Aaru flag values with input types 2024-12-05 11:59:59 -05:00
Matt Nadareski
c72aeb7f58 Add Aaru parameters tests 2024-12-05 10:53:42 -05:00
Matt Nadareski
86eca8dee7 Use string builders where possible 2024-12-05 00:23:35 -05:00
Matt Nadareski
5e5360441a Replace Aaru pre-command flags 2024-12-05 00:06:24 -05:00
Matt Nadareski
5392afbf06 Fix some formatting issues with Input types 2024-12-04 23:08:17 -05:00
Matt Nadareski
c7c700e567 Let inputs read equal-separated values 2024-12-04 22:14:06 -05:00
Matt Nadareski
8c0bbfbf10 Handle issue with old .NET 2024-12-04 21:27:49 -05:00
Matt Nadareski
e7baa7d33f Add self-formatting to Input types 2024-12-04 21:20:38 -05:00
Matt Nadareski
724b1b6719 Add unused inputs to Aaru 2024-12-04 21:00:40 -05:00
Matt Nadareski
ab88f8ffaa Add flag for value being set 2024-12-04 20:30:21 -05:00
Matt Nadareski
6feda0e69d Split Input type into typed classes 2024-12-04 20:22:47 -05:00
Matt Nadareski
d704cec97b Add currently unused Input type 2024-12-04 19:28:34 -05:00
Matt Nadareski
9fe815676f Add BaseExecutionContext tests 2024-12-04 15:57:33 -05:00
Matt Nadareski
129a7ba63f Add tests around ProcessingTool 2024-12-04 14:53:07 -05:00
Matt Nadareski
4617401e8b Use recommended Min/Max/TryParse 2024-12-04 13:12:22 -05:00
Matt Nadareski
3cb037b5af Add safety around sidecar generation 2024-12-04 13:08:44 -05:00
Matt Nadareski
49b70b7ae1 Add tests around DIC helpers 2024-12-04 12:55:05 -05:00
Matt Nadareski
4eb346632a Add sanity check output file tests 2024-12-03 23:29:51 -05:00
Matt Nadareski
56fffb0ed9 Add tests around Redumper helpers 2024-12-03 23:06:54 -05:00
Matt Nadareski
8adde6f8d5 Fix missing test data 2024-12-03 21:10:22 -05:00
Matt Nadareski
eacd60562c Enable test running on package and PR 2024-12-03 20:58:40 -05:00
Matt Nadareski
f6df7e96c1 Add tests around PS3 CFW helpers 2024-12-03 20:55:28 -05:00
Matt Nadareski
0e37025150 Add tests around UIC helpers 2024-12-03 20:46:05 -05:00
Matt Nadareski
4e338d56cd Perform better path emptiness checks 2024-12-03 20:30:15 -05:00
Matt Nadareski
c26253007d Fix failing XBC test 2024-12-03 20:26:50 -05:00
Matt Nadareski
66b4bb4eb2 Add tests around XBC helpers 2024-12-03 20:20:18 -05:00
Matt Nadareski
034494558e Fix CleanRip test access 2024-12-03 16:55:40 -05:00
Matt Nadareski
cc7116a623 Add tests around CleanRip helpers 2024-12-03 16:55:14 -05:00
Matt Nadareski
011ca670e0 Add tests around some Aaru helpers 2024-12-03 16:29:50 -05:00
Matt Nadareski
956d8efcf7 Update tests to account for new file count 2024-12-03 15:45:28 -05:00
Matt Nadareski
ee4e77b208 Ensure SS.bin only required for DVD 2024-12-03 15:42:39 -05:00
Matt Nadareski
8eff11f0ae Add processor base implementation tests 2024-12-03 15:38:59 -05:00
Matt Nadareski
8d4f22a1c4 Add BaseProcessor tests 2024-12-03 14:04:59 -05:00
Matt Nadareski
3829ee1bbe Move existing tests to new projects 2024-12-03 13:27:55 -05:00
Matt Nadareski
61060e98df Add skeleton test projects for individual libraries 2024-12-03 13:22:55 -05:00
Matt Nadareski
7f061f22e0 Update packages 2024-12-03 13:20:05 -05:00
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
248 changed files with 21527 additions and 8143 deletions

View File

@@ -25,7 +25,7 @@ What version are you using?
**Build**
What runtime version are you using?
- [ ] .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.

40
.github/workflows/build_and_test.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Build and Test
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: |
6.0.x
8.0.x
9.0.x
- name: Run tests
run: dotnet test
- name: Run publish script
run: ./publish-nix.sh -dp
- name: Upload to rolling
uses: ncipollo/release-action@v1.14.0
with:
allowUpdates: True
artifacts: "*.nupkg,*.snupkg,*.zip"
body: 'Last built commit: ${{ github.sha }}'
name: 'Rolling Release'
prerelease: True
replacesArtifacts: True
tag: "rolling"
updateOnlyUnreleased: True

View File

@@ -1,53 +0,0 @@
name: MPF Check
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
project: [MPF.Check]
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, osx-arm64]
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
conf: [Debug] #[Release, Debug]
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ 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')) && '-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

View File

@@ -1,53 +0,0 @@
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: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
conf: [Debug] #[Release, Debug]
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ 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')) && '-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

View File

@@ -1,61 +0,0 @@
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: 8.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

@@ -1,67 +0,0 @@
name: MPF UI
on:
push:
branches: [ "master" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
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]
conf: [Debug] #[Release, Debug]
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c ${{ 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')) && '-p:PublishSingleFile=true' || ''}}
- name: Bundle DiscImageCreator
run: |
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_416/redumper-2024.10.12_build416-win64.zip
unzip redumper-2024.10.12_build416-win64.zip
mkdir -p MPF.UI/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper
mv redumper-2024.10.12_build416-win64/bin/redumper.exe MPF.UI/bin/${{ matrix.conf }}/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper/
- 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

View File

@@ -11,13 +11,16 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
dotnet-version: |
6.0.x
8.0.x
9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
run: dotnet build
- name: Test
run: dotnet test --no-restore --verbosity normal
run: dotnet test

1
.gitignore vendored
View File

@@ -68,7 +68,6 @@ artifacts/
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds

18
.vscode/launch.json vendored
View File

@@ -5,12 +5,12 @@
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"name": ".NET Core Launch (Check)",
"type": "coreclr",
"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
@@ -18,6 +18,20 @@
"stopAtEntry": false,
"justMyCode": false
},
{
"name": ".NET Core Launch (CLI)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/MPF.CLI/bin/Debug/net9.0/MPF.CLI.dll",
"args": [],
"cwd": "${workspaceFolder}/MPF.CLI",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false,
"justMyCode": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",

View File

@@ -1,3 +1,357 @@
### 3.4.0 (2025-09-25)
- Remove media type from Check Dump UI
- Always trust the output files for processing
- UI consistency when parameters are editable
- Let the processor always deal with unsupported
- Put a try/catch around GenerateArtifacts
- Reduce preprocessing in DumpEnvironment
- Update RedumpLib to 1.6.9
- Treat all UMD as DL visually
- Add CopyUpdateUrlToClipboard option
- Fix inconsistency with newlines
- Update Aaru to build 5.4.1
- Fix for C2 error doubling issue (fuzz6001)
- Update RedumpLib and LibIRD
- Update LibIRD
- Update redumper to build 653
- Minor cleanup
- Update test Nuget packages
- Update RedumpLib to 1.7.1
- Support multisession cache files
- Update DIC to 20250901 (Windows/Linux only)
- Fix UIC processing logic
- Update RedumpLib, detect Playmaji Polymega system
- Update redumper to b655
- Support new versioning format in redumper (fuzz6001)
- Add RedumperRefineSectorMode setting
- Update BinaryObjectScanner to 3.4.0
- Add context-sensitive protections helper method
- Further flesh out framework
- Add filters to handle Release Control output (Bestest)
- Cleanup last commits, add tests
- Update BinaryObjectScanner to 3.4.2
- Update RedumpLib to 1.7.4
### 3.3.3 (2025-07-18)
- Handle layers for PS3CFW
- Further clarify configuration requirements for CLI
- Ignore volume labels with path separators
- Reduce media-specific checks where unnecessary
- Add DetermineMediaType scaffolding and tests
- Fill out DetermineMediaType for Redumper
- Fill out DetermineMediaType for Aaru
- Fill out DetermineMediaType for DiscImageCreator
- Enable Check to determine media type automatically
- Fix missed test updates
- Rename disc info to media info
- Fix start index in Check
- Use the correct base path for Check
- Update redumper to build 611
- IsAudio cleanup
- IsAudio cleanup cleanup
- Remove dead code in DIC processor
- Set some default values for CLI
- Address nullable default value
- Better handling of Xbox/360, redumper build 613
- Use reasonable default names for PlayStation systems
- Swap PS1/2 back to original name handling
- Only use serial for PS3/4/5 if no custom volume label
- Fix unnecessary null assignment
- Empty should be null (fuzz6001)
- Remove legacy codes from info window
- Net Yaroze only for PS1
- Update RedumpLib to 1.6.8
- Add new help and version flags for CLI and Check
- Strip errant whitespace during path normalization
- Simplify code from previous commit
- Better handle mixed-separator paths
- Slightly better IRD organization
- Update redumper to build 631
### 3.3.2 (2025-06-12)
- Include Aaru in automatic builds
- Update RedumpLib to 1.6.7
- Update redumper, DriveType option, SS sanity check
- Fix issues with last commit
- Update tooltips/labels for controversial options
- Add currently-hidden RetrieveMatchInformation option
- Fix Redump default options
- Fix tests from last commit
- Remove unnecessary options property
- Decouple retieval from login
- Simplify Options naming where possible
- Create multi-size icon
- Fix size retrieval for Aaru
- Update redumper to build 610
### 3.3.1 (2025-05-23)
- Fix typo for L1Toolstamp!.Label (bikerspade)
- Update redumper to build 549
- Allow max speed dumping ("0")
- Normalize file path in CLI
- Always write full configuration file
- Short-circuit CLI first-run experience
- Start defining all download URLs
- Add all separate downloads
- Make more paths configurable
- Add commented placeholders instead of omitting
- Move function definitions
- Smarter mapping of URLs to runtimes
- Unify download maps
- Trailing commas caused issues
- Add initial extraction to new functions
- Add cleanup to download functions
- Fix most issues in new download code
- Fix issue with some downloads
- Replace old download implementations
- Enable packing programs for CLI
- Fix minor packing issues
- Comment download scripts more
- Fix removing empty subdirectories
- Add interactive mode to CLI
- Add padding above root interactive node label
- Clean up case-sensitivity
- Add interactive mode to Check
- Fix menu title in Check interactive
- It's not dumping, it's checking
- Make interactive mode cleaner
- Avoid redundant redundancy
- Allow exiting from interactive modes
- Update RedumpLib to 1.6.5
- Fix package reference layout
- Update Nuget packages
- Normalize Shift-JIS characters, when possible
- Update to DIC 20250501
- Update RedumpLib to 1.6.6
- Pre-clean file path in CLI interactive mode
- Tweak to previous commit for some build versions
- Fix lines wiped before displayed
- Update redumper to build 565
- Close the log archive, if it exists
- Support checking Redumper DAT from zip; add tests
- Add Check warnings around overwriting and parsing
- Clarification on some options
- One more note about hidden settings
- Explicitly unset Redumper speed, if needed
- Simplify WriteOutputData path handling
- Serialize JSON a slightly different way
- Add newlines to pkg info printing
- Better audio-only check
- Minor cleanup from last commit
- Relax PS3CFW output filename requirements
- Fix bracket formatting from last commit
- Fix speed dropdown with Redumper
- Update redumper to build 585
- Further fix speed dropdown with Redumper
- Use null for 0 speed internally
- Don't hash bad SS
- Add and use status label for Check window
- Minor Check UI improvements
- Reset Redump errors when an INFO block is found
- Handle error count resets in Redumper
- Remove unnecessary conditional
### 3.3.0 (2025-01-03)
- Update packages
- Add skeleton test projects for individual libraries
- Move existing tests to new projects
- Add BaseProcessor tests
- Add processor base implementation tests
- Ensure SS.bin only required for DVD
- Update tests to account for new file count
- Add tests around some Aaru helpers
- Add tests around CleanRip helpers
- Fix CleanRip test access
- Add tests around XBC helpers
- Fix failing XBC test
- Perform better path emptiness checks
- Add tests around UIC helpers
- Add tests around PS3 CFW helpers
- Enable test running on package and PR
- Fix missing test data
- Add tests around Redumper helpers
- Add sanity check output file tests
- Add tests around DIC helpers
- Add safety around sidecar generation
- Use recommended Min/Max/TryParse
- Add tests around ProcessingTool
- Add BaseExecutionContext tests
- Add currently unused Input type
- Split Input type into typed classes
- Add flag for value being set
- Add unused inputs to Aaru
- Add self-formatting to Input types
- Handle issue with old .NET
- Let inputs read equal-separated values
- Fix some formatting issues with Input types
- Replace Aaru pre-command flags
- Use string builders where possible
- Add Aaru parameters tests
- Replace Aaru flag values with input types
- Add Redumper parameters tests
- Add quotes option to string input
- Replace Redumper flag values with input types
- Add DIC parameters tests
- Add unused min/max parameters for numeric inputs
- Add Int32 array input type
- Wire up input boundaries
- Fix required Int32 array input
- Ensure dumping commands are tested
- Add tests around default values
- Increase sleep time in queue
- Unify queue handling in processing queue
- Add non-tools frontend tests
- Update RedumpLib to 1.6.1
- Ensure consistency in frontend code
- Ensure Redumper support matrix is consistent
- Add FrontendTool tests
- Update protection tool tests
- Remove unnecessary namespace prefixes
- Clean up original Drive tests
- Add debug flag to publish scripts
- Add gated program downloads to publish scripts
- Migrate to using publish script for GHA
- Update README to remove AppVeyor references
- Remove unused gated using statement
- Add alternate config location
- Add safety check around empty config files
- Add launch config for CLI
- Remove unnecessary re-splitting of path
- Always filter out game engines and packers
- Remove now-redundant option
- Always omit EA CD-Key
- Add packer filtering tests
- Simplify prefix filtering
- Fix issue with odd quoting
- Add DumpingParameters for DIC and Redumper
- Better deal with volume labels
- Fix bug with volume labels
- Slight formatting tweaks
- Update BOS to 3.3.2
- Enable handling non-SS Xbox discs
- Use proper base path for redumper output check
- Verify securom sector count
- Update packages
- Simplify direct package references
- Remove vestigial progress indicators
- Remove non-working last line log code
- Fix access level for log queue
- Fix access level for log document
- Simplify ProcessLogLine method
- Use explicit InvokeAsync when possible
- (EXPERIMENTAL) Skip log line queue
- Fully remove processing queue code
- Introduce maximum log length
- Print PS4/PS5 app.pkg info
- Ensure .NET versions are installed for testing
- Remove use of psxt001
- Minor formatting tweaks; add TODO
- Use simpler cuesheet reader in Redumper
- Fix now-incorrect test values
- Better SecuROM handling for DIC and Redumper
- Include native libraries for self-extract
- Preserve program metadata on publish
- Allow symbols to be packed
- Fix important typo
- Remove vestigial configuration file
- Remove reference to removed file
- Add README files for two libraries
- Simplify output name assembly logic
- Fix output name null edge case
- Selectively rebuild program list
- Use IBM PC as default system out of the box
- Separate params checkbox from input
- Disable more UI elements when editing
- Ensure parameters checkbox is enabled to start
- Fix log line for default system use
- Retire and replace blue secret text
- Enable tabs in input fields by default
- Adjust row count in protection options
- Pedantic GUI changes
- More pendantic GUI changes
- Remove unused progress bar
- Disable all UI elements on protect scan
- Account for menu items for disable/enable
- Allow check and IRD most of the time
- Check for partial dumps
- Slightly reduce nesting of file pre-dump checks
- Slightly increase nesting of file pre-dump checks
- Improve system detection
- Avoid unnecessary null checks
- Pass options to process user info separately
- Use internal options instead of external
- Use Invoke explicitly for delegates
- Wrap log compression in a thread
- Do not display invalid credentials on empty
- Tab input only needed for ringcodes
- More fields behind RedumpCompatible option
- Parse redumper BIG.DAT info
- Unify DisplayUserMessage definitions
- Clean up unused disabled warnings
- Missed one
- Minor UI changes
- Hide empty partially matched list
- Reenable XGD1 PVD reporting
- Improve PS4/PS5 parsing
- Don't add special field keys with no reason
- Add pure-getkey output names for PS3CFW
- Partially clean up PS3CFW
- Wrap some PhysicalTool method calls
- Minor UI and other fixes
- Force path update if default changed
- Force showing tooltips on disabled items
- Update RedumpLib to 1.6.3
- Minor UI changes to DIW
- Be consistent with filename extensions
- Remove unnecessary action step
- Update copyright
- Update BinaryObjectScanner to 3.3.4
- Add and use internal program short names
- Fix BinaryObjectScanner update
- Fix short name test
- Update RedumpLib to 1.6.4
- Fix misunderstanding on perfect offset
- Handle SCSI error count
- Update to DIC 20250101
- Fix system detection logging
### 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
@@ -161,7 +515,7 @@
- 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)
- Enable loading seed JSON
### 3.2.0 (2024-06-20)
@@ -237,7 +591,7 @@
- Clean up usings
- Remove automatic eject and reset options
- Remove options from UI
- Remove firmware output for Redumper (Deterous)
- Remove firmware output for Redumper
- Merge EnumConverter and EnumExtensions
- Move event args to root of Core
- Move processing queue to root of Core
@@ -277,7 +631,7 @@
- Rename main application to MPF.UI
- Fix build scripts
- Make protection file output required
- Standardize PS1-5 outputs and parsing (Deterous)
- Standardize PS1-5 outputs and parsing
- Update Redumper to build 371
- Tools always run in separate window
- Move ConsoleLogger to Check CLI
@@ -353,22 +707,22 @@
### 3.1.9 (2024-05-19)
- Update Redumper to build 325
- Fix CleanRip not pulling info (Deterous)
- Fix XboxOne/XboxSX Filename bug (Deterous)
- Trim PIC for XboxOne/XboxSX (Deterous)
- Fix CleanRip not pulling info
- Fix XboxOne/XboxSX Filename bug
- Trim PIC for XboxOne/XboxSX
- Get volume label from UIC outputs
- Add site code listing to Check
- Update RedumpLib and related
- Update BinaryObjectScanner to 3.1.11
- Remove now-unused Hash enum
- Use IO implementation of IniFile
- Add Xbox Backup Creator support to MPF.Check (Deterous)
- Add Xbox Backup Creator support to MPF.Check
- Update BinaryObjectScanner to 3.1.12
- Prefer PlayStation info from Redumper logs (Deterous)
- Prefer PlayStation info from Redumper logs
### 3.1.8 (2024-05-09)
- Option for default Redumper leadin retries (Deterous)
- Option for default Redumper leadin retries
- Omit false positives on formatting protections
- Critical update to BinaryObjectScanner 3.1.10
- Add _PFI.bin support for UIC
@@ -379,9 +733,9 @@
### 3.1.6 (2024-04-27)
- Fix parameter parsing for `=` symbol (Deterous)
- Define better default categories (Deterous)
- Custom non-redump Redumper options (Deterous)
- Fix parameter parsing for `=` symbol
- Define better default categories
- Custom non-redump Redumper options
- Update packages
- Update packages
@@ -398,10 +752,10 @@
- Fix information pulling for redumper (fuzz6001)
- Update packages
- Update BinaryObjectScanner to 3.1.4
- Detect Xbox Series X discs (Deterous)
- Detect Xbox Series X discs
- Enable Windows targeting for test project
- Fix test project project includes
- Fix CleanRip hash output for Check (Deterous)
- Fix CleanRip hash output for Check
- Enable label-side mastering SID and toolstamp
- Enable remaining fields for label-side information
- Update BinaryObjectScanner to 3.1.5
@@ -418,38 +772,38 @@
- Remove GHA pull request builds
- Add PR check workflow
- Don't link to AppVeyor artifacts page anymore
- Add PS3 CFW support to MPF.Check (Deterous)
- Hide size if value is 0 (Deterous)
- Fix title normalization (Deterous)
- Add PS3 CFW support to MPF.Check
- Hide size if value is 0
- Fix title normalization
- Ensure no labels are empty
- Use SabreTools.Hashing
- Update to SabreTools.RedumpLib 1.3.5
- Update packages to latest
- Enable LibIRD for all .NET frameworks (Deterous)
- Enable LibIRD for all .NET frameworks
- Try updating PR check action
- Fix config access persmission (Deterous)
- Fix Check UI deadlock (Deterous)
- Fix config access persmission
- Fix Check UI deadlock
- Fix formatting output formatting
- Update LibIRD to 0.9.0 (Deterous)
- Update LibIRD to 0.9.0
- Update packages
- Fix Redumper generic drive type (Deterous)
- Add MPF version to Submission info (Deterous)
- Fix Redumper generic drive type
- Add MPF version to Submission info
- Update to RedumpLib 1.3.6
### 3.1.2 (2024-02-27)
- Remove debugging lines from build script
- Port build script fixes from BOS
- Fix double git hash version (feat. Deterous)
- Fix double git hash version
- Readd x86 builds by default
- Hide unavailable dumping programs (Deterous)
- Hide unavailable dumping programs
- Remove DIC and Aaru bundles from CI
- Add x86 builds to AppVeyor
- Make AppVeyor builds framework-dependent
- Fix misattributed artifact
- Update README with current build instructions
- Opt-in automatic IRD creation after PS3 dump (Deterous)
- Add CI via Github Workflows (Deterous)
- Opt-in automatic IRD creation after PS3 dump
- Add CI via Github Workflows
- Reorganize solution items
- Split CI workflow files
- Add GHA CI status badges
@@ -479,19 +833,19 @@
- Show hashes in readonly data
- Update to BinaryObjectScanner 3.1.0
- Add Mattel HyperScan detection
- Pull PS3 Disc Key from redump (Deterous)
- Pull PS3 Disc Key from redump
### 3.1.1 (2024-02-20)
- Remove .NET 6 from auto-builds
- Make Redumper the default for new users
- Fix DIC log parsing for SS version (Deterous)
- Fix DIC log parsing for SS version
- Write outputs with UTF-8
- Add funworld Photo Play detection
- Fix Aaru drive parameter generation
- Limit DVD protection outputs
- Add a GUI for PS3 IRD Creation (Deterous)
- Update LibIRD, disable UI elements when creating IRD (Deterous)
- Add a GUI for PS3 IRD Creation
- Update LibIRD, disable UI elements when creating IRD
### 3.1.0 (2024-02-06)
@@ -499,33 +853,33 @@
- Update Redumper to build 294
- Fix commented out code
- Make missing hash data clearer
- Get BD PIC Identifier for redumper (Deterous)
- Support redumper skeleton and hash files (Deterous)
- Get BD PIC Identifier for redumper
- Support redumper skeleton and hash files
- Support ringcode and PIC for triple/quad-layer (fuzz6001)
- Cleanup !protectionInfo.txt (Deterous)
- Update Redumper to build 311 (Deterous)
- Use PSX/PS2 serial as filename when Volume Label not present (Deterous)
- Allow variables in output path (Deterous)
- Check for presence of complete dump from other programs (Deterous)
- Retrieve volume label from logs (Deterous)
- Cleanup !protectionInfo.txt
- Update Redumper to build 311
- Use PSX/PS2 serial as filename when Volume Label not present
- Allow variables in output path
- Check for presence of complete dump from other programs
- Retrieve volume label from logs
- Correct missing space in PVD (fuzz6001)
- Prevent crashing on invalid parameters (Deterous)
- Detect CDTV discs (Deterous)
- Differentiate CD32 from CDTV (Deterous)
- Normalise Disc Titles in Submission Info (Deterous)
- Prevent crashing on invalid parameters
- Detect CDTV discs
- Differentiate CD32 from CDTV
- Normalise Disc Titles in Submission Info
- Skip warning line in Redumper log
- Add a GUI for MPF.Check (Deterous)
- Add a GUI for MPF.Check
- Fix information pulling for CleanRip and UIC
- Add UMD handling for the disc info window
- Detect Photo CD
- Parse PSX/PS2/KP2 exe date from logs (Deterous)
- Exclude extra tracks when finding disc matches (Deterous)
- Verbose Redumper log by default (Deterous)
- Parse PSX/PS2/KP2 exe date from logs
- Exclude extra tracks when finding disc matches
- Verbose Redumper log by default
- Retrieve serial from Cleanrip
- Fix build from rushed code
- Remove `-disc2` from Cleanrip serial
- Enable Windows builds on Linux and Mac
- Fix compiler warning (Deterous)
- Fix compiler warning
### 3.0.3 (2023-12-04)
@@ -613,15 +967,15 @@
### 2.7.5 (2023-11-06)
- Remove psxt001z Pkg Ref in MPF.Test (Deterous)
- Remove psxt001z Pkg Ref in MPF.Test
- Update Redumper to build 247
- Focus main window after child window closes (Deterous)
- Focus main window after child window closes
- Try to get PS3 data from SFB
- Fix PS3 version finding
- Pull PS3 Firmware Version (Deterous)
- Pull PS3 Firmware Version
- Fix default layerbreak output
- Enable browsing for Redumper path (Deterous)
- Update to MMI 3.0.0 (Deterous)
- Enable browsing for Redumper path
- Update to MMI 3.0.0
### 2.7.4 (2023-10-31)
@@ -642,7 +996,7 @@
- Compile most regex statements
- Handle a couple of messages
- Remove .manufacturer for Bluray
- Fix typo that disables DIC during media check (Deterous)
- Fix typo that disables DIC during media check
- Fix build
- Remove duplicate check
- Add PIC output for Redumper
@@ -671,7 +1025,7 @@
- Add filename suffix setting (nw)
- Wire through filename suffix
- Expose suffix setting
- Set UDF CD threshold at 800MB (Deterous)
- Set UDF CD threshold at 800MB
- Update package versions
- Attempt to parse out PS5 params.json
- Fix CRC32 hashing
@@ -680,7 +1034,7 @@
- Update to MMI 3.0.0-preview.4
- Fix two small nullability issues
- Use Array.Empty in hasher
- Always use relative path internally (Deterous)
- Always use relative path internally
### 2.7.2 (2023-10-17)
@@ -688,9 +1042,9 @@
- Cleanup and gated code
- Gate some switch expressions
- Suppress some unnecessary messages
- Disable dumping button when Redumper selected with unsupported media type (Deterous)
- Improve check for which program supports which media (Deterous)
- Remove code for getting Universal Hash from DIC logs (Deterous)
- Disable dumping button when Redumper selected with unsupported media type
- Improve check for which program supports which media
- Remove code for getting Universal Hash from DIC logs
- Fix Redumper retry count not showing
- Enable parameters checkbox by default
- Update Redumper to build 230
@@ -710,7 +1064,7 @@
- Unify handling of enable/disable events
- Enable nullability in Check
- Remove all but .NET 6 for AppVeyor packaging
- Place message boxes at center of main window (Deterous)
- Place message boxes at center of main window
- Enable nullability in MPF
- Enable nullability in MPF.UI.Core
- Enable nullability in MPF.Test
@@ -746,7 +1100,7 @@
- Be smarter about media type based on system
- Consolidate into MPF.Core
- Fix failing tests
- Remove debug symbols in release builds (Deterous)
- Remove debug symbols in release builds
- Allow nullability for modern .NET
- Fix failing tests
- Remove unnecessary include
@@ -754,7 +1108,7 @@
- Move LogLevel enumeration
- Split logging code a bit more
- Split info window code a bit more
- Clarify build instructions in README (Deterous)
- Clarify build instructions in README
- Split options window code a bit more
- Use binding for more disc info textboxes
- Handle Redump password changing better
@@ -780,7 +1134,7 @@
- Fix media type ordering
- Fix dumping button not enabling
- Recentralize some Check functionality
- Fix media combobox not updating (Deterous)
- Fix media combobox not updating
### 2.6.6 (2023-10-04)
@@ -829,7 +1183,7 @@
- Handle extension changing only
- Swap order of operations for changing program
- Fix dumping path in DD
- Fix PlayStation serial code region parsing (Deterous)
- Fix PlayStation serial code region parsing
- Retrofit README
- Migrate to Nuget package for Redump
- Remove dd for Windows

View File

@@ -2,21 +2,23 @@
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<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>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<NoWarn>NU1902;NU1903</NoWarn>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.2.3</VersionPrefix>
<VersionPrefix>3.4.0</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>
<Copyright>Copyright (c) Matt Nadareski 2019-2025</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
@@ -29,33 +31,19 @@
<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`))">
<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</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.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>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.16" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.4" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
</ItemGroup>
</Project>

View File

@@ -3,7 +3,6 @@ using System.IO;
#if NET40
using System.Threading.Tasks;
#endif
using BinaryObjectScanner;
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib.Data;
@@ -27,7 +26,11 @@ namespace MPF.CLI
// Reset first run
options.FirstRun = false;
OptionsLoader.SaveToConfig(options, saveDefault: true);
OptionsLoader.SaveToConfig(options);
// Display non-error message
DisplayHelp("First-run detected! Please fill out config.json and run again.");
return;
}
// Try processing the standalone arguments
@@ -39,37 +42,56 @@ namespace MPF.CLI
return;
}
// Try processing the common arguments
bool success = OptionsLoader.ProcessCommonArguments(args, out MediaType mediaType, out RedumpSystem? knownSystem, out var error);
if (!success)
// Setup common outputs
CommandOptions opts;
MediaType mediaType;
RedumpSystem? knownSystem;
// Use interactive mode
if (args.Length > 0 && (args[0] == "-i" || args[0] == "--interactive"))
{
DisplayHelp(error);
return;
opts = InteractiveMode(options, out mediaType, out knownSystem);
}
// Validate the supplied credentials
bool? validated = RedumpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).GetAwaiter().GetResult();
string message = validated switch
// Use normal commandline parameters
else
{
true => "Redump username and password accepted!",
false => "Redump username and password denied!",
null => "An error occurred validating your credentials!",
};
// Try processing the common arguments
bool success = OptionsLoader.ProcessCommonArguments(args, out mediaType, out knownSystem, out var error);
if (!success)
{
DisplayHelp(error);
return;
}
if (!string.IsNullOrEmpty(message))
Console.WriteLine(message);
// Validate the supplied credentials
if (options.RetrieveMatchInformation
&& !string.IsNullOrEmpty(options.RedumpUsername)
&& !string.IsNullOrEmpty(options.RedumpPassword))
{
bool? validated = RedumpClient.ValidateCredentials(options.RedumpUsername!, options.RedumpPassword!).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!",
};
Console.WriteLine(message);
}
// Process any custom parameters
int startIndex = 2;
opts = LoadFromArguments(args, options, ref startIndex);
}
// 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...");
DisplayHelp("A path needs to be supplied in config.json for Aaru, exiting...");
return;
}
break;
@@ -77,7 +99,7 @@ namespace MPF.CLI
case InternalProgram.DiscImageCreator:
if (!File.Exists(options.DiscImageCreatorPath))
{
DisplayHelp("A path needs to be supplied for DIC, exiting...");
DisplayHelp("A path needs to be supplied in config.json for DIC, exiting...");
return;
}
break;
@@ -85,7 +107,7 @@ namespace MPF.CLI
case InternalProgram.Redumper:
if (!File.Exists(options.RedumperPath))
{
DisplayHelp("A path needs to be supplied for Redumper, exiting...");
DisplayHelp("A path needs to be supplied in config.json for Redumper, exiting...");
return;
}
break;
@@ -102,6 +124,10 @@ namespace MPF.CLI
return;
}
// Normalize the file path
if (opts.FilePath != null)
opts.FilePath = FrontendTool.NormalizeOutputPaths(opts.FilePath, getFullPath: true);
// Get the speed from the options
int speed = opts.DriveSpeed ?? FrontendTool.GetDefaultSpeedForMediaType(mediaType, options);
@@ -111,28 +137,23 @@ namespace MPF.CLI
opts.FilePath,
drive,
knownSystem,
mediaType,
options.InternalProgram,
parameters: null);
options.InternalProgram);
env.SetExecutionContext(mediaType, null);
env.SetProcessor();
// Process the parameters
string? paramStr = opts.CustomParams ?? env.GetFullParameters(speed);
string? paramStr = opts.CustomParams ?? env.GetFullParameters(mediaType, 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;
env.SetExecutionContext(mediaType, paramStr);
// Invoke the dumping program
Console.WriteLine($"Invoking {options.InternalProgram} using '{paramStr}'");
var dumpResult = env.Run(resultProgress).GetAwaiter().GetResult();
var dumpResult = env.Run(mediaType).GetAwaiter().GetResult();
Console.WriteLine(dumpResult.Message);
if (!dumpResult)
return;
@@ -152,19 +173,14 @@ namespace MPF.CLI
opts.FilePath,
drive,
knownSystem,
mediaType,
internalProgram: null,
parameters: null);
internalProgram: null);
env.SetExecutionContext(mediaType, null);
env.SetProcessor();
}
// Finally, attempt to do the output dance
#if NET40
var verifyTask = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress);
verifyTask.Wait();
var verifyResult = verifyTask.Result;
#else
var verifyResult = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
var verifyResult = env.VerifyAndSaveDumpOutput()
.ConfigureAwait(false).GetAwaiter().GetResult();
Console.WriteLine(verifyResult.Message);
}
@@ -181,15 +197,17 @@ namespace MPF.CLI
Console.WriteLine("MPF.CLI <mediatype> <system> [options]");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
Console.WriteLine("-h, -?, --help Show this help text");
Console.WriteLine("--version Print the program version");
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("-i, --interactive Enable interactive mode");
Console.WriteLine();
Console.WriteLine("CLI Options:");
Console.WriteLine("-u, --use <program> Override default dumping program");
Console.WriteLine("-u, --use <program> Override configured dumping program name");
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)");
@@ -197,6 +215,11 @@ namespace MPF.CLI
Console.WriteLine("-c, --custom \"<params>\" Custom parameters to use");
Console.WriteLine();
Console.WriteLine("Dumping program paths and other settings can be found in the config.json file");
Console.WriteLine("generated next to the program by default. Ensure that all settings are to user");
Console.WriteLine("preference before running MPF.CLI.");
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.");
@@ -207,10 +230,157 @@ namespace MPF.CLI
Console.WriteLine();
}
/// <summary>
/// Enable interactive mode for entering information
/// </summary>
private static CommandOptions InteractiveMode(Options options, out MediaType mediaType, out RedumpSystem? system)
{
// Create return values
var opts = new CommandOptions
{
FilePath = Path.Combine(options.DefaultOutputPath ?? "ISO", "track.bin"),
};
mediaType = MediaType.NONE;
system = options.DefaultSystem;
// Create state values
string? result = string.Empty;
root:
Console.Clear();
Console.WriteLine("MPF.CLI Interactive Mode - Main Menu");
Console.WriteLine("-------------------------");
Console.WriteLine();
Console.WriteLine($"1) Set media type (Currently '{mediaType}')");
Console.WriteLine($"2) Set system (Currently '{system}')");
Console.WriteLine($"3) Set dumping program (Currently '{options.InternalProgram}')");
Console.WriteLine($"4) Set device path (Currently '{opts.DevicePath}')");
Console.WriteLine($"5) Set mounted path (Currently '{opts.MountedPath}')");
Console.WriteLine($"6) Set file path (Currently '{opts.FilePath}')");
Console.WriteLine($"7) Set override speed (Currently '{opts.DriveSpeed}')");
Console.WriteLine($"8) Set custom parameters (Currently '{opts.CustomParams}')");
Console.WriteLine();
Console.WriteLine($"Q) Exit the program");
Console.WriteLine($"X) Start dumping");
Console.Write("> ");
result = Console.ReadLine();
switch (result)
{
case "1":
goto mediaType;
case "2":
goto system;
case "3":
goto dumpingProgram;
case "4":
goto devicePath;
case "5":
goto mountedPath;
case "6":
goto filePath;
case "7":
goto overrideSpeed;
case "8":
goto customParams;
case "q":
case "Q":
Environment.Exit(0);
break;
case "x":
case "X":
Console.Clear();
goto exit;
case "z":
case "Z":
Console.WriteLine("It is pitch black. You are likely to be eaten by a grue.");
Console.Write("> ");
Console.ReadLine();
goto root;
default:
Console.WriteLine($"Invalid selection: {result}");
Console.ReadLine();
goto root;
}
mediaType:
Console.WriteLine();
Console.WriteLine("Input the media type and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
mediaType = OptionsLoader.ToMediaType(result);
goto root;
system:
Console.WriteLine();
Console.WriteLine("Input the system and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
system = Extensions.ToRedumpSystem(result);
goto root;
dumpingProgram:
Console.WriteLine();
Console.WriteLine("Input the dumping program and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
options.InternalProgram = result.ToInternalProgram();
goto root;
devicePath:
Console.WriteLine();
Console.WriteLine("Input the device path and press Enter:");
Console.Write("> ");
opts.DevicePath = Console.ReadLine();
goto root;
mountedPath:
Console.WriteLine();
Console.WriteLine("Input the mounted path and press Enter:");
Console.Write("> ");
opts.MountedPath = Console.ReadLine();
goto root;
filePath:
Console.WriteLine();
Console.WriteLine("Input the file path and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
if (!string.IsNullOrEmpty(result))
result = Path.GetFullPath(result!);
opts.FilePath = result;
goto root;
overrideSpeed:
Console.WriteLine();
Console.WriteLine("Input the override speed and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
if (!int.TryParse(result, out int speed))
speed = -1;
opts.DriveSpeed = speed;
goto root;
customParams:
Console.WriteLine();
Console.WriteLine("Input the custom parameters and press Enter:");
Console.Write("> ");
opts.CustomParams = Console.ReadLine();
goto root;
exit:
return opts;
}
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
private static CommandOptions LoadFromArguments(string[] args, Frontend.Options options, ref int startIndex)
private static CommandOptions LoadFromArguments(string[] args, Options options, ref int startIndex)
{
// Create return values
var opts = new CommandOptions();
@@ -233,12 +403,12 @@ namespace MPF.CLI
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
{
string internalProgram = args[startIndex].Split('=')[1];
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
options.InternalProgram = internalProgram.ToInternalProgram();
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
options.InternalProgram = internalProgram.ToInternalProgram();
startIndex++;
}

View File

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

View File

@@ -1,25 +0,0 @@
using System;
using BinaryObjectScanner;
using MPF.Frontend;
namespace MPF.Check
{
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}");
}
}
}

View File

@@ -2,21 +2,23 @@
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<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>
<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
<LangVersion>latest</LangVersion>
<NoWarn>NU1902;NU1903</NoWarn>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.2.3</VersionPrefix>
<VersionPrefix>3.4.0</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>
<Copyright>Copyright (c) Matt Nadareski 2019-2025</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
@@ -29,37 +31,19 @@
<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`))">
<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</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.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>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.16" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.4" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
</ItemGroup>
</Project>

View File

@@ -1,10 +1,8 @@
using System;
using System.IO;
#if NET40
using System.Threading;
using System.Threading.Tasks;
#endif
using BinaryObjectScanner;
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib;
@@ -18,7 +16,7 @@ namespace MPF.Check
public static void Main(string[] args)
{
// Create a default options object
var options = new Frontend.Options()
var options = new Options()
{
// Internal Program
InternalProgram = InternalProgram.NONE,
@@ -36,11 +34,11 @@ namespace MPF.Check
// Protection Scanning Options
ScanArchivesForProtection = true,
ScanPackersForProtection = false,
IncludeDebugProtectionInformation = false,
HideDriveLetters = false,
// Redump Login Information
RetrieveMatchInformation = true,
RedumpUsername = null,
RedumpPassword = null,
};
@@ -54,40 +52,55 @@ namespace MPF.Check
return;
}
// Try processing the common arguments
bool success = OptionsLoader.ProcessCommonArguments(args, out MediaType mediaType, out RedumpSystem? knownSystem, out var error);
if (!success)
// Setup common outputs
CommandOptions opts;
RedumpSystem? knownSystem;
int startIndex;
// Use interactive mode
if (args.Length > 0 && (args[0] == "-i" || args[0] == "--interactive"))
{
DisplayHelp(error);
return;
startIndex = 1;
opts = InteractiveMode(options, out knownSystem);
}
// Use normal commandline parameters
else
{
// Try processing the common arguments
bool success = OptionsLoader.ProcessCommonArguments(args, out knownSystem, out var error);
if (!success)
{
DisplayHelp(error);
return;
}
// Loop through and process options
startIndex = 1;
opts = LoadFromArguments(args, options, ref startIndex);
}
// Loop through and process options
int startIndex = 2;
CommandOptions opts = LoadFromArguments(args, options, ref startIndex);
if (options.InternalProgram == InternalProgram.NONE)
{
DisplayHelp("A program name needs to be provided");
return;
}
// Make new Progress objects
var resultProgress = new Progress<ResultEventArgs>();
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
// Validate the supplied credentials
bool? validated = RedumpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).GetAwaiter().GetResult();
string message = validated switch
if (options.RetrieveMatchInformation
&& !string.IsNullOrEmpty(options.RedumpUsername)
&& !string.IsNullOrEmpty(options.RedumpPassword))
{
true => "Redump username and password accepted!",
false => "Redump username and password denied!",
null => "An error occurred validating your credentials!",
};
bool? validated = RedumpClient.ValidateCredentials(options.RedumpUsername!, options.RedumpPassword!).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);
}
// Loop through all the rest of the args
for (int i = startIndex; i < args.Length; i++)
@@ -107,10 +120,16 @@ namespace MPF.Check
if (!string.IsNullOrEmpty(opts.DevicePath))
drive = Drive.Create(null, opts.DevicePath!);
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, internalProgram: null, parameters: null);
var env = new DumpEnvironment(options,
filepath,
drive,
knownSystem,
internalProgram: null);
env.SetProcessor();
// Finally, attempt to do the output dance
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress, seedInfo: opts.Seed).GetAwaiter().GetResult();
var result = env.VerifyAndSaveDumpOutput(seedInfo: opts.Seed)
.ConfigureAwait(false).GetAwaiter().GetResult();
Console.WriteLine(result.Message);
}
}
@@ -125,14 +144,16 @@ namespace MPF.Check
Console.WriteLine(error);
Console.WriteLine("Usage:");
Console.WriteLine("MPF.Check <mediatype> <system> [options] </path/to/output.cue/iso> ...");
Console.WriteLine("MPF.Check <system> [options] </path/to/output.cue/iso> ...");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
Console.WriteLine("-h, -?, --help Show this help text");
Console.WriteLine("--version Print the program version");
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("-i, --interactive Enable interactive mode");
Console.WriteLine();
Console.WriteLine("Check Options:");
@@ -140,12 +161,12 @@ namespace MPF.Check
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(" --no-retrieve Disable retrieving match information from Redump");
Console.WriteLine("-c, --credentials <user> <pw> Redump username and password (incompatible with --no-retrieve)");
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");
@@ -154,12 +175,204 @@ namespace MPF.Check
Console.WriteLine("-z, --zip Enable log file compression");
Console.WriteLine("-d, --delete Enable unnecessary file deletion");
Console.WriteLine();
Console.WriteLine("WARNING: Check will overwrite both any existing submission information files as well");
Console.WriteLine("as any log archives. Please make backups of those if you need to before running Check.");
Console.WriteLine();
}
/// <summary>
/// Enable interactive mode for entering information
/// </summary>
private static CommandOptions InteractiveMode(Options options, out RedumpSystem? system)
{
// Create return values
var opts = new CommandOptions();
system = null;
// These values require multiple parts to be active
bool scan = false,
enableArchives = true,
enableDebug = false,
hideDriveLetters = false;
// Create state values
string? result = string.Empty;
root:
Console.Clear();
Console.WriteLine("MPF.Check Interactive Mode - Main Menu");
Console.WriteLine("-------------------------");
Console.WriteLine();
Console.WriteLine($"1) Set system (Currently '{system}')");
Console.WriteLine($"2) Set dumping program (Currently '{options.InternalProgram}')");
Console.WriteLine($"3) Set seed path (Currently '{opts.Seed}')");
Console.WriteLine($"4) Add placeholders (Currently '{options.AddPlaceholders}')");
Console.WriteLine($"5) Create IRD (Currently '{options.CreateIRDAfterDumping}')");
Console.WriteLine($"6) Attempt Redump matches (Currently '{options.RetrieveMatchInformation}')");
Console.WriteLine($"7) Redump credentials (Currently '{options.RedumpUsername}')");
Console.WriteLine($"8) Pull all information (Currently '{options.PullAllInformation}')");
Console.WriteLine($"9) Set device path (Currently '{opts.DevicePath}')");
Console.WriteLine($"A) Scan for protection (Currently '{scan}')");
Console.WriteLine($"B) Scan archives for protection (Currently '{enableArchives}')");
Console.WriteLine($"C) Debug protection scan output (Currently '{enableDebug}')");
Console.WriteLine($"D) Hide drive letters in protection output (Currently '{hideDriveLetters}')");
Console.WriteLine($"E) Hide filename suffix (Currently '{options.AddFilenameSuffix}')");
Console.WriteLine($"F) Output submission JSON (Currently '{options.OutputSubmissionJSON}')");
Console.WriteLine($"G) Include JSON artifacts (Currently '{options.IncludeArtifacts}')");
Console.WriteLine($"H) Compress logs (Currently '{options.CompressLogFiles}')");
Console.WriteLine($"I) Delete unnecessary files (Currently '{options.DeleteUnnecessaryFiles}')");
Console.WriteLine();
Console.WriteLine($"Q) Exit the program");
Console.WriteLine($"X) Start checking");
Console.Write("> ");
result = Console.ReadLine();
switch (result)
{
case "1":
goto system;
case "2":
goto dumpingProgram;
case "3":
goto seedPath;
case "4":
options.AddPlaceholders = !options.AddPlaceholders;
goto root;
case "5":
options.CreateIRDAfterDumping = !options.CreateIRDAfterDumping;
goto root;
case "6":
options.RetrieveMatchInformation = !options.RetrieveMatchInformation;
goto root;
case "7":
goto redumpCredentials;
case "8":
options.PullAllInformation = !options.PullAllInformation;
goto root;
case "9":
goto devicePath;
case "a":
case "A":
scan = !scan;
goto root;
case "b":
case "B":
enableArchives = !enableArchives;
goto root;
case "c":
case "C":
enableDebug = !enableDebug;
goto root;
case "d":
case "D":
hideDriveLetters = !hideDriveLetters;
goto root;
case "e":
case "E":
options.AddFilenameSuffix = !options.AddFilenameSuffix;
goto root;
case "f":
case "F":
options.OutputSubmissionJSON = !options.OutputSubmissionJSON;
goto root;
case "g":
case "G":
options.IncludeArtifacts = !options.IncludeArtifacts;
goto root;
case "h":
case "H":
options.CompressLogFiles = !options.CompressLogFiles;
goto root;
case "i":
case "I":
options.DeleteUnnecessaryFiles = !options.DeleteUnnecessaryFiles;
goto root;
case "q":
case "Q":
Environment.Exit(0);
break;
case "x":
case "X":
Console.Clear();
goto exit;
case "z":
case "Z":
Console.WriteLine("It is pitch black. You are likely to be eaten by a grue.");
Console.Write("> ");
Console.ReadLine();
goto root;
default:
Console.WriteLine($"Invalid selection: {result}");
Console.ReadLine();
goto root;
}
system:
Console.WriteLine();
Console.WriteLine("Input the system and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
system = Extensions.ToRedumpSystem(result);
goto root;
dumpingProgram:
Console.WriteLine();
Console.WriteLine("Input the dumping program and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
options.InternalProgram = result.ToInternalProgram();
goto root;
seedPath:
Console.WriteLine();
Console.WriteLine("Input the seed path and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
opts.Seed = Builder.CreateFromFile(result);
goto root;
redumpCredentials:
Console.WriteLine();
Console.WriteLine("Enter your Redumper username and press Enter:");
Console.Write("> ");
options.RedumpUsername = Console.ReadLine();
Console.WriteLine("Enter your Redumper password (hidden) and press Enter:");
Console.Write("> ");
options.RedumpPassword = string.Empty;
while (true)
{
var key = Console.ReadKey(true);
if (key.Key == ConsoleKey.Enter)
break;
options.RedumpPassword += key.KeyChar;
}
goto root;
devicePath:
Console.WriteLine();
Console.WriteLine("Input the device path and press Enter:");
Console.Write("> ");
opts.DevicePath = Console.ReadLine();
goto root;
exit:
// Now deal with the complex options
options.ScanForProtection = scan && !string.IsNullOrEmpty(opts.DevicePath);
options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(opts.DevicePath);
options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(opts.DevicePath);
options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(opts.DevicePath);
return opts;
}
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
private static CommandOptions LoadFromArguments(string[] args, Frontend.Options options, ref int startIndex)
private static CommandOptions LoadFromArguments(string[] args, Options options, ref int startIndex)
{
// Create return values
var opts = new CommandOptions();
@@ -167,7 +380,6 @@ namespace MPF.Check
// These values require multiple parts to be active
bool scan = false,
enableArchives = true,
enablePackers = false,
enableDebug = false,
hideDriveLetters = false;
@@ -189,12 +401,12 @@ namespace MPF.Check
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
{
string internalProgram = args[startIndex].Split('=')[1];
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
options.InternalProgram = internalProgram.ToInternalProgram();
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
options.InternalProgram = internalProgram.ToInternalProgram();
startIndex++;
}
@@ -223,6 +435,12 @@ namespace MPF.Check
options.CreateIRDAfterDumping = true;
}
// Retrieve Redump match information
else if (args[startIndex] == "--no-retrieve")
{
options.RetrieveMatchInformation = false;
}
// Redump login
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
{
@@ -266,12 +484,6 @@ namespace MPF.Check
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"))
{
@@ -296,7 +508,7 @@ namespace MPF.Check
options.OutputSubmissionJSON = true;
}
// Output submission JSON
// Include JSON artifacts
else if (args[startIndex].Equals("--include-artifacts"))
{
options.IncludeArtifacts = true;
@@ -308,7 +520,7 @@ namespace MPF.Check
options.CompressLogFiles = true;
}
// Delete unnecessary files files
// Delete unnecessary files
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
{
options.DeleteUnnecessaryFiles = true;
@@ -324,7 +536,6 @@ namespace MPF.Check
// 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);

View File

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

View File

@@ -0,0 +1,517 @@
using System.Collections.Generic;
using MPF.ExecutionContexts.Aaru;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.ExecutionContexts.Test
{
public class AaruTests
{
#region Converters.Extension
[Theory]
[InlineData(null, ".aaruf")]
[InlineData(MediaType.CDROM, ".aaruf")]
[InlineData(MediaType.GDROM, ".aaruf")]
[InlineData(MediaType.DVD, ".aaruf")]
[InlineData(MediaType.HDDVD, ".aaruf")]
[InlineData(MediaType.BluRay, ".aaruf")]
[InlineData(MediaType.FloppyDisk, ".aaruf")]
[InlineData(MediaType.HardDisk, ".aaruf")]
[InlineData(MediaType.ApertureCard, ".aaruf")]
public void ExtensionTest(MediaType? type, string expected)
{
string actual = Converters.Extension(type);
Assert.Equal(expected, actual);
}
#endregion
#region Default Values
private static Dictionary<string, string?> AllOptions = new()
{
[SettingConstants.EnableDebug] = "true",
[SettingConstants.EnableVerbose] = "true",
[SettingConstants.ForceDumping] = "true",
[SettingConstants.RereadCount] = "1000",
[SettingConstants.StripPersonalData] = "true",
};
[Theory]
[InlineData(null, null, null, "filename.bin", null, null)]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --store-encrypted True --title-keys False --trim True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
[InlineData(RedumpSystem.NintendoWii, MediaType.NintendoWiiOpticalDisc, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
[InlineData(RedumpSystem.NintendoWiiU, MediaType.NintendoWiiUOpticalDisc, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.FloppyDisk, "/dev/sr0", "filename.bin", 2, "--debug True --verbose True media dump --force True --private True --speed 2 --retry-passes 1000 /dev/sr0 \"filename.bin\"")]
public void DefaultValueTest(RedumpSystem? system,
MediaType? type,
string? drivePath,
string filename,
int? driveSpeed,
string? expected)
{
var context = new ExecutionContext(system, type, drivePath, filename, driveSpeed, AllOptions);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
}
#endregion
#region Archive Family
[Theory]
[InlineData("arc info filename.bin")]
[InlineData("arc info \"filename.bin\"")]
[InlineData("archive info filename.bin")]
[InlineData("archive info \"filename.bin\"")]
public void ArchiveInfoTest(string parameters)
{
string? expected = "archive info \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Database Family
[Theory]
[InlineData("db stats")]
[InlineData("database stats")]
public void DatabaseStatsTest(string parameters)
{
string? expected = "database stats";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("db update --clear --clear-all")]
[InlineData("db update --clear true --clear-all true")]
[InlineData("database update --clear --clear-all")]
[InlineData("database update --clear true --clear-all true")]
public void DatabaseUpdateTest(string parameters)
{
string? expected = "database update --clear True --clear-all True";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Device Family
[Theory]
[InlineData("dev info -w prefix filename.bin")]
[InlineData("dev info --output-prefix prefix filename.bin")]
[InlineData("device info -w prefix filename.bin")]
[InlineData("device info --output-prefix prefix filename.bin")]
public void DeviceInfoTest(string parameters)
{
string? expected = "device info --output-prefix \"prefix\" filename.bin";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("dev list localhost")]
[InlineData("device list localhost")]
public void DeviceListTest(string parameters)
{
string? expected = "device list \"localhost\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("dev report -t filename.bin")]
[InlineData("dev report -t true filename.bin")]
[InlineData("dev report --trap-disc filename.bin")]
[InlineData("dev report --trap-disc true filename.bin")]
[InlineData("device report -t filename.bin")]
[InlineData("device report -t true filename.bin")]
[InlineData("device report --trap-disc filename.bin")]
[InlineData("device report --trap-disc true filename.bin")]
public void DeviceReportTest(string parameters)
{
string? expected = "device report --trap-disc True filename.bin";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Filesystem Family
[Theory]
[InlineData("fi extract -e enc -x -n ns -O opts input output")]
[InlineData("fi extract -e enc -x true -n ns -O opts input output")]
[InlineData("fi extract --encoding enc --xattrs --namespace ns --options opts input output")]
[InlineData("fi extract --encoding enc --xattrs true --namespace ns --options opts input output")]
[InlineData("fs extract -e enc -x -n ns -O opts input output")]
[InlineData("fs extract -e enc -x true -n ns -O opts input output")]
[InlineData("fs extract --encoding enc --xattrs --namespace ns --options opts input output")]
[InlineData("fs extract --encoding enc --xattrs true --namespace ns --options opts input output")]
[InlineData("filesystem extract -e enc -x -n ns -O opts input output")]
[InlineData("filesystem extract -e enc -x true -n ns -O opts input output")]
[InlineData("filesystem extract --encoding enc --xattrs --namespace ns --options opts input output")]
[InlineData("filesystem extract --encoding enc --xattrs true --namespace ns --options opts input output")]
public void FilesystemExtractTest(string parameters)
{
string? expected = "filesystem extract --xattrs True --encoding \"enc\" --namespace \"ns\" --options \"opts\" \"input\" \"output\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("fi info -e enc -x -n ns -O opts input")]
[InlineData("fi info -e enc -x true -n ns -O opts input")]
[InlineData("fi info --encoding enc --xattrs --namespace ns --options opts input")]
[InlineData("fi info --encoding enc --xattrs true --namespace ns --options opts input")]
[InlineData("fs info -e enc -x -n ns -O opts input")]
[InlineData("fs info -e enc -x true -n ns -O opts input")]
[InlineData("fs info --encoding enc --xattrs --namespace ns --options opts input")]
[InlineData("fs info --encoding enc --xattrs true --namespace ns --options opts input")]
[InlineData("filesystem info -e enc -x -n ns -O opts input")]
[InlineData("filesystem info -e enc -x true -n ns -O opts input")]
[InlineData("filesystem info --encoding enc --xattrs --namespace ns --options opts input")]
[InlineData("filesystem info --encoding enc --xattrs true --namespace ns --options opts input")]
public void FilesystemInfoTest(string parameters)
{
string? expected = "filesystem info --xattrs True --encoding \"enc\" --namespace \"ns\" --options \"opts\" \"input\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("fi ls -e enc -f -l -p input")]
[InlineData("fi list -e enc -f -l -p input")]
[InlineData("fi ls -e enc -f true -l true -p true input")]
[InlineData("fi list -e enc -f true -l true -p true input")]
[InlineData("fi ls --encoding enc --filesystems --long-format --partitions input")]
[InlineData("fi list --encoding enc --filesystems --long-format --partitions input")]
[InlineData("fi ls --encoding enc --filesystems true --long-format true --partitions true input")]
[InlineData("fi list --encoding enc --filesystems true --long-format true --partitions true input")]
[InlineData("fs ls -e enc -f -l -p input")]
[InlineData("fs list -e enc -f -l -p input")]
[InlineData("fs ls -e enc -f true -l true -p true input")]
[InlineData("fs list -e enc -f true -l true -p true input")]
[InlineData("fs ls --encoding enc --filesystems --long-format --partitions input")]
[InlineData("fs list --encoding enc --filesystems --long-format --partitions input")]
[InlineData("fs ls --encoding enc --filesystems true --long-format true --partitions true input")]
[InlineData("fs list --encoding enc --filesystems true --long-format true --partitions true input")]
[InlineData("filesystem ls -e enc -f -l -p input")]
[InlineData("filesystem list -e enc -f -l -p input")]
[InlineData("filesystem ls -e enc -f true -l true -p true input")]
[InlineData("filesystem list -e enc -f true -l true -p true input")]
[InlineData("filesystem ls --encoding enc --filesystems --long-format --partitions input")]
[InlineData("filesystem list --encoding enc --filesystems --long-format --partitions input")]
[InlineData("filesystem ls --encoding enc --filesystems true --long-format true --partitions true input")]
[InlineData("filesystem list --encoding enc --filesystems true --long-format true --partitions true input")]
public void FilesystemListTest(string parameters)
{
string? expected = "filesystem list --filesystems True --long-format True --partitions True --encoding \"enc\" \"input\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("fi options")]
[InlineData("fs options")]
[InlineData("filesystem options")]
public void FilesystemOptionsTest(string parameters)
{
string? expected = "filesystem options";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Image Family
[Theory]
[InlineData("i chk -a --crc16 -c --crc64 --fletcher16 --fletcher32 -m -t -s --sha256 --sha384 --sha512 -f -w filename.bin")]
[InlineData("i chk -a true --crc16 true -c true --crc64 true --fletcher16 true --fletcher32 true -m true -t true -s true --sha256 true --sha384 true --sha512 true -f true -w true filename.bin")]
[InlineData("i chk --adler32 --crc16 --crc32 --crc64 --fletcher16 --fletcher32 --md5 --separated-tracks --sha1 --sha256 --sha384 --sha512 --spamsum --whole-disc filename.bin")]
[InlineData("i chk --adler32 true --crc16 true --crc32 true --crc64 true --fletcher16 true --fletcher32 true --md5 true --separated-tracks true --sha1 true --sha256 true --sha384 true --sha512 true --spamsum true --whole-disc true filename.bin")]
[InlineData("i checksum -a --crc16 -c --crc64 --fletcher16 --fletcher32 -m -t -s --sha256 --sha384 --sha512 -f -w filename.bin")]
[InlineData("i checksum -a true --crc16 true -c true --crc64 true --fletcher16 true --fletcher32 true -m true -t true -s true --sha256 true --sha384 true --sha512 true -f true -w true filename.bin")]
[InlineData("i checksum --adler32 --crc16 --crc32 --crc64 --fletcher16 --fletcher32 --md5 --separated-tracks --sha1 --sha256 --sha384 --sha512 --spamsum --whole-disc filename.bin")]
[InlineData("i checksum --adler32 true --crc16 true --crc32 true --crc64 true --fletcher16 true --fletcher32 true --md5 true --separated-tracks true --sha1 true --sha256 true --sha384 true --sha512 true --spamsum true --whole-disc true filename.bin")]
[InlineData("image chk -a --crc16 -c --crc64 --fletcher16 --fletcher32 -m -t -s --sha256 --sha384 --sha512 -f -w filename.bin")]
[InlineData("image chk -a true --crc16 true -c true --crc64 true --fletcher16 true --fletcher32 true -m true -t true -s true --sha256 true --sha384 true --sha512 true -f true -w true filename.bin")]
[InlineData("image chk --adler32 --crc16 --crc32 --crc64 --fletcher16 --fletcher32 --md5 --separated-tracks --sha1 --sha256 --sha384 --sha512 --spamsum --whole-disc filename.bin")]
[InlineData("image chk --adler32 true --crc16 true --crc32 true --crc64 true --fletcher16 true --fletcher32 true --md5 true --separated-tracks true --sha1 true --sha256 true --sha384 true --sha512 true --spamsum true --whole-disc true filename.bin")]
[InlineData("image checksum -a --crc16 -c --crc64 --fletcher16 --fletcher32 -m -t -s --sha256 --sha384 --sha512 -f -w filename.bin")]
[InlineData("image checksum -a true --crc16 true -c true --crc64 true --fletcher16 true --fletcher32 true -m true -t true -s true --sha256 true --sha384 true --sha512 true -f true -w true filename.bin")]
[InlineData("image checksum --adler32 --crc16 --crc32 --crc64 --fletcher16 --fletcher32 --md5 --separated-tracks --sha1 --sha256 --sha384 --sha512 --spamsum --whole-disc filename.bin")]
[InlineData("image checksum --adler32 true --crc16 true --crc32 true --crc64 true --fletcher16 true --fletcher32 true --md5 true --separated-tracks true --sha1 true --sha256 true --sha384 true --sha512 true --spamsum true --whole-disc true filename.bin")]
public void ImageChecksumTest(string parameters)
{
string? expected = "image checksum --adler32 True --crc16 True --crc32 True --crc64 True --fletcher16 True --fletcher32 True --md5 True --separated-tracks True --sha1 True --sha256 True --sha384 True --sha512 True --spamsum True --whole-disc True \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("i cmp input1.bin input2.bin")]
[InlineData("i compare input1.bin input2.bin")]
[InlineData("image cmp input1.bin input2.bin")]
[InlineData("image compare input1.bin input2.bin")]
public void ImageCompareTest(string parameters)
{
string? expected = "image compare \"input1.bin\" \"input2.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("i convert --comments co -c 0 --creator cr --drive-manufacturer dm --drive-model dm --drive-revision dr --drive-serial ds --fix-subchannel --fix-subchannel-crc --fix-subchannel-position -f -p fmt --generate-subchannels -g geo --media-barcode mb --media-lastsequence 0 --media-manufacturer mm --media-model mm --media-partnumber mpn --media-sequence 0 --media-serial ms --media-title mt -O opt -r resume -x cicm input1.bin input2.bin")]
[InlineData("i convert --comments co -c 0 --creator cr --drive-manufacturer dm --drive-model dm --drive-revision dr --drive-serial ds --fix-subchannel true --fix-subchannel-crc true --fix-subchannel-position true -f true -p fmt --generate-subchannels true -g geo --media-barcode mb --media-lastsequence 0 --media-manufacturer mm --media-model mm --media-partnumber mpn --media-sequence 0 --media-serial ms --media-title mt -O opt -r resume -x cicm input1.bin input2.bin")]
[InlineData("i convert --comments co --count 0 --creator cr --drive-manufacturer dm --drive-model dm --drive-revision dr --drive-serial ds --fix-subchannel --fix-subchannel-crc --fix-subchannel-position --force --format fmt --generate-subchannels --geometry geo --media-barcode mb --media-lastsequence 0 --media-manufacturer mm --media-model mm --media-partnumber mpn --media-sequence 0 --media-serial ms --media-title mt --options opt --resume-file resume --cicm-xml cicm input1.bin input2.bin")]
[InlineData("i convert --comments co --count 0 --creator cr --drive-manufacturer dm --drive-model dm --drive-revision dr --drive-serial ds --fix-subchannel true --fix-subchannel-crc true --fix-subchannel-position true --force true --format fmt --generate-subchannels true --geometry geo --media-barcode mb --media-lastsequence 0 --media-manufacturer mm --media-model mm --media-partnumber mpn --media-sequence 0 --media-serial ms --media-title mt --options opt --resume-file resume --cicm-xml cicm input1.bin input2.bin")]
[InlineData("image convert --comments co -c 0 --creator cr --drive-manufacturer dm --drive-model dm --drive-revision dr --drive-serial ds --fix-subchannel --fix-subchannel-crc --fix-subchannel-position -f -p fmt --generate-subchannels -g geo --media-barcode mb --media-lastsequence 0 --media-manufacturer mm --media-model mm --media-partnumber mpn --media-sequence 0 --media-serial ms --media-title mt -O opt -r resume -x cicm input1.bin input2.bin")]
[InlineData("image convert --comments co -c 0 --creator cr --drive-manufacturer dm --drive-model dm --drive-revision dr --drive-serial ds --fix-subchannel true --fix-subchannel-crc true --fix-subchannel-position true -f true -p fmt --generate-subchannels true -g geo --media-barcode mb --media-lastsequence 0 --media-manufacturer mm --media-model mm --media-partnumber mpn --media-sequence 0 --media-serial ms --media-title mt -O opt -r resume -x cicm input1.bin input2.bin")]
[InlineData("image convert --comments co --count 0 --creator cr --drive-manufacturer dm --drive-model dm --drive-revision dr --drive-serial ds --fix-subchannel --fix-subchannel-crc --fix-subchannel-position --force --format fmt --generate-subchannels --geometry geo --media-barcode mb --media-lastsequence 0 --media-manufacturer mm --media-model mm --media-partnumber mpn --media-sequence 0 --media-serial ms --media-title mt --options opt --resume-file resume --cicm-xml cicm input1.bin input2.bin")]
[InlineData("image convert --comments co --count 0 --creator cr --drive-manufacturer dm --drive-model dm --drive-revision dr --drive-serial ds --fix-subchannel true --fix-subchannel-crc true --fix-subchannel-position true --force true --format fmt --generate-subchannels true --geometry geo --media-barcode mb --media-lastsequence 0 --media-manufacturer mm --media-model mm --media-partnumber mpn --media-sequence 0 --media-serial ms --media-title mt --options opt --resume-file resume --cicm-xml cicm input1.bin input2.bin")]
public void ImageConvertTest(string parameters)
{
string? expected = "image convert --fix-subchannel True --fix-subchannel-crc True --fix-subchannel-position True --force True --generate-subchannels True --count 0 --media-lastsequence 0 --media-sequence 0 --comments \"co\" --creator \"cr\" --drive-manufacturer \"dm\" --drive-model \"dm\" --drive-revision \"dr\" --drive-serial \"ds\" --format \"fmt\" --geometry \"geo\" --media-barcode \"mb\" --media-manufacturer \"mm\" --media-model \"mm\" --media-partnumber \"mpn\" --media-serial \"ms\" --media-title \"mt\" --options \"opt\" --resume-file \"resume\" --cicm-xml \"cicm\" \"input1.bin\" \"input2.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("i create-sidecar -b 0 -e enc -t filename.bin")]
[InlineData("i create-sidecar -b 0 -e enc -t true filename.bin")]
[InlineData("i create-sidecar --block-size 0 --encoding enc --tape filename.bin")]
[InlineData("i create-sidecar --block-size 0 --encoding enc --tape true filename.bin")]
[InlineData("image create-sidecar -b 0 -e enc -t filename.bin")]
[InlineData("image create-sidecar -b 0 -e enc -t true filename.bin")]
[InlineData("image create-sidecar --block-size 0 --encoding enc --tape filename.bin")]
[InlineData("image create-sidecar --block-size 0 --encoding enc --tape true filename.bin")]
public void ImageCreateSidecarTest(string parameters)
{
string? expected = "image create-sidecar --tape True --block-size 0 --encoding \"enc\" \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("i decode -f -l all -p -s 0 filename.bin")]
[InlineData("i decode -f true -l all -p true -s 0 filename.bin")]
[InlineData("i decode --disk-tags --length all --sector-tags --start 0 filename.bin")]
[InlineData("i decode --disk-tags true --length all --sector-tags true --start 0 filename.bin")]
[InlineData("image decode -f -l all -p -s 0 filename.bin")]
[InlineData("image decode -f true -l all -p true -s 0 filename.bin")]
[InlineData("image decode --disk-tags --length all --sector-tags --start 0 filename.bin")]
[InlineData("image decode --disk-tags true --length all --sector-tags true --start 0 filename.bin")]
public void ImageDecodeTest(string parameters)
{
string? expected = "image decode --disk-tags True --sector-tags True --length all --start 0 \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("i entropy -p -t -w filename.bin")]
[InlineData("i entropy -p true -t true -w true filename.bin")]
[InlineData("i entropy --duplicated-sectors --separated-tracks --whole-disc filename.bin")]
[InlineData("i entropy --duplicated-sectors true --separated-tracks true --whole-disc true filename.bin")]
[InlineData("image entropy -p -t -w filename.bin")]
[InlineData("image entropy -p true -t true -w true filename.bin")]
[InlineData("image entropy --duplicated-sectors --separated-tracks --whole-disc filename.bin")]
[InlineData("image entropy --duplicated-sectors true --separated-tracks true --whole-disc true filename.bin")]
public void ImageEntropyTest(string parameters)
{
string? expected = "image entropy --duplicated-sectors True --separated-tracks True --whole-disc True \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("i info filename.bin")]
[InlineData("image info filename.bin")]
public void ImageInfoTest(string parameters)
{
string? expected = "image info \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("i options")]
[InlineData("image options")]
public void ImageOptionsTest(string parameters)
{
string? expected = "image options";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("i print -l 0 -r -s 0 -w 0 filename.bin")]
[InlineData("i print -l 0 -r true -s 0 -w 0 filename.bin")]
[InlineData("i print --length 0 --long-sectors --start 0 --width 0 filename.bin")]
[InlineData("i print --length 0 --long-sectors true --start 0 --width 0 filename.bin")]
[InlineData("image print -l 0 -r -s 0 -w 0 filename.bin")]
[InlineData("image print -l 0 -r true -s 0 -w 0 filename.bin")]
[InlineData("image print --length 0 --long-sectors --start 0 --width 0 filename.bin")]
[InlineData("image print --length 0 --long-sectors true --start 0 --width 0 filename.bin")]
public void ImagePrintTest(string parameters)
{
string? expected = "image print --long-sectors True --width 0 --length 0 --start 0 \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("i verify -w -s filename.bin")]
[InlineData("i verify -w true -s true filename.bin")]
[InlineData("i verify --verify-disc --verify-sectors filename.bin")]
[InlineData("i verify --verify-disc true --verify-sectors true filename.bin")]
[InlineData("image verify -w -s filename.bin")]
[InlineData("image verify -w true -s true filename.bin")]
[InlineData("image verify --verify-disc --verify-sectors filename.bin")]
[InlineData("image verify --verify-disc true --verify-sectors true filename.bin")]
public void ImageVerifyTest(string parameters)
{
string? expected = "image verify --verify-disc True --verify-sectors True \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Media Family
[Theory]
[InlineData("m dump --eject -e enc --first-pregap --fix-offset --fix-subchannel --fix-subchannel-crc --fix-subchannel-position -f --generate-subchannels --max-blocks 0 --metadata -O opt --persistent --private -r -p 0 --retry-subchannel -k 0 --skip-cdiready-hole --speed 0 -s --store-encrypted --subchannel any --title-keys --trim --use-buffered-reads -x cicm input output.bin")]
[InlineData("m dump --eject true -e enc --first-pregap true --fix-offset true --fix-subchannel true --fix-subchannel-crc true --fix-subchannel-position true -f true --generate-subchannels true --max-blocks 0 --metadata true -O opt --persistent true --private true -r true -p 0 --retry-subchannel true -k 0 --skip-cdiready-hole true --speed 0 -s true --store-encrypted true --subchannel any --title-keys true --trim true --use-buffered-reads true -x cicm input output.bin")]
[InlineData("m dump --eject --encoding enc --first-pregap --fix-offset --fix-subchannel --fix-subchannel-crc --fix-subchannel-position --force --generate-subchannels --max-blocks 0 --metadata --options opt --persistent --private --resume --retry-passes 0 --retry-subchannel --skip 0 --skip-cdiready-hole --speed 0 --stop-on-error --store-encrypted --subchannel any --title-keys --trim --use-buffered-reads --cicm-xml cicm input output.bin")]
[InlineData("m dump --eject true --encoding enc --first-pregap true --fix-offset true --fix-subchannel true --fix-subchannel-crc true --fix-subchannel-position true --force true --generate-subchannels true --max-blocks 0 --metadata true --options opt --persistent true --private true --resume true --retry-passes 0 --retry-subchannel true --skip 0 --skip-cdiready-hole true --speed 0 --stop-on-error true --store-encrypted true --subchannel any --title-keys true --trim true --use-buffered-reads true --cicm-xml cicm input output.bin")]
[InlineData("media dump --eject -e enc --first-pregap --fix-offset --fix-subchannel --fix-subchannel-crc --fix-subchannel-position -f --generate-subchannels --max-blocks 0 --metadata -O opt --persistent --private -r -p 0 --retry-subchannel -k 0 --skip-cdiready-hole --speed 0 -s --store-encrypted --subchannel any --title-keys --trim --use-buffered-reads -x cicm input output.bin")]
[InlineData("media dump --eject true -e enc --first-pregap true --fix-offset true --fix-subchannel true --fix-subchannel-crc true --fix-subchannel-position true -f true --generate-subchannels true --max-blocks 0 --metadata true -O opt --persistent true --private true -r true -p 0 --retry-subchannel true -k 0 --skip-cdiready-hole true --speed 0 -s true --store-encrypted true --subchannel any --title-keys true --trim true --use-buffered-reads true -x cicm input output.bin")]
[InlineData("media dump --eject --encoding enc --first-pregap --fix-offset --fix-subchannel --fix-subchannel-crc --fix-subchannel-position --force --generate-subchannels --max-blocks 0 --metadata --options opt --persistent --private --resume --retry-passes 0 --retry-subchannel --skip 0 --skip-cdiready-hole --speed 0 --stop-on-error --store-encrypted --subchannel any --title-keys --trim --use-buffered-reads --cicm-xml cicm input output.bin")]
[InlineData("media dump --eject true --encoding enc --first-pregap true --fix-offset true --fix-subchannel true --fix-subchannel-crc true --fix-subchannel-position true --force true --generate-subchannels true --max-blocks 0 --metadata true --options opt --persistent true --private true --resume true --retry-passes 0 --retry-subchannel true --skip 0 --skip-cdiready-hole true --speed 0 --stop-on-error true --store-encrypted true --subchannel any --title-keys true --trim true --use-buffered-reads true --cicm-xml cicm input output.bin")]
public void MediaDumpTest(string parameters)
{
string? expected = "media dump --eject True --first-pregap True --fix-offset True --fix-subchannel True --fix-subchannel-crc True --fix-subchannel-position True --force True --generate-subchannels True --metadata True --persistent True --private True --resume True --retry-subchannel True --skip-cdiready-hole True --stop-on-error True --store-encrypted True --title-keys True --trim True --use-buffered-reads True --speed 0 --retry-passes 0 --max-blocks 0 --skip 0 --encoding \"enc\" --options \"opt\" --subchannel \"any\" --cicm-xml \"cicm\" input \"output.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
[Theory]
[InlineData("m info -w prefix input")]
[InlineData("m info --output-prefix prefix input")]
[InlineData("media info -w prefix input")]
[InlineData("media info --output-prefix prefix input")]
public void MediaInfoTest(string parameters)
{
string? expected = "media info --output-prefix \"prefix\" input";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("m scan -b ibg -m mhdd --use-buffered-reads input")]
[InlineData("m scan -b ibg -m mhdd --use-buffered-reads true input")]
[InlineData("m scan --ibg-log ibg --mhdd-log mhdd --use-buffered-reads input")]
[InlineData("m scan --ibg-log ibg --mhdd-log mhdd --use-buffered-reads true input")]
[InlineData("media scan -b ibg -m mhdd --use-buffered-reads input")]
[InlineData("media scan -b ibg -m mhdd --use-buffered-reads true input")]
[InlineData("media scan --ibg-log ibg --mhdd-log mhdd --use-buffered-reads input")]
[InlineData("media scan --ibg-log ibg --mhdd-log mhdd --use-buffered-reads true input")]
public void MediaScanTest(string parameters)
{
string? expected = "media scan --use-buffered-reads True --ibg-log \"ibg\" --mhdd-log \"mhdd\" input";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Standalone Commands
[Theory]
[InlineData("--debug --help --verbose --version formats")]
[InlineData("--debug true --help true --verbose true --version true formats")]
public void PreCommandFlagsTest(string parameters)
{
string? expected = "--debug True --help True --verbose True --version True formats";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
[Theory]
[InlineData("configure", "configure")]
[InlineData("formats", "formats")]
[InlineData("list-encodings", "list-encodings")]
[InlineData("list-namespaces", "list-namespaces")]
[InlineData("remote localhost", "remote \"localhost\"")]
public void StandaloneCommandsTest(string parameters, string? expected)
{
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
}
}

View File

@@ -0,0 +1,464 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace MPF.ExecutionContexts.Test
{
public class BaseExecutionContextTests
{
#region GetBooleanSetting
[Fact]
public void GetBooleanSetting_InvalidDict_Default()
{
Dictionary<string, string?> settings = [];
string key = "key";
bool defaultValue = false;
bool actual = BaseExecutionContext.GetBooleanSetting(settings, key, defaultValue);
Assert.False(actual);
}
[Fact]
public void GetBooleanSetting_InvalidKey_Default()
{
Dictionary<string, string?> settings = new()
{
["key2"] = "true",
};
string key = "key";
bool defaultValue = false;
bool actual = BaseExecutionContext.GetBooleanSetting(settings, key, defaultValue);
Assert.False(actual);
}
[Fact]
public void GetBooleanSetting_NullValue_Default()
{
Dictionary<string, string?> settings = new()
{
["key"] = null,
};
string key = "key";
bool defaultValue = false;
bool actual = BaseExecutionContext.GetBooleanSetting(settings, key, defaultValue);
Assert.False(actual);
}
[Fact]
public void GetBooleanSetting_InvalidValue_Default()
{
Dictionary<string, string?> settings = new()
{
["key"] = "invalid",
};
string key = "key";
bool defaultValue = false;
bool actual = BaseExecutionContext.GetBooleanSetting(settings, key, defaultValue);
Assert.False(actual);
}
[Fact]
public void GetBooleanSetting_ValidValue_Parsed()
{
Dictionary<string, string?> settings = new()
{
["key"] = "true",
};
string key = "key";
bool defaultValue = false;
bool actual = BaseExecutionContext.GetBooleanSetting(settings, key, defaultValue);
Assert.True(actual);
}
#endregion
#region GetInt32Setting
[Fact]
public void GetInt32Setting_InvalidDict_Default()
{
Dictionary<string, string?> settings = [];
string key = "key";
int defaultValue = -1;
int actual = BaseExecutionContext.GetInt32Setting(settings, key, defaultValue);
Assert.Equal(-1, actual);
}
[Fact]
public void GetInt32Setting_InvalidKey_Default()
{
Dictionary<string, string?> settings = new()
{
["key2"] = "12345",
};
string key = "key";
int defaultValue = -1;
int actual = BaseExecutionContext.GetInt32Setting(settings, key, defaultValue);
Assert.Equal(-1, actual);
}
[Fact]
public void GetInt32Setting_NullValue_Default()
{
Dictionary<string, string?> settings = new()
{
["key"] = null,
};
string key = "key";
int defaultValue = -1;
int actual = BaseExecutionContext.GetInt32Setting(settings, key, defaultValue);
Assert.Equal(-1, actual);
}
[Fact]
public void GetInt32Setting_InvalidValue_Default()
{
Dictionary<string, string?> settings = new()
{
["key"] = "invalid",
};
string key = "key";
int defaultValue = -1;
int actual = BaseExecutionContext.GetInt32Setting(settings, key, defaultValue);
Assert.Equal(-1, actual);
}
[Fact]
public void GetInt32Setting_ValidValue_Parsed()
{
int expected = 12345;
Dictionary<string, string?> settings = new()
{
["key"] = "12345",
};
string key = "key";
int defaultValue = -1;
int actual = BaseExecutionContext.GetInt32Setting(settings, key, defaultValue);
Assert.Equal(expected, actual);
}
#endregion
#region GetStringSetting
[Fact]
public void GetStringSetting_InvalidDict_Default()
{
Dictionary<string, string?> settings = [];
string key = "key";
string? defaultValue = null;
string? actual = BaseExecutionContext.GetStringSetting(settings, key, defaultValue);
Assert.Null(actual);
}
[Fact]
public void GetStringSetting_InvalidKey_Default()
{
Dictionary<string, string?> settings = new()
{
["key2"] = "12345",
};
string key = "key";
string? defaultValue = null;
string? actual = BaseExecutionContext.GetStringSetting(settings, key, defaultValue);
Assert.Null(actual);
}
[Fact]
public void GetStringSetting_ValidValue_Rturned()
{
string expected = "expected";
Dictionary<string, string?> settings = new()
{
["key"] = "expected",
};
string key = "key";
string? defaultValue = null;
string? actual = BaseExecutionContext.GetStringSetting(settings, key, defaultValue);
Assert.Equal(expected, actual);
}
#endregion
#region SplitParameterString
[Fact]
public void SplitParameterString_Empty_Empty()
{
string parameters = string.Empty;
string[] actual = BaseExecutionContext.SplitParameterString(parameters);
Assert.Empty(actual);
}
[Fact]
public void SplitParameterString_NoSplit_Single()
{
string expected = "single";
string parameters = "single";
string[] actual = BaseExecutionContext.SplitParameterString(parameters);
var p0 = Assert.Single(actual);
Assert.Equal(expected, p0);
}
[Fact]
public void SplitParameterString_SplitNoEquals_Multiple()
{
string[] expected = ["-flag1", "value1", "-flag2"];
string parameters = "-flag1 value1 -flag2";
string[] actual = BaseExecutionContext.SplitParameterString(parameters);
Assert.True(expected.SequenceEqual(actual));
}
[Fact]
public void SplitParameterString_SplitEquals_Multiple()
{
string[] expected = ["-flag1=value1", "-flag2"];
string parameters = "-flag1=value1 -flag2";
string[] actual = BaseExecutionContext.SplitParameterString(parameters);
Assert.True(expected.SequenceEqual(actual));
}
[Fact]
public void SplitParameterString_SplitNoEqualsQuotes_Multiple()
{
string[] expected = ["-flag1", "\"value1 value2\"", "-flag2"];
string parameters = "-flag1 \"value1 value2\" -flag2";
string[] actual = BaseExecutionContext.SplitParameterString(parameters);
Assert.True(expected.SequenceEqual(actual));
}
[Fact]
public void SplitParameterString_SplitEqualsQuotes_Multiple()
{
string[] expected = ["-flag1=\"value1 value2\"", "-flag2"];
string parameters = "-flag1=\"value1 value2\" -flag2";
string[] actual = BaseExecutionContext.SplitParameterString(parameters);
Assert.True(expected.SequenceEqual(actual));
}
#endregion
#region DoesExist
[Fact]
public void DoesExist_Empty_False()
{
string[] parts = [];
int index = 0;
bool actual = BaseExecutionContext.DoesExist(parts, index);
Assert.False(actual);
}
[Fact]
public void DoesExist_Negative_False()
{
string[] parts = ["item"];
int index = -1;
bool actual = BaseExecutionContext.DoesExist(parts, index);
Assert.False(actual);
}
[Fact]
public void DoesExist_Greater_False()
{
string[] parts = ["item"];
int index = 1;
bool actual = BaseExecutionContext.DoesExist(parts, index);
Assert.False(actual);
}
[Fact]
public void DoesExist_Valid_True()
{
string[] parts = ["item"];
int index = 0;
bool actual = BaseExecutionContext.DoesExist(parts, index);
Assert.True(actual);
}
#endregion
#region IsValidBool
[Theory]
[InlineData("", false)]
[InlineData("true", true)]
[InlineData("True", true)]
[InlineData("TRUE", true)]
[InlineData("Yes", false)]
[InlineData("false", true)]
[InlineData("False", true)]
[InlineData("FALSE", true)]
[InlineData("No", false)]
[InlineData("Invalid", false)]
public void IsValidBoolTest(string parameter, bool expected)
{
bool actual = BaseExecutionContext.IsValidBool(parameter);
Assert.Equal(expected, actual);
}
#endregion
#region IsValidInt8
[Theory]
[InlineData("", null, null, false)]
[InlineData("", (sbyte)0, (sbyte)1, false)]
[InlineData("", (sbyte)0, sbyte.MaxValue, false)]
[InlineData("-2", null, null, true)]
[InlineData("-2", (sbyte)0, (sbyte)1, false)]
[InlineData("-2", (sbyte)0, sbyte.MaxValue, false)]
[InlineData("0", null, null, true)]
[InlineData("0", (sbyte)0, (sbyte)1, true)]
[InlineData("0", (sbyte)0, sbyte.MaxValue, true)]
[InlineData("2", null, null, true)]
[InlineData("2", (sbyte)0, (sbyte)1, false)]
[InlineData("2", (sbyte)0, sbyte.MaxValue, true)]
[InlineData("Invalid", null, null, false)]
[InlineData("Invalid", (sbyte)0, (sbyte)1, false)]
[InlineData("Invalid", (sbyte)0, sbyte.MaxValue, false)]
public void IsValidInt8Test(string parameter, sbyte? lowerBound, sbyte? upperBound, bool expected)
{
bool actual = BaseExecutionContext.IsValidInt8(parameter, lowerBound, upperBound);
Assert.Equal(expected, actual);
}
#endregion
#region IsValidInt16
[Theory]
[InlineData("", null, null, false)]
[InlineData("", (short)0, (short)1, false)]
[InlineData("", (short)0, short.MaxValue, false)]
[InlineData("-2", null, null, true)]
[InlineData("-2", (short)0, (short)1, false)]
[InlineData("-2", (short)0, short.MaxValue, false)]
[InlineData("0", null, null, true)]
[InlineData("0", (short)0, (short)1, true)]
[InlineData("0", (short)0, short.MaxValue, true)]
[InlineData("2", null, null, true)]
[InlineData("2", (short)0, (short)1, false)]
[InlineData("2", (short)0, short.MaxValue, true)]
[InlineData("Invalid", null, null, false)]
[InlineData("Invalid", (short)0, (short)1, false)]
[InlineData("Invalid", (short)0, short.MaxValue, false)]
public void IsValidInt16Test(string parameter, short? lowerBound, short? upperBound, bool expected)
{
bool actual = BaseExecutionContext.IsValidInt16(parameter, lowerBound, upperBound);
Assert.Equal(expected, actual);
}
#endregion
#region IsValidInt32
[Theory]
[InlineData("", null, null, false)]
[InlineData("", (int)0, (int)1, false)]
[InlineData("", (int)0, int.MaxValue, false)]
[InlineData("-2", null, null, true)]
[InlineData("-2", (int)0, (int)1, false)]
[InlineData("-2", (int)0, int.MaxValue, false)]
[InlineData("0", null, null, true)]
[InlineData("0", (int)0, (int)1, true)]
[InlineData("0", (int)0, int.MaxValue, true)]
[InlineData("2", null, null, true)]
[InlineData("2", (int)0, (int)1, false)]
[InlineData("2", (int)0, int.MaxValue, true)]
[InlineData("Invalid", null, null, false)]
[InlineData("Invalid", (int)0, (int)1, false)]
[InlineData("Invalid", (int)0, int.MaxValue, false)]
public void IsValidInt32Test(string parameter, int? lowerBound, int? upperBound, bool expected)
{
bool actual = BaseExecutionContext.IsValidInt32(parameter, lowerBound, upperBound);
Assert.Equal(expected, actual);
}
#endregion
#region IsValidInt64
[Theory]
[InlineData("", null, null, false)]
[InlineData("", (long)0, (long)1, false)]
[InlineData("", (long)0, long.MaxValue, false)]
[InlineData("-2", null, null, true)]
[InlineData("-2", (long)0, (long)1, false)]
[InlineData("-2", (long)0, long.MaxValue, false)]
[InlineData("0", null, null, true)]
[InlineData("0", (long)0, (long)1, true)]
[InlineData("0", (long)0, long.MaxValue, true)]
[InlineData("2", null, null, true)]
[InlineData("2", (long)0, (long)1, false)]
[InlineData("2", (long)0, long.MaxValue, true)]
[InlineData("Invalid", null, null, false)]
[InlineData("Invalid", (long)0, (long)1, false)]
[InlineData("Invalid", (long)0, long.MaxValue, false)]
public void IsValidInt64Test(string parameter, long? lowerBound, long? upperBound, bool expected)
{
bool actual = BaseExecutionContext.IsValidInt64(parameter, lowerBound, upperBound);
Assert.Equal(expected, actual);
}
#endregion
#region ExtractFactorFromValue
[Theory]
[InlineData("1", "1", 1)]
[InlineData("1c", "1", 1)]
[InlineData("1w", "1", 2)]
[InlineData("1d", "1", 4)]
[InlineData("1q", "1", 8)]
[InlineData("1k", "1", 1024)]
[InlineData("1M", "1", 1024 * 1024)]
[InlineData("1G", "1", 1024 * 1024 * 1024)]
public void ExtractFactorFromValueTest(string value, string expected, long expectedFactor)
{
string actual = BaseExecutionContext.ExtractFactorFromValue(value, out long factor);
Assert.Equal(expected, actual);
Assert.Equal(expectedFactor, factor);
}
#endregion
#region RemoveHexIdentifier
[Theory]
[InlineData("", "")]
[InlineData("0", "0")]
[InlineData("00", "00")]
[InlineData("0x", "0x")]
[InlineData("0X", "0X")]
[InlineData("A", "A")]
[InlineData("A0", "A0")]
[InlineData("Ax", "Ax")]
[InlineData("AX", "AX")]
[InlineData("012345", "012345")]
[InlineData("0x12345", "12345")]
[InlineData("0X12345", "12345")]
public void RemoveHexIdentifierTest(string value, string expected)
{
string actual = BaseExecutionContext.RemoveHexIdentifier(value);
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -0,0 +1,465 @@
using System.Collections.Generic;
using MPF.ExecutionContexts.DiscImageCreator;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.ExecutionContexts.Test
{
public class DiscImageCreatorTests
{
// TODO: Add Converters.ToRedumpSystem test
// TODO: Add Converters.ToMediaType test
#region Converters.Extension
[Theory]
[InlineData(null, null)]
[InlineData(MediaType.CDROM, ".bin")]
[InlineData(MediaType.GDROM, ".bin")]
[InlineData(MediaType.Cartridge, ".bin")]
[InlineData(MediaType.HardDisk, ".bin")]
[InlineData(MediaType.CompactFlash, ".bin")]
[InlineData(MediaType.MMC, ".bin")]
[InlineData(MediaType.SDCard, ".bin")]
[InlineData(MediaType.FlashDrive, ".bin")]
[InlineData(MediaType.DVD, ".iso")]
[InlineData(MediaType.HDDVD, ".iso")]
[InlineData(MediaType.BluRay, ".iso")]
[InlineData(MediaType.NintendoWiiOpticalDisc, ".iso")]
[InlineData(MediaType.LaserDisc, ".raw")]
[InlineData(MediaType.NintendoGameCubeGameDisc, ".raw")]
[InlineData(MediaType.NintendoWiiUOpticalDisc, ".wud")]
[InlineData(MediaType.FloppyDisk, ".img")]
[InlineData(MediaType.Cassette, ".wav")]
[InlineData(MediaType.ApertureCard, null)]
public void ExtensionTest(MediaType? type, string? expected)
{
string? actual = Converters.Extension(type);
Assert.Equal(expected, actual);
}
#endregion
#region Default Values
private static Dictionary<string, string?> AllOptions = new()
{
[SettingConstants.DVDRereadCount] = "1000",
[SettingConstants.MultiSectorRead] = "true",
[SettingConstants.MultiSectorReadValue] = "1000",
[SettingConstants.ParanoidMode] = "true",
[SettingConstants.QuietMode] = "true",
[SettingConstants.RereadCount] = "1000",
[SettingConstants.UseCMIFlag] = "true",
};
[Theory]
[InlineData(null, null, null, "filename.bin", null, null)]
[InlineData(RedumpSystem.AppleMacintosh, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "cd /dev/sr0 \"filename.bin\" 2 /c2 1000 /q /mr 1000 /ns /sf /ss /s 2")]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "cd /dev/sr0 \"filename.bin\" 2 /c2 1000 /q /mr 1000 /ns /sf /ss /s 2")]
[InlineData(RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "cd /dev/sr0 \"filename.bin\" 2 /aj /c2 1000 /q /mr 1000")]
[InlineData(RedumpSystem.HasbroVideoNow, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "cd /dev/sr0 \"filename.bin\" 2 /a 0 /c2 1000 /q /mr 1000")]
[InlineData(RedumpSystem.HasbroVideoNowColor, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "cd /dev/sr0 \"filename.bin\" 2 /a 0 /c2 1000 /q /mr 1000")]
[InlineData(RedumpSystem.HasbroVideoNowJr, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "cd /dev/sr0 \"filename.bin\" 2 /a 0 /c2 1000 /q /mr 1000")]
[InlineData(RedumpSystem.HasbroVideoNowXP, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "cd /dev/sr0 \"filename.bin\" 2 /a 0 /c2 1000 /q /mr 1000")]
[InlineData(RedumpSystem.SonyPlayStation, MediaType.CDROM, "/dev/sr0", "filename.bin", 2, "cd /dev/sr0 \"filename.bin\" 2 /c2 1000 /q /mr 1000 /nl /am")]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, "/dev/sr0", "filename.bin", 2, "dvd /dev/sr0 \"filename.bin\" 2 /c /q /rr 1000 /sf")]
[InlineData(RedumpSystem.MicrosoftXbox, MediaType.DVD, "/dev/sr0", "filename.bin", 2, "xbox /dev/sr0 \"filename.bin\" 2 /q /rr 1000")]
[InlineData(RedumpSystem.MicrosoftXbox360, MediaType.DVD, "/dev/sr0", "filename.bin", 2, "xbox /dev/sr0 \"filename.bin\" 2 /q /rr 1000")]
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, "/dev/sr0", "filename.bin", 2, "dvd /dev/sr0 \"filename.bin\" 2 /q /raw")]
[InlineData(RedumpSystem.NintendoWii, MediaType.NintendoWiiOpticalDisc, "/dev/sr0", "filename.bin", 2, "dvd /dev/sr0 \"filename.bin\" 2 /q /raw")]
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, "/dev/sr0", "filename.bin", 2, "gd /dev/sr0 \"filename.bin\" 2 /c2 1000 /q")]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, "/dev/sr0", "filename.bin", 2, "dvd /dev/sr0 \"filename.bin\" 2 /c /q /rr 1000")]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, "/dev/sr0", "filename.bin", 2, "bd /dev/sr0 \"filename.bin\" 2 /q /rr 1000")]
[InlineData(RedumpSystem.NintendoWiiU, MediaType.NintendoWiiUOpticalDisc, "/dev/sr0", "filename.bin", 2, "bd /dev/sr0 \"filename.bin\" 2 /q")]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.FloppyDisk, "/dev/sr0", "filename.bin", 2, "fd /dev/sr0 \"filename.bin\"")]
public void DefaultValueTest(RedumpSystem? system,
MediaType? type,
string? drivePath,
string filename,
int? driveSpeed,
string? expected)
{
var context = new ExecutionContext(system, type, drivePath, filename, driveSpeed, AllOptions);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
}
#endregion
#region Audio
[Theory]
[InlineData("audio F filename.bin 0 1 2 /be raw /c2 1 2 3 1 5 6 /c2new 1 /d8 /d /q /f 0 /np /nq /nr /r /am /sf 1 /ss /sk 1 0 /s 0 /t")]
public void AudioTest(string parameters)
{
string? expected = "audio F \"filename.bin\" 0 1 2 /be raw /c2 1 2 3 1 5 6 /c2new 1 /d8 /d /q /f 0 /np /nq /nr /r /am /sf 1 /ss /sk 1 0 /s 0 /t";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region BluRay
[Theory]
[InlineData("bd F filename.bin 0 /d /q /rr 0 /f 0 /ra /avdp")]
public void BluRayTest(string parameters)
{
string? expected = "bd F \"filename.bin\" 0 /d /q /rr 0 /f 0 /ra /avdp";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region Close
[Theory]
[InlineData("close f")]
public void CloseTest(string parameters)
{
string? expected = "close f";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region CompactDisc
[Theory]
[InlineData("cd f filename.bin 0 /a 0 /p /aj /be raw /c2 1 2 3 1 5 6 /c2new 1 /d8 /d /q /mscf /fdesc sync edc /f 0 /fulltoc /mr 0 /np /nq /nl /ns /nr /am /sf 1 /ss /74 /s 0 /toc /trp /vn 0 /vnc /vnx")]
public void CompactDiscTest(string parameters)
{
string? expected = "cd f \"filename.bin\" 0 /a 0 /p /aj /be raw /c2 1 2 3 1 5 6 /c2new 1 /d8 /d /q /mscf /fdesc sync edc /f 0 /fulltoc /mr 0 /np /nq /nl /ns /nr /am /sf 1 /ss /74 /s 0 /toc /trp /vn 0 /vnc /vnx";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region Data
[Theory]
[InlineData("data F filename.bin 0 1 2 /be raw /c2 1 2 3 1 5 6 /c2new 1 /d8 /d /q /f 0 /np /nq /nr /r /am /sf 1 /ss /sk 1 0 /s 0 /t")]
public void DataTest(string parameters)
{
string? expected = "data F \"filename.bin\" 0 1 2 /be raw /c2 1 2 3 1 5 6 /c2new 1 /d8 /d /q /f 0 /np /nq /nr /r /am /sf 1 /ss /sk 1 0 /s 0 /t";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region DigitalVideoDisc
[Theory]
[InlineData("dvd F filename.bin 0 /c /d /q /rr 0 /fix 0 /ps 0 /ra 0 1 /raw /re /r 0 1 /sf 1 /sk 1 0 /avdp")]
public void DigitalVideoDiscTest(string parameters)
{
string? expected = "dvd F \"filename.bin\" 0 /c /d /q /rr 0 /fix 0 /ps 0 /ra 0 1 /raw /re /r 0 1 /sf 1 /sk 1 0 /avdp";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region Disk
[Theory]
[InlineData("disk F filename.bin /d")]
public void DiskTest(string parameters)
{
string? expected = "disk F \"filename.bin\" /d";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region DriveSpeed
[Theory]
[InlineData("ls f")]
public void DriveSpeedTest(string parameters)
{
string? expected = "ls f";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Eject
[Theory]
[InlineData("eject f")]
public void EjectTest(string parameters)
{
string? expected = "eject f";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Floppy
[Theory]
[InlineData("fd F filename.bin /d")]
public void FloppyTest(string parameters)
{
string? expected = "fd F \"filename.bin\" /d";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region GDROM
[Theory]
[InlineData("gd f filename.bin 0 /be raw /c2 1 2 3 1 5 6 /c2new 1 /d8 /d /q /f 0 /np /nq /nr /s 0")]
public void GDROMTest(string parameters)
{
string? expected = "gd f \"filename.bin\" 0 /be raw /c2 1 2 3 1 5 6 /c2new 1 /d8 /d /q /f 0 /np /nq /nr /s 0";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region MDS
[Theory]
[InlineData("mds filename.bin")]
public void MDSTest(string parameters)
{
string? expected = "mds \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Merge
[Theory]
[InlineData("merge input1.bin input2.bin")]
public void MergeTest(string parameters)
{
string? expected = "merge \"input1.bin\" \"input2.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Reset
[Theory]
[InlineData("reset f")]
public void ResetTest(string parameters)
{
string? expected = "reset f";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region SACD
[Theory]
[InlineData("sacd f filename.bin 0 /d /q")]
public void SACDTest(string parameters)
{
string? expected = "sacd f \"filename.bin\" 0 /d /q";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region Start
[Theory]
[InlineData("start f")]
public void StartTest(string parameters)
{
string? expected = "start f";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Stop
[Theory]
[InlineData("stop f")]
public void StopTest(string parameters)
{
string? expected = "stop f";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Sub
[Theory]
[InlineData("sub filename.bin")]
public void SubTest(string parameters)
{
string? expected = "sub \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Swap
[Theory]
[InlineData("swap f filename.bin 0 /a 0 /be raw /c2 1 2 3 1 5 6 /c2new 1 /d8 /d /q /f 0 /np /nq /nl /ns /nr /am /sf 1 /ss /74 /s 0 /trp /vn 0 /vnc /vnx")]
public void SwapTest(string parameters)
{
string? expected = "swap f \"filename.bin\" 0 /a 0 /be raw /c2 1 2 3 1 5 6 /c2new 1 /d8 /d /q /f 0 /np /nq /nl /ns /nr /am /sf 1 /ss /74 /s 0 /trp /vn 0 /vnc /vnx";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region Tape
[Theory]
[InlineData("tape filename.bin")]
public void TapeTest(string parameters)
{
string? expected = "tape \"filename.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region Version
[Theory]
[InlineData("/v")]
public void VersionTest(string parameters)
{
string? expected = "/v";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region XBOX
[Theory]
[InlineData("xbox f filename.bin 0 /d /q /rr 0 /f 0 /nss 0")]
public void XBOXTest(string parameters)
{
string? expected = "xbox f \"filename.bin\" 0 /d /q /rr 0 /f 0 /nss 0";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region XBOXSwap
[Theory]
[InlineData("xboxswap f filename.bin 0 /d /q /f 0 /nss 0")]
public void XBOXSwapTest(string parameters)
{
string? expected = "xboxswap f \"filename.bin\" 0 /d /q /f 0 /nss 0";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region XGD2Swap
[Theory]
[InlineData("xgd2swap f filename.bin 0 /d /q /f 0 /nss 0")]
public void XGD2SwapTest(string parameters)
{
string? expected = "xgd2swap f \"filename.bin\" 0 /d /q /f 0 /nss 0";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region XGD3Swap
[Theory]
[InlineData("xgd3swap f filename.bin 0 /d /q /f 0 /nss 0")]
public void XGD3SwapTest(string parameters)
{
string? expected = "xgd3swap f \"filename.bin\" 0 /d /q /f 0 /nss 0";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
}
}

View File

@@ -0,0 +1,410 @@
using MPF.ExecutionContexts.Data;
using Xunit;
namespace MPF.ExecutionContexts.Test
{
public class InputTests
{
#region FlagInput
[Theory]
// Invalid parts
[InlineData("flag", new string[0], 0, false, false)]
// Invalid index
[InlineData("flag", new string[] { "flag" }, -1, false, false)]
[InlineData("flag", new string[] { "flag" }, 1, false, false)]
// Invalid name
[InlineData("flag", new string[] { "" }, 0, false, false)]
[InlineData("flag", new string[] { "flag2" }, 0, false, false)]
// Valid
[InlineData("flag", new string[] { "flag" }, 0, true, true)]
public void FlagInputTest(string name, string[] parts, int index, bool success, bool expected)
{
FlagInput input = new FlagInput(name);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region BooleanInput
[Theory]
// Invalid parts
[InlineData("flag", true, new string[0], 0, false, null)]
// Invalid index
[InlineData("flag", true, new string[] { "flag" }, -1, false, null)]
[InlineData("flag", true, new string[] { "flag" }, 1, false, null)]
// Invalid name
[InlineData("flag", true, new string[] { "" }, 0, false, null)]
[InlineData("flag", true, new string[] { "flag2" }, 0, false, null)]
// Valid name, no following
[InlineData("flag", true, new string[] { "flag" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag" }, 0, true, true)]
// Valid name, invalid following
[InlineData("flag", true, new string[] { "flag", "invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag", "invalid" }, 0, true, true)]
[InlineData("flag", true, new string[] { "flag=invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag=invalid" }, 0, true, true)]
// Valid name, valid following
[InlineData("flag", true, new string[] { "flag", "true" }, 0, true, true)]
[InlineData("flag", true, new string[] { "flag", "false" }, 0, true, false)]
[InlineData("flag", true, new string[] { "flag=true" }, 0, true, true)]
[InlineData("flag", true, new string[] { "flag=false" }, 0, true, false)]
public void BooleanInputTest(string name, bool required, string[] parts, int index, bool success, bool? expected)
{
BooleanInput input = new BooleanInput(name, required);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region Int8Input
[Theory]
// Invalid parts
[InlineData("flag", true, new string[0], 0, false, null)]
// Invalid index
[InlineData("flag", true, new string[] { "flag" }, -1, false, null)]
[InlineData("flag", true, new string[] { "flag" }, 1, false, null)]
// Invalid name
[InlineData("flag", true, new string[] { "" }, 0, false, null)]
[InlineData("flag", true, new string[] { "flag2" }, 0, false, null)]
// Valid name, no following
[InlineData("flag", true, new string[] { "flag" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag" }, 0, true, sbyte.MinValue)]
// Valid name, invalid following
[InlineData("flag", true, new string[] { "flag", "invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag", "invalid" }, 0, true, sbyte.MinValue)]
[InlineData("flag", true, new string[] { "flag=invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag=invalid" }, 0, true, sbyte.MinValue)]
// Valid name, valid following
[InlineData("flag", true, new string[] { "flag", "1" }, 0, true, (sbyte)1)]
[InlineData("flag", true, new string[] { "flag", "-1" }, 0, true, (sbyte)-1)]
[InlineData("flag", true, new string[] { "flag=1" }, 0, true, (sbyte)1)]
[InlineData("flag", true, new string[] { "flag=-1" }, 0, true, (sbyte)-1)]
public void Int8InputTest(string name, bool required, string[] parts, int index, bool success, sbyte? expected)
{
Int8Input input = new Int8Input(name, required);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region UInt8Input
[Theory]
// Invalid parts
[InlineData("flag", true, new string[0], 0, false, null)]
// Invalid index
[InlineData("flag", true, new string[] { "flag" }, -1, false, null)]
[InlineData("flag", true, new string[] { "flag" }, 1, false, null)]
// Invalid name
[InlineData("flag", true, new string[] { "" }, 0, false, null)]
[InlineData("flag", true, new string[] { "flag2" }, 0, false, null)]
// Valid name, no following
[InlineData("flag", true, new string[] { "flag" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag" }, 0, true, byte.MinValue)]
// Valid name, invalid following
[InlineData("flag", true, new string[] { "flag", "invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag", "invalid" }, 0, true, byte.MinValue)]
[InlineData("flag", true, new string[] { "flag=invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag=invalid" }, 0, true, byte.MinValue)]
// Valid name, valid following
[InlineData("flag", true, new string[] { "flag", "1" }, 0, true, (byte)1)]
[InlineData("flag", true, new string[] { "flag=1" }, 0, true, (byte)1)]
public void UInt8InputTest(string name, bool required, string[] parts, int index, bool success, byte? expected)
{
UInt8Input input = new UInt8Input(name, required);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region Int16Input
[Theory]
// Invalid parts
[InlineData("flag", true, new string[0], 0, false, null)]
// Invalid index
[InlineData("flag", true, new string[] { "flag" }, -1, false, null)]
[InlineData("flag", true, new string[] { "flag" }, 1, false, null)]
// Invalid name
[InlineData("flag", true, new string[] { "" }, 0, false, null)]
[InlineData("flag", true, new string[] { "flag2" }, 0, false, null)]
// Valid name, no following
[InlineData("flag", true, new string[] { "flag" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag" }, 0, true, short.MinValue)]
// Valid name, invalid following
[InlineData("flag", true, new string[] { "flag", "invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag", "invalid" }, 0, true, short.MinValue)]
[InlineData("flag", true, new string[] { "flag=invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag=invalid" }, 0, true, short.MinValue)]
// Valid name, valid following
[InlineData("flag", true, new string[] { "flag", "1" }, 0, true, (short)1)]
[InlineData("flag", true, new string[] { "flag", "-1" }, 0, true, (short)-1)]
[InlineData("flag", true, new string[] { "flag=1" }, 0, true, (short)1)]
[InlineData("flag", true, new string[] { "flag=-1" }, 0, true, (short)-1)]
public void Int16InputTest(string name, bool required, string[] parts, int index, bool success, short? expected)
{
Int16Input input = new Int16Input(name, required);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region UInt16Input
[Theory]
// Invalid parts
[InlineData("flag", true, new string[0], 0, false, null)]
// Invalid index
[InlineData("flag", true, new string[] { "flag" }, -1, false, null)]
[InlineData("flag", true, new string[] { "flag" }, 1, false, null)]
// Invalid name
[InlineData("flag", true, new string[] { "" }, 0, false, null)]
[InlineData("flag", true, new string[] { "flag2" }, 0, false, null)]
// Valid name, no following
[InlineData("flag", true, new string[] { "flag" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag" }, 0, true, ushort.MinValue)]
// Valid name, invalid following
[InlineData("flag", true, new string[] { "flag", "invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag", "invalid" }, 0, true, ushort.MinValue)]
[InlineData("flag", true, new string[] { "flag=invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag=invalid" }, 0, true, ushort.MinValue)]
// Valid name, valid following
[InlineData("flag", true, new string[] { "flag", "1" }, 0, true, (ushort)1)]
[InlineData("flag", true, new string[] { "flag=1" }, 0, true, (ushort)1)]
public void UInt16InputTest(string name, bool required, string[] parts, int index, bool success, ushort? expected)
{
UInt16Input input = new UInt16Input(name, required);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region Int32Input
[Theory]
// Invalid parts
[InlineData("flag", true, new string[0], 0, false, null)]
// Invalid index
[InlineData("flag", true, new string[] { "flag" }, -1, false, null)]
[InlineData("flag", true, new string[] { "flag" }, 1, false, null)]
// Invalid name
[InlineData("flag", true, new string[] { "" }, 0, false, null)]
[InlineData("flag", true, new string[] { "flag2" }, 0, false, null)]
// Valid name, no following
[InlineData("flag", true, new string[] { "flag" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag" }, 0, true, int.MinValue)]
// Valid name, invalid following
[InlineData("flag", true, new string[] { "flag", "invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag", "invalid" }, 0, true, int.MinValue)]
[InlineData("flag", true, new string[] { "flag=invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag=invalid" }, 0, true, int.MinValue)]
// Valid name, valid following
[InlineData("flag", true, new string[] { "flag", "1" }, 0, true, (int)1)]
[InlineData("flag", true, new string[] { "flag", "-1" }, 0, true, (int)-1)]
[InlineData("flag", true, new string[] { "flag=1" }, 0, true, (int)1)]
[InlineData("flag", true, new string[] { "flag=-1" }, 0, true, (int)-1)]
public void Int32InputTest(string name, bool required, string[] parts, int index, bool success, int? expected)
{
Int32Input input = new Int32Input(name, required);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region UInt32Input
[Theory]
// Invalid parts
[InlineData("flag", true, new string[0], 0, false, null)]
// Invalid index
[InlineData("flag", true, new string[] { "flag" }, -1, false, null)]
[InlineData("flag", true, new string[] { "flag" }, 1, false, null)]
// Invalid name
[InlineData("flag", true, new string[] { "" }, 0, false, null)]
[InlineData("flag", true, new string[] { "flag2" }, 0, false, null)]
// Valid name, no following
[InlineData("flag", true, new string[] { "flag" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag" }, 0, true, uint.MinValue)]
// Valid name, invalid following
[InlineData("flag", true, new string[] { "flag", "invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag", "invalid" }, 0, true, uint.MinValue)]
[InlineData("flag", true, new string[] { "flag=invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag=invalid" }, 0, true, uint.MinValue)]
// Valid name, valid following
[InlineData("flag", true, new string[] { "flag", "1" }, 0, true, (uint)1)]
[InlineData("flag", true, new string[] { "flag=1" }, 0, true, (uint)1)]
public void UInt32InputTest(string name, bool required, string[] parts, int index, bool success, uint? expected)
{
UInt32Input input = new UInt32Input(name, required);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region Int64Input
[Theory]
// Invalid parts
[InlineData("flag", true, new string[0], 0, false, null)]
// Invalid index
[InlineData("flag", true, new string[] { "flag" }, -1, false, null)]
[InlineData("flag", true, new string[] { "flag" }, 1, false, null)]
// Invalid name
[InlineData("flag", true, new string[] { "" }, 0, false, null)]
[InlineData("flag", true, new string[] { "flag2" }, 0, false, null)]
// Valid name, no following
[InlineData("flag", true, new string[] { "flag" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag" }, 0, true, long.MinValue)]
// Valid name, invalid following
[InlineData("flag", true, new string[] { "flag", "invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag", "invalid" }, 0, true, long.MinValue)]
[InlineData("flag", true, new string[] { "flag=invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag=invalid" }, 0, true, long.MinValue)]
// Valid name, valid following
[InlineData("flag", true, new string[] { "flag", "1" }, 0, true, (long)1)]
[InlineData("flag", true, new string[] { "flag", "-1" }, 0, true, (long)-1)]
[InlineData("flag", true, new string[] { "flag=1" }, 0, true, (long)1)]
[InlineData("flag", true, new string[] { "flag=-1" }, 0, true, (long)-1)]
public void Int64InputTest(string name, bool required, string[] parts, int index, bool success, long? expected)
{
Int64Input input = new Int64Input(name, required);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region UInt64Input
[Theory]
// Invalid parts
[InlineData("flag", true, new string[0], 0, false, null)]
// Invalid index
[InlineData("flag", true, new string[] { "flag" }, -1, false, null)]
[InlineData("flag", true, new string[] { "flag" }, 1, false, null)]
// Invalid name
[InlineData("flag", true, new string[] { "" }, 0, false, null)]
[InlineData("flag", true, new string[] { "flag2" }, 0, false, null)]
// Valid name, no following
[InlineData("flag", true, new string[] { "flag" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag" }, 0, true, ulong.MinValue)]
// Valid name, invalid following
[InlineData("flag", true, new string[] { "flag", "invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag", "invalid" }, 0, true, ulong.MinValue)]
[InlineData("flag", true, new string[] { "flag=invalid" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag=invalid" }, 0, true, ulong.MinValue)]
// Valid name, valid following
[InlineData("flag", true, new string[] { "flag", "1" }, 0, true, (ulong)1)]
[InlineData("flag", true, new string[] { "flag=1" }, 0, true, (ulong)1)]
public void UInt64InputTest(string name, bool required, string[] parts, int index, bool success, ulong? expected)
{
UInt64Input input = new UInt64Input(name, required);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region StringInput
[Theory]
// Invalid parts
[InlineData("flag", true, new string[0], 0, false, null)]
// Invalid index
[InlineData("flag", true, new string[] { "flag" }, -1, false, null)]
[InlineData("flag", true, new string[] { "flag" }, 1, false, null)]
// Invalid name
[InlineData("flag", true, new string[] { "" }, 0, false, null)]
[InlineData("flag", true, new string[] { "flag2" }, 0, false, null)]
// Valid name, no following
[InlineData("flag", true, new string[] { "flag" }, 0, false, null)]
[InlineData("flag", false, new string[] { "flag" }, 0, true, "")]
// Valid name, following
[InlineData("flag", true, new string[] { "flag", "value" }, 0, true, "value")]
[InlineData("flag", true, new string[] { "flag=value" }, 0, true, "value")]
public void StringInputTest(string name, bool required, string[] parts, int index, bool success, string? expected)
{
StringInput input = new StringInput(name, required);
bool actual = input.Process(parts, ref index);
Assert.Equal(success, actual);
Assert.Equal(expected, input.Value);
}
#endregion
#region ExtractFactorFromValue
[Theory]
[InlineData("1", "1", 1)]
[InlineData("1c", "1", 1)]
[InlineData("1w", "1", 2)]
[InlineData("1d", "1", 4)]
[InlineData("1q", "1", 8)]
[InlineData("1k", "1", 1024)]
[InlineData("1M", "1", 1024 * 1024)]
[InlineData("1G", "1", 1024 * 1024 * 1024)]
public void ExtractFactorFromValueTest(string value, string expected, long expectedFactor)
{
string actual = Input.ExtractFactorFromValue(value, out long factor);
Assert.Equal(expected, actual);
Assert.Equal(expectedFactor, factor);
}
#endregion
#region RemoveHexIdentifier
[Theory]
[InlineData("", "")]
[InlineData("0", "0")]
[InlineData("00", "00")]
[InlineData("0x", "0x")]
[InlineData("0X", "0X")]
[InlineData("A", "A")]
[InlineData("A0", "A0")]
[InlineData("Ax", "Ax")]
[InlineData("AX", "AX")]
[InlineData("012345", "012345")]
[InlineData("0x12345", "12345")]
[InlineData("0X12345", "12345")]
public void RemoveHexIdentifierTest(string value, string expected)
{
string actual = Input.RemoveHexIdentifier(value);
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
@@ -11,26 +11,25 @@
<ItemGroup>
<ProjectReference Include="..\MPF.ExecutionContexts\MPF.ExecutionContexts.csproj" />
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="17.11.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.4" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="Microsoft.CodeCoverage" Version="17.14.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="1.16.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">
<PackageReference Include="xunit.analyzers" Version="1.24.0" />
<PackageReference Include="xunit.assert" Version="2.9.3" />
<PackageReference Include="xunit.core" Version="2.9.3" />
<PackageReference Include="xunit.extensibility.core" Version="2.9.3" />
<PackageReference Include="xunit.extensibility.execution" Version="2.9.3" />
<PackageReference Include="xunit.runner.console" Version="2.9.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -0,0 +1,378 @@
using System.Collections.Generic;
using MPF.ExecutionContexts.Redumper;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.ExecutionContexts.Test
{
public class RedumperTests
{
#region Converters.Extension
[Theory]
[InlineData(null, null)]
[InlineData(MediaType.CDROM, ".bin")]
[InlineData(MediaType.GDROM, ".bin")]
[InlineData(MediaType.DVD, ".iso")]
[InlineData(MediaType.HDDVD, ".iso")]
[InlineData(MediaType.BluRay, ".iso")]
[InlineData(MediaType.NintendoWiiOpticalDisc, ".iso")]
[InlineData(MediaType.NintendoGameCubeGameDisc, ".raw")]
[InlineData(MediaType.NintendoWiiUOpticalDisc, ".wud")]
[InlineData(MediaType.ApertureCard, null)]
public void ExtensionTest(MediaType? type, string? expected)
{
string? actual = Converters.Extension(type);
Assert.Equal(expected, actual);
}
#endregion
#region Default Values
private static Dictionary<string, string?> AllOptions = new()
{
[SettingConstants.EnableVerbose] = "true",
[SettingConstants.LeadinRetryCount] = "1000",
[SettingConstants.ReadMethod] = "BE",
[SettingConstants.RereadCount] = "1000",
[SettingConstants.SectorOrder] = "DATA_C2_SUB",
[SettingConstants.DriveType] = "GENERIC",
};
[Theory]
[InlineData(null, null, null, "filename.bin", null, "")]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --skeleton --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
[InlineData(RedumpSystem.NintendoWii, MediaType.NintendoWiiOpticalDisc, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
[InlineData(RedumpSystem.NintendoWiiU, MediaType.NintendoWiiUOpticalDisc, "/dev/sr0", "path/filename.bin", 2, "disc --verbose --drive=/dev/sr0 --speed=2 --retries=1000 --image-path=\"path\" --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
public void DefaultValueTest(RedumpSystem? system,
MediaType? type,
string? drivePath,
string filename,
int? driveSpeed,
string? expected)
{
var context = new ExecutionContext(system, type, drivePath, filename, driveSpeed, AllOptions);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
}
#endregion
#region Disc
[Theory]
[InlineData("disc -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("disc --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void DiscTest(string parameters)
{
string? expected = "disc --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
[Theory]
[InlineData("disc --drive=dr --image-path=\"directory name\" --image-name=\"image name.bin\"")]
public void SpacesTest(string parameters)
{
string? expected = "disc --drive=dr --image-path=\"directory name\" --image-name=\"image name.bin\"";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
#endregion
#region Rings
[Theory]
[InlineData("rings -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("rings --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void RingsTest(string parameters)
{
string? expected = "rings --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Dump
[Theory]
[InlineData("dump -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("dump --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void DumpTest(string parameters)
{
string? expected = "dump --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region DumpExtra
[Theory]
[InlineData("dump::extra -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("dump::extra --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void DumpExtraTest(string parameters)
{
string? expected = "dump::extra --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Refine
[Theory]
[InlineData("refine -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("refine --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void RefineTest(string parameters)
{
string? expected = "refine --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Verify
[Theory]
[InlineData("verify -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("verify --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void VerifyTest(string parameters)
{
string? expected = "verify --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region DVDKey
[Theory]
[InlineData("dvdkey -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("dvdkey --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void DVDKeyTest(string parameters)
{
string? expected = "dvdkey --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Eject
[Theory]
[InlineData("eject -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("eject --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void EjectTest(string parameters)
{
string? expected = "eject --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region DVDIsoKey
[Theory]
[InlineData("dvdisokey -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("dvdisokey --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void DVDIsoKeyTest(string parameters)
{
string? expected = "dvdisokey --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Protection
[Theory]
[InlineData("protection -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("protection --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs --disable-cdtext")]
public void ProtectionTest(string parameters)
{
string? expected = "protection --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Split
[Theory]
[InlineData("split -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("split --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void SplitTest(string parameters)
{
string? expected = "split --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Hash
[Theory]
[InlineData("hash -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("hash --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void HashTest(string parameters)
{
string? expected = "hash --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Info
[Theory]
[InlineData("info -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("info --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void InfoTest(string parameters)
{
string? expected = "info --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Skeleton
[Theory]
[InlineData("skeleton -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("skeleton --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void SkeletonTest(string parameters)
{
string? expected = "skeleton --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Subchannel
[Theory]
[InlineData("subchannel -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("subchannel --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void SubchannelTest(string parameters)
{
string? expected = "subchannel --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region Debug
[Theory]
[InlineData("debug -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("debug --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void DebugTest(string parameters)
{
string? expected = "debug --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(context.IsDumpingCommand());
}
#endregion
#region FixMSF
[Theory]
[InlineData("fixmsf -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("fixmsf --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void FixMSFTest(string parameters)
{
string? expected = "fixmsf --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
}
#endregion
#region DebugFlip
[Theory]
[InlineData("debug::flip -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("debug::flip --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void DebugFlipTest(string parameters)
{
string? expected = "debug::flip --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
}
#endregion
#region DriveTest
[Theory]
[InlineData("drive::test -h --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
[InlineData("drive::test --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=path --image-name=image --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs")]
public void DriveTestTest(string parameters)
{
string? expected = "drive::test --help --version --verbose --auto-eject --skeleton --drive=dr --speed=8 --retries=0 --image-path=\"path\" --image-name=\"image\" --overwrite --drive-type=dt --drive-read-offset=0 --drive-c2-shift=0 --drive-pregap-start=0 --drive-read-method=drm --drive-sector-order=dso --plextor-skip-leadin --plextor-leadin-retries=0 --asus-skip-leadout --disable-cdtext --force-offset=0 --audio-silence-threshold=0 --correct-offset-shift --offset-shift-relocate --force-split --leave-unchanged --force-qtoc --skip-fill=0 --iso9660-trim --lba-start=0 --lba-end=0 --refine-subchannel --refine-sector-mode --skip=0 --dump-write-offset=0 --dump-read-size=0 --overread-leadout --force-unscrambled --legacy-subs";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -7,25 +7,35 @@ namespace MPF.ExecutionContexts.Aaru
{
public const string NONE = "";
// Archive Family
#region Archive Family
public const string ArchivePrefixShort = "arc";
public const string ArchivePrefixLong = "archive";
public const string ArchiveInfo = "info";
// Database Family
#endregion
#region Database Family
public const string DatabasePrefixShort = "db";
public const string DatabasePrefixLong = "database";
public const string DatabaseStats = "stats";
public const string DatabaseUpdate = "update";
// Device Family
#endregion
#region Device Family
public const string DevicePrefixShort = "dev";
public const string DevicePrefixLong = "device";
public const string DeviceInfo = "info";
public const string DeviceList = "list";
public const string DeviceReport = "report";
// Filesystem Family
#endregion
#region Filesystem Family
public const string FilesystemPrefixShort = "fi";
public const string FilesystemPrefixShortAlt = "fs";
public const string FilesystemPrefixLong = "filesystem";
@@ -35,7 +45,10 @@ namespace MPF.ExecutionContexts.Aaru
public const string FilesystemListLong = "list";
public const string FilesystemOptions = "options";
// Image Family
#endregion
#region Image Family
public const string ImagePrefixShort = "i";
public const string ImagePrefixLong = "image";
public const string ImageChecksumShort = "chk";
@@ -51,18 +64,26 @@ namespace MPF.ExecutionContexts.Aaru
public const string ImagePrint = "print";
public const string ImageVerify = "verify";
// Media Family
#endregion
#region Media Family
public const string MediaPrefixShort = "m";
public const string MediaPrefixLong = "media";
public const string MediaDump = "dump";
public const string MediaInfo = "info";
public const string MediaScan = "scan";
// Standalone Commands
#endregion
#region Standalone Commands
public const string Configure = "configure";
public const string Formats = "formats";
public const string ListEncodings = "list-encodings";
public const string ListNamespaces = "list-namespaces";
public const string Remote = "remote";
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,21 @@ namespace MPF.ExecutionContexts.Aaru
/// </summary>
public static class FlagStrings
{
// Boolean flags
#region Precommand Flags
public const string DebugShort = "-d";
public const string DebugLong = "--debug";
public const string HelpShort = "-h";
public const string HelpShortAlt = "-?";
public const string HelpLong = "--help";
public const string VerboseShort = "-v";
public const string VerboseLong = "--verbose";
public const string VersionLong = "--version";
#endregion
#region Boolean flags
public const string Adler32Short = "-a";
public const string Adler32Long = "--adler32";
public const string ClearLong = "--clear";
@@ -14,8 +28,6 @@ namespace MPF.ExecutionContexts.Aaru
public const string CRC32Short = "-c";
public const string CRC32Long = "--crc32";
public const string CRC64Long = "--crc64";
public const string DebugShort = "-d";
public const string DebugLong = "--debug";
public const string DiskTagsShort = "-f";
public const string DiskTagsLong = "--disk-tags";
public const string DuplicatedSectorsShort = "-p";
@@ -35,9 +47,6 @@ namespace MPF.ExecutionContexts.Aaru
public const string ForceShort = "-f";
public const string ForceLong = "--force";
public const string GenerateSubchannelsLong = "--generate-subchannels";
public const string HelpShort = "-h";
public const string HelpShortAlt = "-?";
public const string HelpLong = "--help";
public const string LongFormatShort = "-l";
public const string LongFormatLong = "--long-format";
public const string LongSectorsShort = "-r";
@@ -75,26 +84,32 @@ namespace MPF.ExecutionContexts.Aaru
public const string TrapDiscLong = "--trap-disc";
public const string TrimLong = "--trim";
public const string UseBufferedReadsLong = "--use-buffered-reads";
public const string VerboseShort = "-v";
public const string VerboseLong = "--verbose";
public const string VerifyDiscShort = "-w";
public const string VerifyDiscLong = "--verify-disc";
public const string VerifySectorsShort = "-s";
public const string VerifySectorsLong = "--verify-sectors";
public const string VersionLong = "--version";
public const string WholeDiscShort = "-w";
public const string WholeDiscLong = "--whole-disc";
// Int8 flags
#endregion
#region Int8 flags
public const string SpeedLong = "--speed";
// Int16 flags
#endregion
#region Int16 flags
public const string RetryPassesShort = "-p";
public const string RetryPassesLong = "--retry-passes";
public const string WidthShort = "-w";
public const string WidthLong = "--width";
// Int32 flags
#endregion
#region Int32 flags
public const string BlockSizeShort = "-b";
public const string BlockSizeLong = "--block-size";
public const string CountShort = "-c";
@@ -105,13 +120,19 @@ namespace MPF.ExecutionContexts.Aaru
public const string SkipShort = "-k";
public const string SkipLong = "--skip";
// Int64 flags
#endregion
#region Int64 flags
public const string LengthShort = "-l"; // or "all"
public const string LengthLong = "--length"; // or "all"
public const string StartShort = "-s";
public const string StartLong = "--start";
// String flags
#endregion
#region String flags
public const string CommentsLong = "--comments";
public const string CreatorLong = "--creator";
public const string DriveManufacturerLong = "--drive-manufacturer";
@@ -121,9 +142,8 @@ namespace MPF.ExecutionContexts.Aaru
public const string EncodingShort = "-e";
public const string EncodingLong = "--encoding";
public const string FormatConvertShort = "-p";
public const string FormatConvertLong = "--format";
public const string FormatDumpShort = "-t";
public const string FormatDumpLong = "--format";
public const string FormatLong = "--format";
public const string GeometryShort = "-g";
public const string GeometryLong = "--geometry";
public const string ImgBurnLogShort = "-b";
@@ -147,5 +167,7 @@ namespace MPF.ExecutionContexts.Aaru
public const string SubchannelLong = "--subchannel";
public const string XMLSidecarShort = "-x";
public const string XMLSidecarLong = "--cicm-xml";
#endregion
}
}

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
@@ -234,7 +242,7 @@ namespace MPF.ExecutionContexts
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
protected static bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
internal static bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
{
if (settings.ContainsKey(key))
{
@@ -256,7 +264,7 @@ namespace MPF.ExecutionContexts
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
protected static int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
internal static int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
{
if (settings.ContainsKey(key))
{
@@ -278,7 +286,7 @@ namespace MPF.ExecutionContexts
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
protected static string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
internal static string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
{
if (settings.ContainsKey(key))
return settings[key];
@@ -290,14 +298,31 @@ namespace MPF.ExecutionContexts
#region Parameter Parsing
/// <summary>
/// Split a parameters string into a list while taking quotes into account
/// </summary>
internal static 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-Z0-9\-]*=)?[\""].+?[\""]|[^ ]+", 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>
/// <param name="parameters">List of parameters to check against</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="index">Current index</param>
/// <returns>True if the next item exists, false otherwise</returns>
protected static bool DoesExist(List<string> parameters, int index)
=> index < parameters.Count;
internal static bool DoesExist(string[] parts, int index)
=> index >= 0 && index < parts.Length;
/// <summary>
/// Gets if the flag is supported by the current command
@@ -308,9 +333,9 @@ 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);
}
@@ -320,7 +345,7 @@ namespace MPF.ExecutionContexts
/// </summary>
/// <param name="parameter">String value to check</param>
/// <returns>True if it's a valid bool, false otherwise</returns>
protected static bool IsValidBool(string parameter)
internal static bool IsValidBool(string parameter)
=> bool.TryParse(parameter, out bool _);
/// <summary>
@@ -330,14 +355,14 @@ namespace MPF.ExecutionContexts
/// <param name="lowerBound">Lower bound (>=)</param>
/// <param name="upperBound">Upper bound (<=)</param>
/// <returns>True if it's a valid byte, false otherwise</returns>
protected static bool IsValidInt8(string parameter, sbyte lowerBound = -1, sbyte upperBound = -1)
internal static bool IsValidInt8(string parameter, sbyte? lowerBound = null, sbyte? upperBound = null)
{
string value = ExtractFactorFromValue(parameter, out _);
if (!sbyte.TryParse(value, out sbyte temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
else if (lowerBound != null && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
else if (upperBound != null && temp > upperBound)
return false;
return true;
@@ -350,14 +375,14 @@ namespace MPF.ExecutionContexts
/// <param name="lowerBound">Lower bound (>=)</param>
/// <param name="upperBound">Upper bound (<=)</param>
/// <returns>True if it's a valid Int16, false otherwise</returns>
protected static bool IsValidInt16(string parameter, short lowerBound = -1, short upperBound = -1)
internal static bool IsValidInt16(string parameter, short? lowerBound = null, short? upperBound = null)
{
string value = ExtractFactorFromValue(parameter, out _);
if (!short.TryParse(value, out short temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
else if (lowerBound != null && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
else if (upperBound != null && temp > upperBound)
return false;
return true;
@@ -370,14 +395,14 @@ namespace MPF.ExecutionContexts
/// <param name="lowerBound">Lower bound (>=)</param>
/// <param name="upperBound">Upper bound (<=)</param>
/// <returns>True if it's a valid Int32, false otherwise</returns>
protected static bool IsValidInt32(string parameter, int lowerBound = -1, int upperBound = -1)
internal static bool IsValidInt32(string parameter, int? lowerBound = null, int? upperBound = null)
{
string value = ExtractFactorFromValue(parameter, out _);
if (!int.TryParse(value, out int temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
else if (lowerBound != null && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
else if (upperBound != null && temp > upperBound)
return false;
return true;
@@ -390,14 +415,14 @@ namespace MPF.ExecutionContexts
/// <param name="lowerBound">Lower bound (>=)</param>
/// <param name="upperBound">Upper bound (<=)</param>
/// <returns>True if it's a valid Int64, false otherwise</returns>
protected static bool IsValidInt64(string parameter, long lowerBound = -1, long upperBound = -1)
internal static bool IsValidInt64(string parameter, long? lowerBound = null, long? upperBound = null)
{
string value = ExtractFactorFromValue(parameter, out _);
if (!long.TryParse(value, out long temp))
return false;
else if (lowerBound != -1 && temp < lowerBound)
else if (lowerBound != null && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
else if (upperBound != null && temp > upperBound)
return false;
return true;
@@ -406,22 +431,22 @@ namespace MPF.ExecutionContexts
/// <summary>
/// Process a flag parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
protected bool ProcessFlagParameter(List<string> parts, string flagString, ref int i)
protected bool ProcessFlagParameter(string[] parts, string flagString, ref int i)
=> ProcessFlagParameter(parts, null, flagString, ref i);
/// <summary>
/// Process a flag parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
protected bool ProcessFlagParameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i)
protected bool ProcessFlagParameter(string[] parts, string? shortFlagString, string longFlagString, ref int i)
{
if (parts == null)
return false;
@@ -440,24 +465,24 @@ namespace MPF.ExecutionContexts
/// <summary>
/// Process a boolean parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
protected bool ProcessBooleanParameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
protected bool ProcessBooleanParameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessBooleanParameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process a boolean parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
protected bool ProcessBooleanParameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
protected bool ProcessBooleanParameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return false;
@@ -515,24 +540,24 @@ namespace MPF.ExecutionContexts
/// <summary>
/// Process a sbyte parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>SByte value if success, SByte.MinValue if skipped, null on error/returns>
protected sbyte? ProcessInt8Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
protected sbyte? ProcessInt8Parameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt8Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an sbyte parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>SByte value if success, SByte.MinValue if skipped, null on error/returns>
protected sbyte? ProcessInt8Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
protected sbyte? ProcessInt8Parameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
@@ -603,24 +628,24 @@ namespace MPF.ExecutionContexts
/// <summary>
/// Process an Int16 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int16 value if success, Int16.MinValue if skipped, null on error/returns>
protected short? ProcessInt16Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
protected short? ProcessInt16Parameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt16Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an Int16 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int16 value if success, Int16.MinValue if skipped, null on error/returns>
protected short? ProcessInt16Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
protected short? ProcessInt16Parameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
@@ -690,24 +715,24 @@ namespace MPF.ExecutionContexts
/// <summary>
/// Process an Int32 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int32 value if success, Int32.MinValue if skipped, null on error/returns>
protected int? ProcessInt32Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
protected int? ProcessInt32Parameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt32Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an Int32 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int32 value if success, Int32.MinValue if skipped, null on error/returns>
protected int? ProcessInt32Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
protected int? ProcessInt32Parameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
@@ -771,30 +796,30 @@ namespace MPF.ExecutionContexts
return null;
}
return Int32.MinValue;
return int.MinValue;
}
/// <summary>
/// Process an Int64 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int64 value if success, Int64.MinValue if skipped, null on error/returns>
protected long? ProcessInt64Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
protected long? ProcessInt64Parameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessInt64Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process an Int64 parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Int64 value if success, Int64.MinValue if skipped, null on error/returns>
protected long? ProcessInt64Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
protected long? ProcessInt64Parameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
@@ -858,30 +883,30 @@ namespace MPF.ExecutionContexts
return null;
}
return Int64.MinValue;
return long.MinValue;
}
/// <summary>
/// Process an string parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
protected string? ProcessStringParameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
protected string? ProcessStringParameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessStringParameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process a string parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
protected string? ProcessStringParameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
protected string? ProcessStringParameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
@@ -937,24 +962,24 @@ namespace MPF.ExecutionContexts
/// <summary>
/// Process a byte parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="flagString">Flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Byte value if success, Byte.MinValue if skipped, null on error/returns>
protected byte? ProcessUInt8Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
protected byte? ProcessUInt8Parameter(string[] parts, string flagString, ref int i, bool missingAllowed = false)
=> ProcessUInt8Parameter(parts, null, flagString, ref i, missingAllowed);
/// <summary>
/// Process a byte parameter
/// </summary>
/// <param name="parts">List of parts to be referenced</param>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="shortFlagString">Short flag string, if available</param>
/// <param name="longFlagString">Long flag string, if available</param>
/// <param name="i">Reference to the position in the parts</param>
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
/// <returns>Byte value if success, Byte.MinValue if skipped, null on error/returns>
protected byte? ProcessUInt8Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
protected byte? ProcessUInt8Parameter(string[] parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
{
if (parts == null)
return null;
@@ -1027,7 +1052,7 @@ 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 ExtractFactorFromValue(string value, out long factor)
internal static string ExtractFactorFromValue(string value, out long factor)
{
value = value.Trim('"');
factor = 1;
@@ -1089,7 +1114,7 @@ namespace MPF.ExecutionContexts
/// </summary>
/// <param name="value">String with removed leading 0x</param>
/// <returns></returns>
private static string RemoveHexIdentifier(string value)
internal static string RemoveHexIdentifier(string value)
{
if (value.Length <= 2)
return value;

View File

@@ -0,0 +1,123 @@
using System;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents a boolean flag with an optional trailing value
/// </summary>
public class BooleanInput : Input<bool?>
{
#region Constructors
/// <inheritdoc/>
public BooleanInput(string name)
: base(name) { }
/// <inheritdoc/>
public BooleanInput(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public BooleanInput(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public BooleanInput(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public BooleanInput(string[] names)
: base(names) { }
/// <inheritdoc/>
public BooleanInput(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
builder.Append(Value.ToString());
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
// Ensure the value exists
if (index + 1 >= parts.Length)
{
Value = _required ? null : true;
return !_required;
}
// If the next value is valid
if (!bool.TryParse(parts[index + 1], out bool value))
{
Value = _required ? null : true;
return !_required;
}
index++;
Value = value;
return true;
}
// Check for equal separated
if (part.StartsWith($"{Name}=") || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => part.StartsWith($"{n}=")) > -1))
{
// Split the string, using the first equal sign as the separator
string[] tempSplit = part.Split('=');
string key = tempSplit[0];
string val = string.Join("=", tempSplit, 1, tempSplit.Length - 1);
// Ensure the value exists
if (string.IsNullOrEmpty(val))
{
Value = _required ? null : true;
return !_required;
}
// If the next value is valid
if (!bool.TryParse(val, out bool value))
{
Value = _required ? null : true;
return !_required;
}
Value = value;
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,73 @@
using System;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents a boolean flag without a trailing value
/// </summary>
public class FlagInput : Input<bool>
{
#region Constructors
/// <inheritdoc/>
public FlagInput(string name)
: base(name) { }
/// <inheritdoc/>
public FlagInput(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public FlagInput(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public FlagInput(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public FlagInput(string[] names)
: base(names) { }
/// <inheritdoc/>
public FlagInput(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == false)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check the name
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
Value = true;
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,261 @@
using System;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents a single input for an execution context
/// </summary>
public abstract class Input
{
#region Properties
/// <summary>
/// Primary name for the input
/// </summary>
public readonly string Name;
/// <summary>
/// Alternative name for the input
/// </summary>
protected readonly string[] _altNames;
/// <summary>
/// Indicates if the value following is required or not
/// </summary>
protected readonly bool _required;
/// <summary>
/// Indicates if a value has been set
/// </summary>
public abstract bool ValueSet { get; }
#endregion
#region Constructors
/// <param name="name">Flag name / value</param>
public Input(string name)
{
Name = name;
_altNames = [];
_required = true;
}
/// <param name="name">Flag name / value</param>
/// <param name="required">Indicates if a following value is required</param>
public Input(string name, bool required)
{
Name = name;
_altNames = [];
_required = required;
}
/// <param name="shortName">Flag name / value</param>
/// <param name="longName">Verbose flag name / value</param>
public Input(string shortName, string longName)
{
Name = longName;
_altNames = [shortName];
_required = true;
}
/// <param name="shortName">Flag name / value</param>
/// <param name="longName">Verbose flag name / value</param>
/// <param name="required">Indicates if a following value is required</param>
public Input(string shortName, string longName, bool required)
{
Name = longName;
_altNames = [shortName];
_required = required;
}
/// <param name="names">Set of names to use</param>
public Input(string[] names)
{
Name = names.Length > 0 ? names[0] : string.Empty;
_altNames = names;
_required = true;
}
/// <param name="names">Set of names to use</param>
/// <param name="required">Indicates if a following value is required</param>
public Input(string[] names, bool required)
{
Name = names.Length > 0 ? names[0] : string.Empty;
_altNames = names;
_required = required;
}
#endregion
#region Functionality
/// <summary>
/// Clear any accumulated value
/// </summary>
public abstract void ClearValue();
/// <summary>
/// Create a formatted representation of the input and possible value
/// </summary>
/// <param name="useEquals">Use an equal sign as a separator on output</param>
public abstract string Format(bool useEquals);
/// <summary>
/// Process the current index, if possible
/// </summary>
/// <param name="parts">Parts array to be referenced</param>
/// <param name="index">Reference to the position in the parts</param>
/// <returns>True if a value could be determined, false otherwise</returns>
public abstract bool Process(string[] parts, ref int index);
#endregion
#region Helpers
/// <summary>
/// Get the trimmed value and multiplication factor from a value
/// </summary>
/// <param name="value">String value to treat as suffixed number</param>
/// <returns>Trimmed value and multiplication factor</returns>
internal static string ExtractFactorFromValue(string value, out long factor)
{
value = value.Trim('"');
factor = 1;
// Characters
if (value.EndsWith("c", StringComparison.Ordinal))
{
factor = 1;
value = value.TrimEnd('c');
}
// Words
else if (value.EndsWith("w", StringComparison.Ordinal))
{
factor = 2;
value = value.TrimEnd('w');
}
// Double Words
else if (value.EndsWith("d", StringComparison.Ordinal))
{
factor = 4;
value = value.TrimEnd('d');
}
// Quad Words
else if (value.EndsWith("q", StringComparison.Ordinal))
{
factor = 8;
value = value.TrimEnd('q');
}
// Kilobytes
else if (value.EndsWith("k", StringComparison.Ordinal))
{
factor = 1024;
value = value.TrimEnd('k');
}
// Megabytes
else if (value.EndsWith("M", StringComparison.Ordinal))
{
factor = 1024 * 1024;
value = value.TrimEnd('M');
}
// Gigabytes
else if (value.EndsWith("G", StringComparison.Ordinal))
{
factor = 1024 * 1024 * 1024;
value = value.TrimEnd('G');
}
return value;
}
/// <summary>
/// Removes a leading 0x if it exists, case insensitive
/// </summary>
/// <param name="value">String with removed leading 0x</param>
/// <returns></returns>
internal static string RemoveHexIdentifier(string value)
{
if (value.Length <= 2)
return value;
if (value[0] != '0')
return value;
if (value[1] != 'x' && value[1] != 'X')
return value;
return value.Substring(2);
}
#endregion
}
/// <summary>
/// Represents a single input for an execution context
/// </summary>
public abstract class Input<T> : Input
{
#region Properties
/// <summary>
/// Represents the last value stored
/// </summary>
public T? Value { get; protected set; }
/// <inheritdoc/>
public override bool ValueSet => Value != null;
#endregion
#region Constructors
/// <inheritdoc/>
public Input(string name)
: base(name) { }
/// <inheritdoc/>
public Input(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public Input(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public Input(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public Input(string[] names)
: base(names) { }
/// <inheritdoc/>
public Input(string[] names, bool required)
: base(names, required) { }
#endregion
#region Functionality
/// <inheritdoc/>
public override void ClearValue()
{
Value = default;
}
/// <summary>
/// Set a new value
/// </summary>
public void SetValue(T value)
{
Value = value;
}
#endregion
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Globalization;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents an Int16 flag with an optional trailing value
/// </summary>
public class Int16Input : Input<short?>
{
#region Properties
/// <summary>
/// Indicates a minimum value (inclusive) for the flag
/// </summary>
public short? MinValue { get; set; } = null;
/// <summary>
/// Indicates a maximum value (inclusive) for the flag
/// </summary>
public short? MaxValue { get; set; } = null;
#endregion
#region Constructors
/// <inheritdoc/>
public Int16Input(string name)
: base(name) { }
/// <inheritdoc/>
public Int16Input(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public Int16Input(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public Int16Input(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public Int16Input(string[] names)
: base(names) { }
/// <inheritdoc/>
public Int16Input(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Only output separator and value if needed
if (_required || (!_required && Value != short.MinValue))
{
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
builder.Append(Value.ToString());
}
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
// Ensure the value exists
if (index + 1 >= parts.Length)
{
Value = _required ? null : short.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out short? value) && value != null)
{
index++;
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : short.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// Check for equal separated
if (part.StartsWith($"{Name}=") || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => part.StartsWith($"{n}=")) > -1))
{
// Split the string, using the first equal sign as the separator
string[] tempSplit = part.Split('=');
string key = tempSplit[0];
string val = string.Join("=", tempSplit, 1, tempSplit.Length - 1);
// Ensure the value exists
if (string.IsNullOrEmpty(val))
{
Value = _required ? null : short.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out short? value) && value != null)
{
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : short.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
return false;
}
/// <summary>
/// Parse a value from a string
/// </summary>
private static bool ParseValue(string str, out short? output)
{
// If the next value is valid
if (short.TryParse(str, out short value))
{
output = value;
return true;
}
// Try to process as a formatted string
string baseVal = ExtractFactorFromValue(str, out long factor);
if (short.TryParse(baseVal, out value))
{
output = (short)(value * factor);
return true;
}
// Try to process as a hex string
string hexValue = RemoveHexIdentifier(baseVal);
if (short.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value))
{
output = (short)(value * factor);
return true;
}
// The value could not be parsed
output = null;
return false;
}
}
}

View File

@@ -0,0 +1,161 @@
using System;
using System.Globalization;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents an Int32 flag with an optional trailing value
/// </summary>
public class Int32ArrInput : Input<int?[]>
{
#region Properties
/// <summary>
/// Internal array size
/// </summary>
public int Size { get; set; }
/// <summary>
/// Indicates a minimum value (inclusive) for the flag
/// </summary>
public int? MinValue { get; set; } = null;
/// <summary>
/// Indicates a maximum value (inclusive) for the flag
/// </summary>
public int? MaxValue { get; set; } = null;
#endregion
#region Constructors
/// <inheritdoc/>
public Int32ArrInput(string name)
: base(name) { }
/// <inheritdoc/>
public Int32ArrInput(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public Int32ArrInput(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public Int32ArrInput(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public Int32ArrInput(string[] names)
: base(names) { }
/// <inheritdoc/>
public Int32ArrInput(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Only output separator and value if needed
if (_required || (!_required && Value != null))
{
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
int?[] nonNull = Array.FindAll(Value, i => i != null);
string[] stringValues = Array.ConvertAll(nonNull, i => i.ToString() ?? string.Empty);
builder.Append(string.Join(" ", stringValues));
}
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
Value = new int?[Size];
for (int i = 0; i < Size; i++)
{
// Ensure the value exists
if (index + 1 >= parts.Length)
return !_required;
// If the next value is valid
if (ParseValue(parts[index + 1], out int? value) && value != null)
{
index++;
Value[i] = value;
Value[i] = (MinValue != null && Value[i] < MinValue) ? MinValue : Value[i];
Value[i] = (MaxValue != null && Value[i] > MaxValue) ? MaxValue : Value[i];
continue;
}
// Return value based on required flag
return !_required;
}
return true;
}
return false;
}
/// <summary>
/// Parse a value from a string
/// </summary>
private static bool ParseValue(string str, out int? output)
{
// If the next value is valid
if (int.TryParse(str, out int value))
{
output = value;
return true;
}
// Try to process as a formatted string
string baseVal = ExtractFactorFromValue(str, out long factor);
if (int.TryParse(baseVal, out value))
{
output = (int)(value * factor);
return true;
}
// Try to process as a hex string
string hexValue = RemoveHexIdentifier(baseVal);
if (int.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value))
{
output = (int)(value * factor);
return true;
}
// The value could not be parsed
output = null;
return false;
}
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Globalization;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents an Int32 flag with an optional trailing value
/// </summary>
public class Int32Input : Input<int?>
{
#region Properties
/// <summary>
/// Indicates a minimum value (inclusive) for the flag
/// </summary>
public int? MinValue { get; set; } = null;
/// <summary>
/// Indicates a maximum value (inclusive) for the flag
/// </summary>
public int? MaxValue { get; set; } = null;
#endregion
#region Constructors
/// <inheritdoc/>
public Int32Input(string name)
: base(name) { }
/// <inheritdoc/>
public Int32Input(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public Int32Input(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public Int32Input(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public Int32Input(string[] names)
: base(names) { }
/// <inheritdoc/>
public Int32Input(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Only output separator and value if needed
if (_required || (!_required && Value != int.MinValue))
{
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
builder.Append(Value.ToString());
}
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
// Ensure the value exists
if (index + 1 >= parts.Length)
{
Value = _required ? null : int.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out int? value) && value != null)
{
index++;
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : int.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// Check for equal separated
if (part.StartsWith($"{Name}=") || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => part.StartsWith($"{n}=")) > -1))
{
// Split the string, using the first equal sign as the separator
string[] tempSplit = part.Split('=');
string key = tempSplit[0];
string val = string.Join("=", tempSplit, 1, tempSplit.Length - 1);
// Ensure the value exists
if (string.IsNullOrEmpty(val))
{
Value = _required ? null : int.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out int? value) && value != null)
{
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : int.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
return false;
}
/// <summary>
/// Parse a value from a string
/// </summary>
private static bool ParseValue(string str, out int? output)
{
// If the next value is valid
if (int.TryParse(str, out int value))
{
output = value;
return true;
}
// Try to process as a formatted string
string baseVal = ExtractFactorFromValue(str, out long factor);
if (int.TryParse(baseVal, out value))
{
output = (int)(value * factor);
return true;
}
// Try to process as a hex string
string hexValue = RemoveHexIdentifier(baseVal);
if (int.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value))
{
output = (int)(value * factor);
return true;
}
// The value could not be parsed
output = null;
return false;
}
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Globalization;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents an Int64 flag with an optional trailing value
/// </summary>
public class Int64Input : Input<long?>
{
#region Properties
/// <summary>
/// Indicates a minimum value (inclusive) for the flag
/// </summary>
public long? MinValue { get; set; } = null;
/// <summary>
/// Indicates a maximum value (inclusive) for the flag
/// </summary>
public long? MaxValue { get; set; } = null;
#endregion
#region Constructors
/// <inheritdoc/>
public Int64Input(string name)
: base(name) { }
/// <inheritdoc/>
public Int64Input(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public Int64Input(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public Int64Input(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public Int64Input(string[] names)
: base(names) { }
/// <inheritdoc/>
public Int64Input(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Only output separator and value if needed
if (_required || (!_required && Value != long.MinValue))
{
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
builder.Append(Value.ToString());
}
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
// Ensure the value exists
if (index + 1 >= parts.Length)
{
Value = _required ? null : long.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out long? value) && value != null)
{
index++;
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : long.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// Check for equal separated
if (part.StartsWith($"{Name}=") || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => part.StartsWith($"{n}=")) > -1))
{
// Split the string, using the first equal sign as the separator
string[] tempSplit = part.Split('=');
string key = tempSplit[0];
string val = string.Join("=", tempSplit, 1, tempSplit.Length - 1);
// Ensure the value exists
if (string.IsNullOrEmpty(val))
{
Value = _required ? null : long.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out long? value) && value != null)
{
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : long.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
return false;
}
/// <summary>
/// Parse a value from a string
/// </summary>
private static bool ParseValue(string str, out long? output)
{
// If the next value is valid
if (long.TryParse(str, out long value))
{
output = value;
return true;
}
// Try to process as a formatted string
string baseVal = ExtractFactorFromValue(str, out long factor);
if (long.TryParse(baseVal, out value))
{
output = (long)(value * factor);
return true;
}
// Try to process as a hex string
string hexValue = RemoveHexIdentifier(baseVal);
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value))
{
output = (long)(value * factor);
return true;
}
// The value could not be parsed
output = null;
return false;
}
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Globalization;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents an Int8 flag with an optional trailing value
/// </summary>
public class Int8Input : Input<sbyte?>
{
#region Properties
/// <summary>
/// Indicates a minimum value (inclusive) for the flag
/// </summary>
public sbyte? MinValue { get; set; } = null;
/// <summary>
/// Indicates a maximum value (inclusive) for the flag
/// </summary>
public sbyte? MaxValue { get; set; } = null;
#endregion
#region Constructors
/// <inheritdoc/>
public Int8Input(string name)
: base(name) { }
/// <inheritdoc/>
public Int8Input(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public Int8Input(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public Int8Input(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public Int8Input(string[] names)
: base(names) { }
/// <inheritdoc/>
public Int8Input(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Only output separator and value if needed
if (_required || (!_required && Value != sbyte.MinValue))
{
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
builder.Append(Value.ToString());
}
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
// Ensure the value exists
if (index + 1 >= parts.Length)
{
Value = _required ? null : sbyte.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out sbyte? value) && value != null)
{
index++;
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : sbyte.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// Check for equal separated
if (part.StartsWith($"{Name}=") || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => part.StartsWith($"{n}=")) > -1))
{
// Split the string, using the first equal sign as the separator
string[] tempSplit = part.Split('=');
string key = tempSplit[0];
string val = string.Join("=", tempSplit, 1, tempSplit.Length - 1);
// Ensure the value exists
if (string.IsNullOrEmpty(val))
{
Value = _required ? null : sbyte.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out sbyte? value) && value != null)
{
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : sbyte.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
return false;
}
/// <summary>
/// Parse a value from a string
/// </summary>
private static bool ParseValue(string str, out sbyte? output)
{
// If the next value is valid
if (sbyte.TryParse(str, out sbyte value))
{
output = value;
return true;
}
// Try to process as a formatted string
string baseVal = ExtractFactorFromValue(str, out long factor);
if (sbyte.TryParse(baseVal, out value))
{
output = (sbyte)(value * factor);
return true;
}
// Try to process as a hex string
string hexValue = RemoveHexIdentifier(baseVal);
if (sbyte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value))
{
output = (sbyte)(value * factor);
return true;
}
// The value could not be parsed
output = null;
return false;
}
}
}

View File

@@ -0,0 +1,126 @@
using System;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents a string flag with an optional trailing value
/// </summary>
public class StringInput : Input<string>
{
#region Properties
/// <summary>
/// Indicates whether quotes are used in output or not
/// </summary>
public bool Quotes { get; set; } = false;
#endregion
#region Constructors
/// <inheritdoc/>
public StringInput(string name)
: base(name) { }
/// <inheritdoc/>
public StringInput(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public StringInput(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public StringInput(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public StringInput(string[] names)
: base(names) { }
/// <inheritdoc/>
public StringInput(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Only output separator and value if needed
if (_required || (!_required && Value != string.Empty))
{
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
if (Quotes)
builder.Append($"\"{Value}\"");
else
builder.Append(Value);
}
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
// Ensure the value exists
if (index + 1 >= parts.Length)
{
Value = _required ? null : string.Empty;
return !_required;
}
index++;
Value = parts[index].Trim('"');
return true;
}
// Check for equal separated
if (part.StartsWith($"{Name}=") || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => part.StartsWith($"{n}=")) > -1))
{
// Split the string, using the first equal sign as the separator
string[] tempSplit = part.Split('=');
string key = tempSplit[0];
string val = string.Join("=", tempSplit, 1, tempSplit.Length - 1);
// Ensure the value exists
if (string.IsNullOrEmpty(val))
{
Value = _required ? null : string.Empty;
return !_required;
}
Value = val.Trim('"');
return true;
}
return false;
}
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Globalization;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents an UInt16 flag with an optional trailing value
/// </summary>
public class UInt16Input : Input<ushort?>
{
#region Properties
/// <summary>
/// Indicates a minimum value (inclusive) for the flag
/// </summary>
public ushort? MinValue { get; set; } = null;
/// <summary>
/// Indicates a maximum value (inclusive) for the flag
/// </summary>
public ushort? MaxValue { get; set; } = null;
#endregion
#region Constructors
/// <inheritdoc/>
public UInt16Input(string name)
: base(name) { }
/// <inheritdoc/>
public UInt16Input(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public UInt16Input(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public UInt16Input(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public UInt16Input(string[] names)
: base(names) { }
/// <inheritdoc/>
public UInt16Input(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Only output separator and value if needed
if (_required || (!_required && Value != ushort.MinValue))
{
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
builder.Append(Value.ToString());
}
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
// Ensure the value exists
if (index + 1 >= parts.Length)
{
Value = _required ? null : ushort.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out ushort? value) && value != null)
{
index++;
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : ushort.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// Check for equal separated
if (part.StartsWith($"{Name}=") || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => part.StartsWith($"{n}=")) > -1))
{
// Split the string, using the first equal sign as the separator
string[] tempSplit = part.Split('=');
string key = tempSplit[0];
string val = string.Join("=", tempSplit, 1, tempSplit.Length - 1);
// Ensure the value exists
if (string.IsNullOrEmpty(val))
{
Value = _required ? null : ushort.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out ushort? value) && value != null)
{
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : ushort.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
return false;
}
/// <summary>
/// Parse a value from a string
/// </summary>
private static bool ParseValue(string str, out ushort? output)
{
// If the next value is valid
if (ushort.TryParse(str, out ushort value))
{
output = value;
return true;
}
// Try to process as a formatted string
string baseVal = ExtractFactorFromValue(str, out long factor);
if (ushort.TryParse(baseVal, out value))
{
output = (ushort)(value * factor);
return true;
}
// Try to process as a hex string
string hexValue = RemoveHexIdentifier(baseVal);
if (ushort.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value))
{
output = (ushort)(value * factor);
return true;
}
// The value could not be parsed
output = null;
return false;
}
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Globalization;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents an UInt32 flag with an optional trailing value
/// </summary>
public class UInt32Input : Input<uint?>
{
#region Properties
/// <summary>
/// Indicates a minimum value (inclusive) for the flag
/// </summary>
public uint? MinValue { get; set; } = null;
/// <summary>
/// Indicates a maximum value (inclusive) for the flag
/// </summary>
public uint? MaxValue { get; set; } = null;
#endregion
#region Constructors
/// <inheritdoc/>
public UInt32Input(string name)
: base(name) { }
/// <inheritdoc/>
public UInt32Input(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public UInt32Input(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public UInt32Input(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public UInt32Input(string[] names)
: base(names) { }
/// <inheritdoc/>
public UInt32Input(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Only output separator and value if needed
if (_required || (!_required && Value != uint.MinValue))
{
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
builder.Append(Value.ToString());
}
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
// Ensure the value exists
if (index + 1 >= parts.Length)
{
Value = _required ? null : uint.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out uint? value) && value != null)
{
index++;
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : uint.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// Check for equal separated
if (part.StartsWith($"{Name}=") || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => part.StartsWith($"{n}=")) > -1))
{
// Split the string, using the first equal sign as the separator
string[] tempSplit = part.Split('=');
string key = tempSplit[0];
string val = string.Join("=", tempSplit, 1, tempSplit.Length - 1);
// Ensure the value exists
if (string.IsNullOrEmpty(val))
{
Value = _required ? null : uint.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out uint? value) && value != null)
{
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : uint.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
return false;
}
/// <summary>
/// Parse a value from a string
/// </summary>
private static bool ParseValue(string str, out uint? output)
{
// If the next value is valid
if (uint.TryParse(str, out uint value))
{
output = value;
return true;
}
// Try to process as a formatted string
string baseVal = ExtractFactorFromValue(str, out long factor);
if (uint.TryParse(baseVal, out value))
{
output = (uint)(value * factor);
return true;
}
// Try to process as a hex string
string hexValue = RemoveHexIdentifier(baseVal);
if (uint.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value))
{
output = (uint)(value * factor);
return true;
}
// The value could not be parsed
output = null;
return false;
}
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Globalization;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents an UInt64 flag with an optional trailing value
/// </summary>
public class UInt64Input : Input<ulong?>
{
#region Properties
/// <summary>
/// Indicates a minimum value (inclusive) for the flag
/// </summary>
public ulong? MinValue { get; set; } = null;
/// <summary>
/// Indicates a maximum value (inclusive) for the flag
/// </summary>
public ulong? MaxValue { get; set; } = null;
#endregion
#region Constructors
/// <inheritdoc/>
public UInt64Input(string name)
: base(name) { }
/// <inheritdoc/>
public UInt64Input(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public UInt64Input(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public UInt64Input(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public UInt64Input(string[] names)
: base(names) { }
/// <inheritdoc/>
public UInt64Input(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Only output separator and value if needed
if (_required || (!_required && Value != ulong.MinValue))
{
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
builder.Append(Value.ToString());
}
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
// Ensure the value exists
if (index + 1 >= parts.Length)
{
Value = _required ? null : ulong.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out ulong? value) && value != null)
{
index++;
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : ulong.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// Check for equal separated
if (part.StartsWith($"{Name}=") || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => part.StartsWith($"{n}=")) > -1))
{
// Split the string, using the first equal sign as the separator
string[] tempSplit = part.Split('=');
string key = tempSplit[0];
string val = string.Join("=", tempSplit, 1, tempSplit.Length - 1);
// Ensure the value exists
if (string.IsNullOrEmpty(val))
{
Value = _required ? null : ulong.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out ulong? value) && value != null)
{
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : ulong.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
return false;
}
/// <summary>
/// Parse a value from a string
/// </summary>
private static bool ParseValue(string str, out ulong? output)
{
// If the next value is valid
if (ulong.TryParse(str, out ulong value))
{
output = value;
return true;
}
// Try to process as a formatted string
string baseVal = ExtractFactorFromValue(str, out long factor);
if (ulong.TryParse(baseVal, out value))
{
output = (ulong)(value * (ulong)factor);
return true;
}
// Try to process as a hex string
string hexValue = RemoveHexIdentifier(baseVal);
if (ulong.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value))
{
output = (ulong)(value * (ulong)factor);
return true;
}
// The value could not be parsed
output = null;
return false;
}
}
}

View File

@@ -0,0 +1,189 @@
using System;
using System.Globalization;
using System.Text;
namespace MPF.ExecutionContexts.Data
{
/// <summary>
/// Represents an UInt8 flag with an optional trailing value
/// </summary>
public class UInt8Input : Input<byte?>
{
#region Properties
/// <summary>
/// Indicates a minimum value (inclusive) for the flag
/// </summary>
public byte? MinValue { get; set; } = null;
/// <summary>
/// Indicates a maximum value (inclusive) for the flag
/// </summary>
public byte? MaxValue { get; set; } = null;
#endregion
#region Constructors
/// <inheritdoc/>
public UInt8Input(string name)
: base(name) { }
/// <inheritdoc/>
public UInt8Input(string name, bool required)
: base(name, required) { }
/// <inheritdoc/>
public UInt8Input(string shortName, string longName)
: base(shortName, longName) { }
/// <inheritdoc/>
public UInt8Input(string shortName, string longName, bool required)
: base(shortName, longName, required) { }
/// <inheritdoc/>
public UInt8Input(string[] names)
: base(names) { }
/// <inheritdoc/>
public UInt8Input(string[] names, bool required)
: base(names, required) { }
#endregion
/// <inheritdoc/>
public override string Format(bool useEquals)
{
// Do not output if there is no value
if (Value == null)
return string.Empty;
// Build the output format
var builder = new StringBuilder();
// Flag name
builder.Append(Name);
// Only output separator and value if needed
if (_required || (!_required && Value != byte.MinValue))
{
// Separator
if (useEquals)
builder.Append("=");
else
builder.Append(" ");
// Value
builder.Append(Value.ToString());
}
return builder.ToString();
}
/// <inheritdoc/>
public override bool Process(string[] parts, ref int index)
{
// Check the parts array
if (index < 0 || index >= parts.Length)
return false;
// Check for space-separated
string part = parts[index];
if (part == Name || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => n == part) > -1))
{
// Ensure the value exists
if (index + 1 >= parts.Length)
{
Value = _required ? null : byte.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out byte? value) && value != null)
{
index++;
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : byte.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// Check for equal separated
if (part.StartsWith($"{Name}=") || (_altNames.Length > 0 && Array.FindIndex(_altNames, n => part.StartsWith($"{n}=")) > -1))
{
// Split the string, using the first equal sign as the separator
string[] tempSplit = part.Split('=');
string key = tempSplit[0];
string val = string.Join("=", tempSplit, 1, tempSplit.Length - 1);
// Ensure the value exists
if (string.IsNullOrEmpty(val))
{
Value = _required ? null : byte.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out byte? value) && value != null)
{
Value = value;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : byte.MinValue;
Value = (MinValue != null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue != null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
return false;
}
/// <summary>
/// Parse a value from a string
/// </summary>
private static bool ParseValue(string str, out byte? output)
{
// If the next value is valid
if (byte.TryParse(str, out byte value))
{
output = value;
return true;
}
// Try to process as a formatted string
string baseVal = ExtractFactorFromValue(str, out long factor);
if (byte.TryParse(baseVal, out value))
{
output = (byte)(value * factor);
return true;
}
// Try to process as a hex string
string hexValue = RemoveHexIdentifier(baseVal);
if (byte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out value))
{
output = (byte)(value * factor);
return true;
}
// The value could not be parsed
output = null;
return false;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@ namespace MPF.ExecutionContexts.DiscImageCreator
public const string AtariJaguar = "/aj";
public const string BEOpcode = "/be";
public const string C2Opcode = "/c2";
public const string C2OpcodeNew = "/c2new";
public const string CopyrightManagementInformation = "/c";
public const string D8Opcode = "/d8";
public const string DatExpand = "/d";
@@ -17,7 +18,9 @@ namespace MPF.ExecutionContexts.DiscImageCreator
public const string DVDReread = "/rr";
public const string ExtractMicroSoftCabFile = "/mscf";
public const string Fix = "/fix";
public const string ForceDescrambleSector = "/fdesc";
public const string ForceUnitAccess = "/f";
public const string FullToc = "/fulltoc";
public const string MultiSectorRead = "/mr";
public const string NoFixSubP = "/np";
public const string NoFixSubQ = "/nq";
@@ -37,6 +40,7 @@ namespace MPF.ExecutionContexts.DiscImageCreator
public const string SkipSector = "/sk";
public const string SubchannelReadLevel = "/s";
public const string Tages = "/t";
public const string Toc = "/toc";
public const string TryReadingPregap = "/trp";
public const string UseAnchorVolumeDescriptorPointer = "/avdp";
public const string VideoNow = "/vn";

View File

@@ -1,53 +1,38 @@
<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>
<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>
<IncludeSymbols>true</IncludeSymbols>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.2.3</VersionPrefix>
<VersionPrefix>3.4.0</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>
<Copyright>Copyright (c) Matt Nadareski 2019-2025</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="MPF.Test" />
</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`))">
<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</TargetFrameworks>
</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" />
<None Include="README.md" Pack="true" PackagePath="" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.4" />
<InternalsVisibleTo Include="MPF.ExecutionContexts.Test" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,13 @@
# MPF.ExecutionContexts
This library represents the logic needed to invoke 3 different dumping programs:
- [Aaru](github.com/aaru-dps/Aaru)
- [DiscImageCreator](github.com/saramibreak/DiscImageCreator)
- [Redumper](https://github.com/superg/redumper)
These execution wrappers allow for generating valid parameters for each of the programs as well as provide some helpers that make it easier to determine what those parameters are.
External options are defined in order to help create reasonable default parameter sets for different combinations of media type and system.
Paths to the programs need to be provided if they are expected to be invoked as a part of another program. The expected versions of these programs can be found in either the changelog or the publish script in the parent project.

View File

@@ -6,23 +6,27 @@ namespace MPF.ExecutionContexts.Redumper
public static class CommandStrings
{
public const string NONE = "";
public const string CD = "cd";
public const string DVD = "dvd"; // Synonym for CD
public const string BluRay = "bd"; // Synonym for CD
public const string SACD = "sacd"; // Synonym for CD
public const string New = "new"; // Synonym for CD; Temporary command, to be removed later
public const string Disc = "disc";
public const string Rings = "rings";
public const string Dump = "dump";
public const string DumpNew = "dumpnew"; // Temporary command, to be removed later
public const string DumpExtra = "dump::extra";
public const string Refine = "refine";
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 FlashMT1339 = "flash::mt1339";
public const string FlashSD616 = "flash::sd616";
public const string FlashPlextor = "flash::plextor";
public const string Subchannel = "subchannel";
public const string Debug = "debug";
public const string FixMSF = "fixmsf";
public const string DebugFlip = "debug::flip";
public const string DriveTest = "drive::test";
}
}

View File

@@ -15,10 +15,14 @@ namespace MPF.ExecutionContexts.Redumper
{
return type switch
{
MediaType.CDROM => ".bin",
MediaType.CDROM
or MediaType.GDROM => ".bin",
MediaType.DVD
or MediaType.HDDVD
or MediaType.BluRay => ".iso",
or MediaType.BluRay
or MediaType.NintendoWiiOpticalDisc => ".iso",
MediaType.NintendoGameCubeGameDisc => ".raw",
MediaType.NintendoWiiUOpticalDisc => ".wud",
_ => null,
};
}

View File

@@ -1,5 +1,21 @@
namespace MPF.ExecutionContexts.Redumper
{
/// <summary>
/// Drive type option
/// </summary>
public enum DriveType
{
NONE = 0,
GENERIC,
PLEXTOR,
LG_ASU8A,
LG_ASU8B,
LG_ASU8C,
LG_ASU3,
LG_ASU2,
}
/// <summary>
/// Drive read method option
/// </summary>
@@ -9,7 +25,6 @@ namespace MPF.ExecutionContexts.Redumper
BE,
D8,
BE_CDDA,
}
/// <summary>
@@ -24,4 +39,4 @@ namespace MPF.ExecutionContexts.Redumper
DATA_SUB,
DATA_C2,
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -5,20 +5,28 @@ namespace MPF.ExecutionContexts.Redumper
/// </summary>
public static class FlagStrings
{
// General
#region General
public const string HelpLong = "--help";
public const string HelpShort = "-h";
public const string Version = "--version";
public const string Verbose = "--verbose";
public const string Debug = "--debug";
public const string ListRecommendedDrives = "--list-recommended-drives";
public const string ListAllDrives = "--list-all-drives";
public const string AutoEject = "--auto-eject";
public const string Skeleton = "--skeleton";
public const string Drive = "--drive";
public const string Speed = "--speed";
public const string Retries = "--retries";
public const string ImagePath = "--image-path";
public const string ImageName = "--image-name";
public const string Overwrite = "--overwrite";
public const string DiscType = "--disc-type";
#endregion
#region Drive Configuration
// Drive Configuration
public const string DriveType = "--drive-type";
public const string DriveReadOffset = "--drive-read-offset";
public const string DriveC2Shift = "--drive-c2-shift";
@@ -26,34 +34,70 @@ namespace MPF.ExecutionContexts.Redumper
public const string DriveReadMethod = "--drive-read-method";
public const string DriveSectorOrder = "--drive-sector-order";
// Drive Specific
#endregion
#region Drive Specific
public const string PlextorSkipLeadin = "--plextor-skip-leadin";
public const string PlextorLeadinRetries = "--plextor-leadin-retries";
public const string PlextorLeadinForceStore = "--plextor-leadin-force-store";
public const string KreonPartialSS = "--kreon-partial-ss";
public const string AsusSkipLeadout = "--asus-skip-leadout";
public const string AsusLeadoutRetries = "--asus-leadout-retries";
public const string DisableCDText = "--disable-cdtext";
#endregion
#region Offset
// Offset
public const string ForceOffset = "--force-offset";
public const string AudioSilenceThreshold = "--audio-silence-threshold";
public const string CorrectOffsetShift = "--correct-offset-shift";
public const string OffsetShiftRelocate = "--offset-shift-relocate";
// Split
#endregion
#region Split
public const string ForceSplit = "--force-split";
public const string LeaveUnchanged = "--leave-unchanged";
public const string ForceQTOC = "--force-qtoc";
public const string SkipFill = "--skip-fill";
public const string ISO9660Trim = "--iso9660-trim";
// Miscellaneous
#endregion
#region Drive Test
public const string DriveTestSkipPlextorLeadin = "--drive-test-skip-plextor-leadin";
public const string DriveTestSkipCacheRead = "--drive-test-skip-cache-read";
#endregion
#region Miscellaneous
public const string Continue = "--continue";
public const string LBAStart = "--lba-start";
public const string LBAEnd = "--lba-end";
public const string RefineSubchannel = "--refine-subchannel";
public const string RefineSectorMode = "--refine-sector-mode";
public const string Skip = "--skip";
public const string DumpWriteOffset = "--dump-write-offset";
public const string DumpReadSize = "--dump-read-size";
public const string OverreadLeadout = "--overread-leadout";
public const string ForceUnscrambled = "--force-unscrambled";
public const string ForceRefine = "--force-refine";
public const string Firmware = "--firmware";
public const string SkipSubcodeDesync = "--skip-subcode-desync";
public const string Rings = "--rings";
#endregion
#region Undocumented
public const string Debug = "--debug";
public const string LegacySubs = "--legacy-subs";
public const string DisableCDText = "--disable-cdtext";
#endregion
}
}
}

View File

@@ -2,14 +2,14 @@ namespace MPF.ExecutionContexts.Redumper
{
public static class SettingConstants
{
public const string EnableDebug = "RedumperEnableDebug";
public const bool EnableDebugDefault = false;
public const string DriveType = "RedumperDriveType";
public static readonly string DriveTypeDefault = Redumper.DriveType.NONE.ToString();
public const string EnableLeadinRetry = "RedumperEnableLeadinRetry";
public const bool EnableLeadinRetryDefault = false;
public const string EnableSkeleton = "RedumperEnableSkeleton";
public const bool EnableSkeletonDefault = true;
public const string EnableVerbose = "RedumperEnableVerbose";
public const bool EnableVerboseDefault = true;
public const bool EnableVerboseDefault = false;
public const string LeadinRetryCount = "RedumperLeadinRetryCount";
public const int LeadinRetryCountDefault = 4;
@@ -17,13 +17,13 @@ namespace MPF.ExecutionContexts.Redumper
public const string ReadMethod = "RedumperReadMethod";
public static readonly string ReadMethodDefault = Redumper.ReadMethod.NONE.ToString();
public const string RefineSectorMode = "RedumperRefineSectorMode";
public const bool RefineSectorModeDefault = false;
public const string RereadCount = "RedumperRereadCount";
public const int RereadCountDefault = 20;
public const string SectorOrder = "RedumperSectorOrder";
public static readonly string SectorOrderDefault = Redumper.SectorOrder.NONE.ToString();
public const string UseGenericDriveType = "RedumperUseGenericDriveType";
public const bool UseGenericDriveTypeDefault = false;
}
}
}

View File

@@ -1,15 +1,13 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Frontend;
using Xunit;
namespace MPF.Test.Frontend
namespace MPF.Frontend.Test
{
public class EnumConverterTests
public class DriveTests
{
#region Cross-enumeration conversions
#region ToInternalDriveType
/// <summary>
/// DiscType values that map to InternalDriveType
@@ -47,7 +45,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]);
@@ -57,40 +55,5 @@ namespace MPF.Test.Frontend
}
#endregion
#region Convert to Long Name
// TODO: Maybe add a test for the generic "GetLongName" method
/// <summary>
/// Check that every InternalProgram has a long name provided
/// </summary>
/// <param name="internalProgram">InternalProgram value to check</param>
[Theory]
[MemberData(nameof(GenerateInternalProgramTestData))]
public void InternalProgramLongNameTest(InternalProgram? internalProgram)
{
string actual = internalProgram.LongName();
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of InternalProgram values
/// </summary>
/// <returns>MemberData-compatible list of InternalProgram values</returns>
public static List<object?[]> GenerateInternalProgramTestData()
{
var testData = new List<object?[]>() { new object?[] { null } };
foreach (InternalProgram? internalProgram in Enum.GetValues(typeof(InternalProgram)))
{
testData.Add([internalProgram]);
}
return testData;
}
#endregion
// TODO: Add from-string tests
}
}

View File

@@ -1,8 +1,7 @@
using MPF.Frontend;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Frontend
namespace MPF.Frontend.Test
{
public class DumpEnvironmentTests
{
@@ -10,6 +9,7 @@ namespace MPF.Test.Frontend
[InlineData(null, 'D', false, MediaType.NONE, false)]
[InlineData("", 'D', false, MediaType.NONE, false)]
[InlineData("cd F test.bin 8 /c2 20", 'F', false, MediaType.CDROM, true)]
[InlineData("cd F test.bin 8 /c2new 20", 'F', false, MediaType.CDROM, true)]
[InlineData("fd A test.img", 'A', true, MediaType.FloppyDisk, true)]
[InlineData("dvd X test.iso 8 /raw", 'X', false, MediaType.FloppyDisk, false)]
[InlineData("stop D", 'D', false, MediaType.DVD, true)]
@@ -22,9 +22,15 @@ namespace MPF.Test.Frontend
? Drive.Create(InternalDriveType.Floppy, letter.ToString())
: Drive.Create(InternalDriveType.Optical, letter.ToString());
var env = new DumpEnvironment(options, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, null, parameters);
var env = new DumpEnvironment(options,
string.Empty,
drive,
RedumpSystem.IBMPCcompatible,
null);
env.SetExecutionContext(mediaType, parameters);
env.SetProcessor();
bool actual = env.ParametersValid();
bool actual = env.ParametersValid(mediaType);
Assert.Equal(expected, actual);
}
}

View File

@@ -0,0 +1,180 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SabreTools.RedumpLib.Data;
using Xunit;
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
namespace MPF.Frontend.Test
{
public class EnumExtensionsTests
{
#region Long Name
[Theory]
[InlineData(null, "Unknown")]
[InlineData(InternalProgram.NONE, "Unknown")]
[InlineData(InternalProgram.Aaru, "Aaru")]
[InlineData(InternalProgram.DiscImageCreator, "DiscImageCreator")]
[InlineData(InternalProgram.Redumper, "Redumper")]
[InlineData(InternalProgram.CleanRip, "CleanRip")]
[InlineData(InternalProgram.PS3CFW, "PS3 CFW")]
[InlineData(InternalProgram.UmdImageCreator, "UmdImageCreator")]
[InlineData(InternalProgram.XboxBackupCreator, "XboxBackupCreator")]
public void LongName_InternalProgram(InternalProgram? prog, string? expected)
{
string? actual = prog.LongName();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(null, "Unknown")]
[InlineData(RedumperReadMethod.NONE, "Default")]
[InlineData(RedumperReadMethod.D8, "D8")]
[InlineData(RedumperReadMethod.BE, "BE")]
public void LongName_RedumperReadMethod(RedumperReadMethod? method, string? expected)
{
string? actual = method.LongName();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(null, "Unknown")]
[InlineData(RedumperSectorOrder.NONE, "Default")]
[InlineData(RedumperSectorOrder.DATA_C2_SUB, "DATA_C2_SUB")]
[InlineData(RedumperSectorOrder.DATA_SUB_C2, "DATA_SUB_C2")]
[InlineData(RedumperSectorOrder.DATA_SUB, "DATA_SUB")]
[InlineData(RedumperSectorOrder.DATA_C2, "DATA_C2")]
public void LongName_RedumperSectorOrder(RedumperSectorOrder? order, string? expected)
{
string? actual = order.LongName();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(null, "Unknown")]
[InlineData(RedumperDriveType.NONE, "Default")]
[InlineData(RedumperDriveType.GENERIC, "GENERIC")]
[InlineData(RedumperDriveType.PLEXTOR, "PLEXTOR")]
[InlineData(RedumperDriveType.LG_ASU8A, "LG_ASU8A")]
[InlineData(RedumperDriveType.LG_ASU8B, "LG_ASU8B")]
[InlineData(RedumperDriveType.LG_ASU8C, "LG_ASU8C")]
[InlineData(RedumperDriveType.LG_ASU3, "LG_ASU3")]
[InlineData(RedumperDriveType.LG_ASU2, "LG_ASU2")]
public void LongName_RedumperDriveType(RedumperDriveType? type, string? expected)
{
string? actual = type.LongName();
Assert.Equal(expected, actual);
}
#endregion
#region Short Name
[Theory]
[InlineData(null, "Unknown")]
[InlineData(InternalProgram.NONE, "Unknown")]
[InlineData(InternalProgram.Aaru, "aaru")]
[InlineData(InternalProgram.DiscImageCreator, "dic")]
[InlineData(InternalProgram.Redumper, "redumper")]
[InlineData(InternalProgram.CleanRip, "cleanrip")]
[InlineData(InternalProgram.PS3CFW, "ps3cfw")]
[InlineData(InternalProgram.UmdImageCreator, "uic")]
[InlineData(InternalProgram.XboxBackupCreator, "xbc")]
public void ShortName_InternalProgram(InternalProgram? prog, string? expected)
{
string? actual = prog.ShortName();
Assert.Equal(expected, actual);
}
#endregion
#region From String
[Theory]
[InlineData(null, InternalProgram.NONE)]
[InlineData("", InternalProgram.NONE)]
[InlineData("aaru", InternalProgram.Aaru)]
[InlineData("dic", InternalProgram.DiscImageCreator)]
[InlineData("redumper", InternalProgram.Redumper)]
[InlineData("cleanrip", InternalProgram.CleanRip)]
[InlineData("ps3cfw", InternalProgram.PS3CFW)]
[InlineData("uic", InternalProgram.UmdImageCreator)]
[InlineData("xbc", InternalProgram.XboxBackupCreator)]
public void ToInternalProgramTest(string? internalProgram, InternalProgram expected)
{
InternalProgram actual = internalProgram.ToInternalProgram();
Assert.Equal(expected, actual);
}
// TODO: Write remaining from-string tests
#endregion
#region Functionality Support
private static readonly RedumpSystem?[] _antiModchipSystems =
[
RedumpSystem.SonyPlayStation,
];
private static readonly RedumpSystem?[] _copyProtectionSystems =
[
RedumpSystem.AppleMacintosh,
RedumpSystem.EnhancedCD ,
RedumpSystem.IBMPCcompatible,
RedumpSystem.PalmOS,
RedumpSystem.PocketPC,
RedumpSystem.RainbowDisc,
RedumpSystem.SonyElectronicBook,
];
[Theory]
[MemberData(nameof(GenerateSupportsAntiModchipScansData))]
public void SupportsAntiModchipScansTest(RedumpSystem? redumpSystem, bool expected)
{
bool actual = redumpSystem.SupportsAntiModchipScans();
Assert.Equal(expected, actual);
}
[Theory]
[MemberData(nameof(GenerateSupportsCopyProtectionScansData))]
public void SupportsCopyProtectionScansTest(RedumpSystem? redumpSystem, bool expected)
{
bool actual = redumpSystem.SupportsCopyProtectionScans();
Assert.Equal(expected, actual);
}
public static List<object?[]> GenerateSupportsAntiModchipScansData()
{
var testData = new List<object?[]>() { new object?[] { null, false } };
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_antiModchipSystems.Contains(redumpSystem))
testData.Add([redumpSystem, true]);
else
testData.Add([redumpSystem, false]);
}
return testData;
}
public static List<object?[]> GenerateSupportsCopyProtectionScansData()
{
var testData = new List<object?[]>() { new object?[] { null, false } };
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
if (_copyProtectionSystems.Contains(redumpSystem))
testData.Add([redumpSystem, true]);
else
testData.Add([redumpSystem, false]);
}
return testData;
}
#endregion
}
}

View File

@@ -1,15 +1,18 @@
using MPF.Frontend;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Frontend
namespace MPF.Frontend.Test
{
public class UIElementsTest
public class InterfaceConstantsTests
{
[Theory]
[InlineData(MediaType.CDROM, 72)]
[InlineData(MediaType.DVD, 24)]
[InlineData(MediaType.NintendoGameCubeGameDisc, 24)]
[InlineData(MediaType.NintendoWiiOpticalDisc, 24)]
[InlineData(MediaType.HDDVD, 24)]
[InlineData(MediaType.BluRay, 16)]
[InlineData(MediaType.NintendoWiiUOpticalDisc, 16)]
[InlineData(MediaType.LaserDisc, 1)]
[InlineData(null, 1)]
public void GetAllowedDriveSpeedForMediaTypeTest(MediaType? mediaType, int maxExpected)

View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>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.Frontend\MPF.Frontend.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="17.14.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="1.24.0" />
<PackageReference Include="xunit.assert" Version="2.9.3" />
<PackageReference Include="xunit.core" Version="2.9.3" />
<PackageReference Include="xunit.extensibility.core" Version="2.9.3" />
<PackageReference Include="xunit.extensibility.execution" Version="2.9.3" />
<PackageReference Include="xunit.runner.console" Version="2.9.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,47 @@
using System.Collections.Generic;
using Xunit;
namespace MPF.Frontend.Test
{
public class OptionsTests
{
[Theory]
[InlineData("key2", null, "key", false, false)]
[InlineData("key", null, "key", false, false)]
[InlineData("key", "", "key", false, false)]
[InlineData("key", "INVALID", "key", false, false)]
[InlineData("key", "true", "key", false, true)]
public void GetBooleanSettingTest(string key, string? value, string expectedKey, bool defaultValue, bool expectedValue)
{
Dictionary<string, string?> settings = new() { [key] = value };
bool actual = Options.GetBooleanSetting(settings, expectedKey, defaultValue);
Assert.Equal(expectedValue, actual);
}
[Theory]
[InlineData("key2", null, "key", -1, -1)]
[InlineData("key", null, "key", -1, -1)]
[InlineData("key", "", "key", -1, -1)]
[InlineData("key", "INVALID", "key", -1, -1)]
[InlineData("key", "12345", "key", -1, 12345)]
public void GetInt32SettingTest(string key, string? value, string expectedKey, int defaultValue, int expectedValue)
{
Dictionary<string, string?> settings = new() { [key] = value };
int actual = Options.GetInt32Setting(settings, expectedKey, defaultValue);
Assert.Equal(expectedValue, actual);
}
[Theory]
[InlineData("key2", null, "key", null, null)]
[InlineData("key", null, "key", null, null)]
[InlineData("key", "", "key", null, "")]
[InlineData("key", "INVALID", "key", null, "INVALID")]
[InlineData("key", "String", "key", null, "String")]
public void GetStringSettingTest(string key, string? value, string expectedKey, string? defaultValue, string? expectedValue)
{
Dictionary<string, string?> settings = new() { [key] = value };
string? actual = Options.GetStringSetting(settings, expectedKey, defaultValue);
Assert.Equal(expectedValue, actual);
}
}
}

View File

@@ -1,7 +1,6 @@
using MPF.Frontend;
using Xunit;
using Xunit;
namespace MPF.Test.Frontend
namespace MPF.Frontend.Test
{
public class ResultEventArgsTests
{

View File

@@ -0,0 +1,100 @@
using System.IO;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Frontend.Test.Tools
{
public class FrontendToolTests
{
#region GetDefaultSpeedForMediaType
[Theory]
[InlineData(null, 72)]
[InlineData(MediaType.CDROM, 72)]
[InlineData(MediaType.GDROM, 72)]
[InlineData(MediaType.DVD, 24)]
[InlineData(MediaType.NintendoGameCubeGameDisc, 24)]
[InlineData(MediaType.NintendoWiiOpticalDisc, 24)]
[InlineData(MediaType.HDDVD, 24)]
[InlineData(MediaType.BluRay, 16)]
[InlineData(MediaType.NintendoWiiUOpticalDisc, 16)]
public void GetDefaultSpeedForMediaTypeTest(MediaType? mediaType, int expected)
{
Options options = new Options
{
PreferredDumpSpeedCD = 72,
PreferredDumpSpeedDVD = 24,
PreferredDumpSpeedHDDVD = 24,
PreferredDumpSpeedBD = 16,
};
int actual = FrontendTool.GetDefaultSpeedForMediaType(mediaType, options);
Assert.Equal(expected, actual);
}
#endregion
#region GetRedumpSystemFromVolumeLabel
[Theory]
[InlineData(null, null)]
[InlineData("", null)]
[InlineData("Audio CD", RedumpSystem.AudioCD)]
[InlineData("SEP13011042", RedumpSystem.MicrosoftXbox)]
[InlineData("SEP13011042072", RedumpSystem.MicrosoftXbox)]
[InlineData("XBOX360", RedumpSystem.MicrosoftXbox360)]
[InlineData("XGD2DVD_NTSC", RedumpSystem.MicrosoftXbox360)]
[InlineData("Sega_CD", RedumpSystem.SegaMegaCDSegaCD)]
[InlineData("PS3VOLUME", RedumpSystem.SonyPlayStation3)]
[InlineData("PS4VOLUME", RedumpSystem.SonyPlayStation4)]
[InlineData("PS5VOLUME", RedumpSystem.SonyPlayStation5)]
public void GetRedumpSystemFromVolumeLabelTest(string? volumeLabel, RedumpSystem? expected)
{
RedumpSystem? actual = FrontendTool.GetRedumpSystemFromVolumeLabel(volumeLabel);
Assert.Equal(expected, actual);
}
#endregion
#region NormalizeDiscTitle
// TODO: Write NormalizeDiscTitle(string?, Language?[]?) test
// TODO: Write NormalizeDiscTitle(string?, Language?) test
#endregion
#region NormalizeOutputPaths
[Theory]
[InlineData(null, false, "")]
[InlineData(null, true, "")]
[InlineData("", false, "")]
[InlineData("", true, "")]
[InlineData("filename.bin", false, "filename.bin")]
[InlineData("filename.bin", true, "filename.bin")]
[InlineData("\"filename.bin\"", false, "filename.bin")]
[InlineData("\"filename.bin\"", true, "filename.bin")]
[InlineData("<filename.bin>", false, "filename.bin")]
[InlineData("<filename.bin>", true, "filename.bin")]
[InlineData("1.2.3.4..bin", false, "1.2.3.4..bin")]
[InlineData("1.2.3.4..bin", true, "1.2.3.4..bin")]
[InlineData("dir/filename.bin", false, "dir/filename.bin")]
[InlineData("dir/filename.bin", true, "dir/filename.bin")]
[InlineData(" dir / filename.bin", false, "dir/filename.bin")]
[InlineData(" dir / filename.bin", true, "dir/filename.bin")]
[InlineData("\0dir/\0filename.bin", false, "_dir/_filename.bin")]
[InlineData("\0dir/\0filename.bin", true, "_dir/_filename.bin")]
public void NormalizeOutputPathsTest(string? path, bool getFullPath, string expected)
{
// Modify expected to account for test data if necessary
if (getFullPath && !string.IsNullOrEmpty(expected))
expected = Path.GetFullPath(expected);
string actual = FrontendTool.NormalizeOutputPaths(path, getFullPath);
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -5,28 +5,10 @@ using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Frontend.Tools
namespace MPF.Frontend.Test.Tools
{
public class InfoToolTests
{
[Theory]
[InlineData(null, "")]
[InlineData(" ", " ")]
[InlineData("super\\blah.bin", "super\\blah.bin")]
[InlineData("super\\hero\\blah.bin", "super\\hero\\blah.bin")]
[InlineData("super.hero\\blah.bin", "super.hero\\blah.bin")]
[InlineData("superhero\\blah.rev.bin", "superhero\\blah.rev.bin")]
[InlineData("super&hero\\blah.bin", "super&hero\\blah.bin")]
[InlineData("superhero\\blah&foo.bin", "superhero\\blah&foo.bin")]
public void NormalizeOutputPathsTest(string? outputPath, string? expectedPath)
{
if (!string.IsNullOrEmpty(expectedPath))
expectedPath = Path.GetFullPath(expectedPath);
string actualPath = FrontendTool.NormalizeOutputPaths(outputPath, true);
Assert.Equal(expectedPath, actualPath);
}
[Fact]
public void ProcessSpecialFieldsCompleteTest()
{
@@ -64,7 +46,7 @@ namespace MPF.Test.Frontend.Tools
// Validate the lines
Assert.Equal(3, splitComments.Length);
Assert.Equal(5, splitContents.Length);
Assert.Equal(4, splitContents.Length);
}
[Fact]

View File

@@ -0,0 +1,792 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Frontend.Tools;
using Xunit;
namespace MPF.Frontend.Test.Tools
{
public class ProtectionToolTests
{
#region SanitizeContextSensitiveProtections
[Fact]
public void SanitizeContextSensitiveProtections_Empty_NoException()
{
Dictionary<string, List<string>>? protections = [];
var actual = ProtectionTool.SanitizeContextSensitiveProtections(protections);
Assert.NotNull(actual);
Assert.Empty(actual);
}
[Fact]
public void SanitizeContextSensitiveProtections_NoMatch_NoChange()
{
Dictionary<string, List<string>>? protections = [];
protections["File1"] = ["Protection 1", "Protection 2"];
var actual = ProtectionTool.SanitizeContextSensitiveProtections(protections);
Assert.NotNull(actual);
string[] keys = [.. actual.Keys];
Assert.Contains("File1", keys);
}
[Fact]
public void SanitizeContextSensitiveProtections_Match_NoSub_NoChange()
{
Dictionary<string, List<string>>? protections = [];
protections["File1"] = ["Protection 1", "Protection 2"];
protections["File2"] = ["SecuROM Release Control - ANYTHING", "Protection 2"];
protections["File3"] = ["Protection 1", "SecuROM Release Control -"];
var actual = ProtectionTool.SanitizeContextSensitiveProtections(protections);
Assert.NotNull(actual);
string[] keys = [.. actual.Keys];
Assert.Contains("File1", keys);
Assert.Contains("File2", keys);
Assert.Contains("File3", keys);
}
[Fact]
public void SanitizeContextSensitiveProtections_Match_Sub_Change()
{
Dictionary<string, List<string>>? protections = [];
protections["File1"] = ["Protection 1", "Protection 2"];
protections["File2"] = ["SecuROM Release Control - ANYTHING"];
protections["File2/FileA"] = ["ANYTHING GitHub ANYTHING"];
protections["File2/FileB"] = ["SecuROM 7"];
protections["File2/FileC"] = ["SecuROM 8"];
protections["File2/FileD"] = ["SecuROM Content Activation"];
protections["File2/FileE"] = ["SecuROM Data File Activation"];
protections["File2/FileF"] = ["Unlock"];
var actual = ProtectionTool.SanitizeContextSensitiveProtections(protections);
Assert.NotNull(actual);
string[] keys = [.. actual.Keys];
Assert.Contains("File1", keys);
Assert.Contains("File2", keys);
Assert.Contains("File2/FileA", keys);
Assert.DoesNotContain("File2/FileB", keys);
Assert.DoesNotContain("File2/FileC", keys);
Assert.DoesNotContain("File2/FileD", keys);
Assert.DoesNotContain("File2/FileE", keys);
Assert.DoesNotContain("File2/FileF", keys);
}
[Fact]
public void SanitizeContextSensitiveProtections_MultiMatch_Sub_Change()
{
Dictionary<string, List<string>>? protections = [];
protections["File1"] = ["Protection 1", "Protection 2"];
protections["File2"] = ["SecuROM Release Control - ANYTHING"];
protections["File2/FileA"] = ["ANYTHING GitHub ANYTHING"];
protections["File2/FileB"] = ["SecuROM 7"];
protections["File2/FileC"] = ["SecuROM 8"];
protections["File3"] = ["SecuROM Release Control - ANYTHING"];
protections["File3/FileD"] = ["SecuROM Content Activation"];
protections["File3/FileE"] = ["SecuROM Data File Activation"];
protections["File3/FileF"] = ["Unlock"];
var actual = ProtectionTool.SanitizeContextSensitiveProtections(protections);
Assert.NotNull(actual);
string[] keys = [.. actual.Keys];
Assert.Contains("File1", keys);
Assert.Contains("File2", keys);
Assert.Contains("File2/FileA", keys);
Assert.DoesNotContain("File2/FileB", keys);
Assert.DoesNotContain("File2/FileC", keys);
Assert.Contains("File3", keys);
Assert.DoesNotContain("File3/FileD", keys);
Assert.DoesNotContain("File3/FileE", keys);
Assert.DoesNotContain("File3/FileF", keys);
}
#endregion
#region SanitizeFoundProtections
[Fact]
public void SanitizeFoundProtections_Exception()
{
List<string> protections =
[
"Anything Else Protection",
"[Access issue when opening file",
"[Exception opening file",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection, Exception occurred while scanning [RESCAN NEEDED]", sanitized);
}
#region Game Engine
[Fact]
public void SanitizeFoundProtections_RenderWare()
{
List<string> protections =
[
"RenderWare",
"RenderWare ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
#endregion
#region Packers
[Fact]
public void SanitizeFoundProtections_dotNetReactor()
{
List<string> protections =
[
".NET Reactor",
".NET Reactor ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_SevenZipSFX()
{
List<string> protections =
[
"7-Zip SFX",
"7-Zip SFX ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_ASPack()
{
List<string> protections =
[
"ASPack",
"ASPack ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_AutoPlayMediaStudio()
{
List<string> protections =
[
"AutoPlay Media Studio",
"AutoPlay Media Studio ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_CaphyonAdvancedInstaller()
{
List<string> protections =
[
"Caphyon Advanced Installer",
"Caphyon Advanced Installer ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_CExe()
{
List<string> protections =
[
"CExe",
"CExe ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_dotFuscator()
{
List<string> protections =
[
"dotFuscator",
"dotFuscator ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_EmbeddedArchive()
{
List<string> protections =
[
"Embedded 7-zip Archive",
"Embedded PKZIP Archive",
"Embedded RAR Archive",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_EmbeddedExecutable()
{
List<string> protections =
[
"Embedded Executable",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_EXEStealth()
{
List<string> protections =
[
"EXE Stealth",
"EXE Stealth ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_GenteeInstaller()
{
List<string> protections =
[
"Gentee Installer",
"Gentee Installer ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_HyperTechCrackProof()
{
List<string> protections =
[
"HyperTech CrackProof",
"HyperTech CrackProof ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_InnoSetup()
{
List<string> protections =
[
"Inno Setup",
"Inno Setup ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_InstallAnywhere()
{
List<string> protections =
[
"InstallAnywhere",
"InstallAnywhere ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_InstallerVISE()
{
List<string> protections =
[
"Installer VISE",
"Installer VISE ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_IntelInstallationFramework()
{
List<string> protections =
[
"Intel Installation Framework",
"Intel Installation Framework ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_MicrosoftCABSFX()
{
List<string> protections =
[
"Microsoft CAB SFX",
"Microsoft CAB SFX ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_NeoLite()
{
List<string> protections =
[
"NeoLite",
"NeoLite ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_NSIS()
{
List<string> protections =
[
"NSIS",
"NSIS ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_PECompact()
{
List<string> protections =
[
"PE Compact",
"PE Compact ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_PEtite()
{
List<string> protections =
[
"PEtite",
"PEtite ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_SetupFactory()
{
List<string> protections =
[
"Setup Factory",
"Setup Factory ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_Shrinker()
{
List<string> protections =
[
"Shrinker",
"Shrinker ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_UPX()
{
List<string> protections =
[
"UPX",
"UPX ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_WinRARSFX()
{
List<string> protections =
[
"WinRAR SFX",
"WinRAR SFX ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_WinZipSFX()
{
List<string> protections =
[
"WinZip SFX",
"WinZip SFX ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_WiseInstaller()
{
List<string> protections =
[
"Wise Installation",
"Wise Installation ANYTHING",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
#endregion
#region Protections
[Fact]
public void SanitizeFoundProtections_ActiveMARK()
{
List<string> protections =
[
"ActiveMARK",
"ActiveMARK 5",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("ActiveMARK 5", sanitized);
}
[Fact]
public void SanitizeFoundProtections_CactusDataShield()
{
List<string> protections =
[
"Cactus Data Shield 200",
"Cactus Data Shield 200 (Build 3.0.100a)",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("Cactus Data Shield 200 (Build 3.0.100a)", sanitized);
}
[Fact]
public void SanitizeFoundProtections_CactusDataShieldMacrovision()
{
List<string> protections =
[
"Anything Else Protection",
"Cactus Data Shield 300 (Confirm presence of other CDS-300 files)",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection, Cactus Data Shield 300", sanitized);
}
[Fact]
public void SanitizeFoundProtections_CDCheck()
{
List<string> protections =
[
"Anything Else Protection",
"Executable-Based CD Check",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
}
[Fact]
public void SanitizeFoundProtections_CDCops()
{
List<string> protections =
[
"CD-Cops",
"CD-Cops v1.2.0",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("CD-Cops v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtections_CDKey()
{
List<string> protections =
[
"Anything Else Protection",
"CD-Key / Serial",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
}
[Fact]
public void SanitizeFoundProtections_EACdKey()
{
List<string> protections =
[
"EA CdKey Registration Module",
"EA CdKey Registration Module v1.2.0",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Empty(sanitized);
}
[Fact]
public void SanitizeFoundProtections_EADRM()
{
List<string> protections =
[
"EA DRM Protection",
"EA DRM Protection v1.2.0",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("EA DRM Protection v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtections_GFWL()
{
List<string> protections =
[
"Games for Windows LIVE",
"Games for Windows LIVE v1.2.0",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("Games for Windows LIVE v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtections_GFWLZDPP()
{
List<string> protections =
[
"Games for Windows LIVE",
"Games for Windows LIVE Zero Day Piracy Protection",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("Games for Windows LIVE, Games for Windows LIVE Zero Day Piracy Protection", sanitized);
}
[Fact]
public void SanitizeFoundProtections_ImpulseReactor()
{
List<string> protections =
[
"Impulse Reactor",
"Impulse Reactor Core Module v1.2.0",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("Impulse Reactor Core Module v1.2.0", sanitized);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public void SanitizeFoundProtections_JoWoodXProtTest(int skip)
{
List<string> protections =
[
"JoWood X-Prot 1.2.0.00",
"JoWood X-Prot v2",
"JoWood X-Prot v1.4+",
"JoWood X-Prot v1.0-v1.3",
"JoWood X-Prot",
];
// Safeguard for the future
if (skip >= protections.Count)
throw new ArgumentException("Invalid skip value", nameof(skip));
// The list is in order of preference
protections = protections.Skip(skip).ToList();
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal(protections[0], sanitized);
}
[Fact]
public void SanitizeFoundProtections_OnlineRegistration()
{
List<string> protections =
[
"Anything Else Protection",
"Executable-Based Online Registration",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
}
[Theory]
[InlineData(0, "Macrovision Protected Application [SafeDisc 0.00.000], SafeDisc 0.00.000, SafeDisc Lite")]
[InlineData(1, "Macrovision Protected Application [SafeDisc 0.00.000 / SRV Tool APP], SafeDisc 0.00.000, SafeDisc Lite")]
[InlineData(2, "Macrovision Security Driver, Macrovision Security Driver [SafeDisc 1.11.111], SafeDisc 0.00.000, SafeDisc 0.00.000-1.11.111, SafeDisc Lite")]
[InlineData(3, "Macrovision Security Driver, Macrovision Security Driver [SafeDisc 1.11.111], SafeDisc 0.00.000, SafeDisc Lite")]
[InlineData(4, "Macrovision Security Driver, Macrovision Security Driver [SafeDisc 1.11.111], SafeDisc Lite")]
[InlineData(5, "Macrovision Security Driver, SafeDisc Lite")]
[InlineData(6, "Macrovision Protection File, SafeDisc 2+, SafeDisc 3+ (DVD), SafeDisc Lite")]
[InlineData(7, "SafeDisc 3+ (DVD)")]
[InlineData(8, "SafeDisc 2+")]
public void SanitizeFoundProtections_SafeDisc(int skip, string expected)
{
List<string> protections =
[
"Macrovision Protected Application [SafeDisc 0.00.000]",
"Macrovision Protected Application [SafeDisc 0.00.000 / SRV Tool APP]",
"SafeDisc 0.00.000-1.11.111",
"SafeDisc 0.00.000",
"Macrovision Security Driver [SafeDisc 1.11.111]",
"Macrovision Security Driver",
"SafeDisc Lite",
"SafeDisc 3+ (DVD)",
"SafeDisc 2+",
"Macrovision Protection File",
];
// Safeguard for the future
if (skip >= protections.Count)
throw new ArgumentException("Invalid skip value", nameof(skip));
// The list is in order of preference
protections = protections.Skip(skip).ToList();
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal(expected, sanitized);
}
[Fact]
public void SanitizeFoundProtections_SecuROM()
{
List<string> protections =
[
"SecuROM Release Control",
"SecuROM Release Control - ANYTHING",
"SecuROM Release Control - ANYTHING ELSE",
"SecuROM Release Control - EVEN MORE",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("SecuROM Release Control", sanitized);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public void SanitizeFoundProtections_StarForce(int skip)
{
List<string> protections =
[
"StarForce 1.20.000.000",
"StarForce 5 [Protected Module]",
"StarForce 5",
"StarForce 3-5",
"StarForce",
];
// Safeguard for the future
if (skip >= protections.Count)
throw new ArgumentException("Invalid skip value", nameof(skip));
// The list is in order of preference
protections = protections.Skip(skip).ToList();
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal(protections[0], sanitized);
}
[Fact]
public void SanitizeFoundProtections_Sysiphus()
{
List<string> protections =
[
"Sysiphus",
"Sysiphus v1.2.0",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("Sysiphus v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtections_XCP()
{
List<string> protections =
[
"XCP",
"XCP v1.2.0",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("XCP v1.2.0", sanitized);
}
#endregion
#endregion
}
}

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,8 +1,7 @@
using System;
using BinaryObjectScanner;
using MPF.Frontend;
namespace MPF.CLI
namespace MPF.Frontend
{
public static class ConsoleLogger
{

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if NET462_OR_GREATER || NETCOREAPP
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Generic;
@@ -136,8 +136,13 @@ 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>
@@ -223,7 +228,7 @@ 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);
}
@@ -252,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
@@ -282,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;
});
}
}
}
@@ -292,7 +299,7 @@ namespace MPF.Frontend
}
#endif
return drives;
return [.. drives];
}
/// <summary>

View File

@@ -2,11 +2,7 @@
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;
@@ -53,7 +49,7 @@ namespace MPF.Frontend
/// <summary>
/// Options object representing user-defined options
/// </summary>
private readonly Frontend.Options _options;
private readonly Options _options;
/// <summary>
/// Processor object representing how to process the outputs
@@ -65,11 +61,6 @@ namespace MPF.Frontend
/// </summary>
private readonly RedumpSystem? _system;
/// <summary>
/// Currently selected media type
/// </summary>
private readonly MediaType? _type;
#endregion
#region Passthrough Fields
@@ -118,16 +109,12 @@ namespace MPF.Frontend
/// <param name="outputPath"></param>
/// <param name="drive"></param>
/// <param name="system"></param>
/// <param name="type"></param>
/// <param name="internalProgram"></param>
/// <param name="parameters"></param>
public DumpEnvironment(Frontend.Options options,
public DumpEnvironment(Options options,
string? outputPath,
Drive? drive,
RedumpSystem? system,
MediaType? type,
InternalProgram? internalProgram,
string? parameters)
InternalProgram? internalProgram)
{
// Set options object
_options = options;
@@ -138,12 +125,7 @@ namespace MPF.Frontend
// UI information
_drive = drive;
_system = system ?? options.DefaultSystem;
_type = type ?? MediaType.NONE;
_internalProgram = internalProgram ?? options.InternalProgram;
// Dumping program
SetExecutionContext(parameters);
SetProcessor();
}
#region Internal Program Management
@@ -151,31 +133,60 @@ namespace MPF.Frontend
/// <summary>
/// Check output path for matching logs from all dumping programs
/// </summary>
public InternalProgram? CheckForMatchingProgram(string? outputDirectory, string outputFilename)
public InternalProgram? CheckForMatchingProgram(MediaType? mediaType, string? outputDirectory, string outputFilename)
{
// If a complete dump exists from a different program
InternalProgram? programFound = null;
if (programFound == null && _internalProgram != InternalProgram.Aaru)
if (programFound == null && _internalProgram != InternalProgram.Redumper)
{
var processor = new Processors.Aaru(_system, _type);
var missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
var processor = new Processors.Redumper(_system);
var missingFiles = processor.FoundAllFiles(mediaType, outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.Aaru;
programFound = InternalProgram.Redumper;
}
if (programFound == null && _internalProgram != InternalProgram.DiscImageCreator)
{
var processor = new Processors.DiscImageCreator(_system, _type);
var missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
var processor = new Processors.DiscImageCreator(_system);
var missingFiles = processor.FoundAllFiles(mediaType, outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.DiscImageCreator;
}
if (programFound == null && _internalProgram != InternalProgram.Aaru)
{
var processor = new Processors.Aaru(_system);
var missingFiles = processor.FoundAllFiles(mediaType, outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.Aaru;
}
return programFound;
}
/// <summary>
/// Check output path for partial logs from all dumping programs
/// </summary>
public InternalProgram? CheckForPartialProgram(MediaType? mediaType, string? outputDirectory, string outputFilename)
{
// If a complete dump exists from a different program
InternalProgram? programFound = null;
if (programFound == null && _internalProgram != InternalProgram.Redumper)
{
var processor = new Processors.Redumper(_system, _type);
var missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
if (missingFiles.Count == 0)
var processor = new Processors.Redumper(_system);
if (processor.FoundAnyFiles(mediaType, outputDirectory, outputFilename))
programFound = InternalProgram.Redumper;
}
if (programFound == null && _internalProgram != InternalProgram.DiscImageCreator)
{
var processor = new Processors.DiscImageCreator(_system);
if (processor.FoundAnyFiles(mediaType, outputDirectory, outputFilename))
programFound = InternalProgram.DiscImageCreator;
}
if (programFound == null && _internalProgram != InternalProgram.Aaru)
{
var processor = new Processors.Aaru(_system);
if (processor.FoundAnyFiles(mediaType, outputDirectory, outputFilename))
programFound = InternalProgram.Aaru;
}
return programFound;
}
@@ -183,8 +194,9 @@ namespace MPF.Frontend
/// <summary>
/// Set the parameters object based on the internal program and parameters string
/// </summary>
/// <param name="mediaType">MediaType for specialized dumping parameters</param>
/// <param name="parameters">String representation of the parameters</param>
public bool SetExecutionContext(string? parameters)
public bool SetExecutionContext(MediaType? mediaType, string? parameters)
{
_executionContext = _internalProgram switch
{
@@ -200,8 +212,8 @@ namespace MPF.Frontend
// Set system, type, and drive
if (_executionContext != null)
{
_executionContext.System = _system;
_executionContext.Type = _type;
_executionContext.RedumpSystem = _system;
_executionContext.MediaType = mediaType;
// Set some parameters, if not already set
OutputPath ??= _executionContext.OutputPath!;
@@ -218,13 +230,13 @@ namespace MPF.Frontend
{
_processor = _internalProgram switch
{
InternalProgram.Aaru => new Processors.Aaru(_system, _type),
InternalProgram.CleanRip => new CleanRip(_system, _type),
InternalProgram.DiscImageCreator => new DiscImageCreator(_system, _type),
InternalProgram.PS3CFW => new PS3CFW(_system, _type),
InternalProgram.Redumper => new Redumper(_system, _type),
InternalProgram.UmdImageCreator => new UmdImageCreator(_system, _type),
InternalProgram.XboxBackupCreator => new XboxBackupCreator(_system, _type),
InternalProgram.Aaru => new Processors.Aaru(_system),
InternalProgram.CleanRip => new CleanRip(_system),
InternalProgram.DiscImageCreator => new DiscImageCreator(_system),
InternalProgram.PS3CFW => new PS3CFW(_system),
InternalProgram.Redumper => new Redumper(_system),
InternalProgram.UmdImageCreator => new UmdImageCreator(_system),
InternalProgram.XboxBackupCreator => new XboxBackupCreator(_system),
// If no dumping program found, set to null
InternalProgram.NONE => null,
@@ -237,12 +249,13 @@ namespace MPF.Frontend
/// <summary>
/// Get the full parameter string for either DiscImageCreator or Aaru
/// </summary>
/// <param name="mediaType">MediaType for specialized dumping parameters</param>
/// <param name="driveSpeed">Nullable int representing the drive speed</param>
/// <returns>String representing the params, null on error</returns>
public string? GetFullParameters(int? driveSpeed)
public string? GetFullParameters(MediaType? mediaType, int? driveSpeed)
{
// Populate with the correct params for inputs (if we're not on the default option)
if (_system != null && _type != MediaType.NONE)
if (_system != null && mediaType != MediaType.NONE)
{
// If drive letter is invalid, skip this
if (_drive == null)
@@ -251,9 +264,9 @@ namespace MPF.Frontend
// Set the proper parameters
_executionContext = _internalProgram switch
{
InternalProgram.Aaru => new ExecutionContexts.Aaru.ExecutionContext(_system, _type, _drive.Name, OutputPath, driveSpeed, _options.Settings),
InternalProgram.DiscImageCreator => new ExecutionContexts.DiscImageCreator.ExecutionContext(_system, _type, _drive.Name, OutputPath, driveSpeed, _options.Settings),
InternalProgram.Redumper => new ExecutionContexts.Redumper.ExecutionContext(_system, _type, _drive.Name, OutputPath, driveSpeed, _options.Settings),
InternalProgram.Aaru => new ExecutionContexts.Aaru.ExecutionContext(_system, mediaType, _drive.Name, OutputPath, driveSpeed, _options.Settings),
InternalProgram.DiscImageCreator => new ExecutionContexts.DiscImageCreator.ExecutionContext(_system, mediaType, _drive.Name, OutputPath, driveSpeed, _options.Settings),
InternalProgram.Redumper => new ExecutionContexts.Redumper.ExecutionContext(_system, mediaType, _drive.Name, OutputPath, driveSpeed, _options.Settings),
// If no dumping program found, set to null
InternalProgram.NONE => null,
@@ -279,9 +292,9 @@ namespace MPF.Frontend
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>True if the media has variable dumping speeds, false otherwise</returns>
public bool DoesSupportDriveSpeed()
public bool DoesSupportDriveSpeed(MediaType? mediaType)
{
return _type switch
return mediaType switch
{
MediaType.CDROM
or MediaType.DVD
@@ -289,18 +302,28 @@ namespace MPF.Frontend
or MediaType.HDDVD
or MediaType.BluRay
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => true,
or MediaType.NintendoWiiOpticalDisc
or MediaType.NintendoWiiUOpticalDisc => true,
_ => false,
};
}
/// <inheritdoc cref="BaseProcessor.FoundAllFiles(string?, string)"/>
public bool FoundAllFiles(string? outputDirectory, string outputFilename)
/// <inheritdoc cref="BaseProcessor.FoundAllFiles(MediaType?, string?, string)"/>
public bool FoundAllFiles(MediaType? mediaType, string? outputDirectory, string outputFilename)
{
if (_processor == null)
return false;
return _processor.FoundAllFiles(outputDirectory, outputFilename).Count == 0;
return _processor.FoundAllFiles(mediaType, outputDirectory, outputFilename).Count == 0;
}
/// <inheritdoc cref="BaseProcessor.FoundAnyFiles(MediaType?, string?, string)"/>
public bool FoundAnyFiles(MediaType? mediaType, string? outputDirectory, string outputFilename)
{
if (_processor == null)
return false;
return _processor.FoundAnyFiles(mediaType, outputDirectory, outputFilename);
}
/// <inheritdoc cref="BaseExecutionContext.GetDefaultExtension(MediaType?)"/>
@@ -324,14 +347,14 @@ namespace MPF.Frontend
/// <summary>
/// Verify that, given a system and a media type, they are correct
/// </summary>
public ResultEventArgs GetSupportStatus()
public ResultEventArgs GetSupportStatus(MediaType? mediaType)
{
// No system chosen, update status
if (_system == null)
return ResultEventArgs.Failure("Please select a valid system");
// If we're on an unsupported type, update the status accordingly
return _type switch
return mediaType switch
{
// Fully supported types
MediaType.BluRay
@@ -342,21 +365,22 @@ namespace MPF.Frontend
or MediaType.CompactFlash
or MediaType.SDCard
or MediaType.FlashDrive
or MediaType.HDDVD => ResultEventArgs.Success($"{_type.LongName()} ready to dump"),
or MediaType.HDDVD => ResultEventArgs.Success($"{mediaType.LongName()} ready to dump"),
// Partially supported types
MediaType.GDROM
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => ResultEventArgs.Success($"{_type.LongName()} partially supported for dumping"),
or MediaType.NintendoWiiOpticalDisc
or MediaType.NintendoWiiUOpticalDisc => ResultEventArgs.Success($"{mediaType.LongName()} partially supported for dumping"),
// Special case for other supported tools
MediaType.UMD => ResultEventArgs.Failure($"{_type.LongName()} supported for submission info parsing"),
MediaType.UMD => ResultEventArgs.Failure($"{mediaType.LongName()} supported for submission info parsing"),
// Specifically unknown type
MediaType.NONE => ResultEventArgs.Failure($"Please select a valid media type"),
// Undumpable but recognized types
_ => ResultEventArgs.Failure($"{_type.LongName()} media are not supported for dumping"),
_ => ResultEventArgs.Failure($"{mediaType.LongName()} media are not supported for dumping"),
};
}
@@ -384,15 +408,24 @@ namespace MPF.Frontend
/// <summary>
/// Execute the initial invocation of the dumping programs
/// </summary>
/// <param name="mediaType">MediaType for specialized dumping parameters</param>
/// <param name="progress">Optional result progress callback</param>
public async Task<ResultEventArgs> Run(IProgress<ResultEventArgs>? progress = null)
public async Task<ResultEventArgs> Run(MediaType? mediaType, IProgress<ResultEventArgs>? progress = null)
{
// If we don't have parameters
if (_executionContext == null)
return ResultEventArgs.Failure("Error! Current configuration is not supported!");
// Build default console progress indicators if none exist
if (progress == null)
{
var temp = new Progress<ResultEventArgs>();
temp.ProgressChanged += ConsoleLogger.ProgressUpdated;
progress = temp;
}
// Check that we have the basics for dumping
ResultEventArgs result = IsValidForDump();
ResultEventArgs result = IsValidForDump(mediaType);
if (!result)
return result;
@@ -430,151 +463,161 @@ namespace MPF.Frontend
if (_processor == null)
return ResultEventArgs.Failure("Error! Current configuration is not supported!");
resultProgress?.Report(ResultEventArgs.Success("Gathering submission information... please wait!"));
// Build default console progress indicators if none exist
if (resultProgress == null)
{
var temp = new Progress<ResultEventArgs>();
temp.ProgressChanged += ConsoleLogger.ProgressUpdated;
resultProgress = temp;
}
if (protectionProgress == null)
{
var temp = new Progress<ProtectionProgress>();
temp.ProgressChanged += ConsoleLogger.ProgressUpdated;
protectionProgress = temp;
}
resultProgress.Report(ResultEventArgs.Success("Gathering submission information... please wait!"));
// Get the output directory and filename separately
var outputDirectory = Path.GetDirectoryName(OutputPath);
var outputFilename = Path.GetFileName(OutputPath);
// Check to make sure that the output had all the correct files
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!");
}
// Determine the media type from the processor
MediaType? mediaType = _processor.DetermineMediaType(outputDirectory, outputFilename);
// Extract the information from the output files
resultProgress?.Report(ResultEventArgs.Success("Extracting output information from output files..."));
resultProgress.Report(ResultEventArgs.Success("Extracting output information from output files..."));
var submissionInfo = await SubmissionGenerator.ExtractOutputInformation(
OutputPath,
_drive,
_system,
_type,
mediaType,
_options,
_processor,
resultProgress,
protectionProgress);
resultProgress?.Report(ResultEventArgs.Success("Extracting information complete!"));
if (submissionInfo == null)
{
resultProgress.Report(ResultEventArgs.Failure("There was an issue extracting information!"));
return ResultEventArgs.Failure();
}
else
{
resultProgress.Report(ResultEventArgs.Success("Extracting information complete!"));
}
// Inject seed submission info data, if necessary
if (seedInfo != null)
{
resultProgress?.Report(ResultEventArgs.Success("Injecting user-supplied information..."));
Builder.InjectSubmissionInformation(submissionInfo, seedInfo);
resultProgress?.Report(ResultEventArgs.Success("Information injection complete!"));
resultProgress.Report(ResultEventArgs.Success("Injecting user-supplied information..."));
submissionInfo = Builder.InjectSubmissionInformation(submissionInfo, seedInfo);
resultProgress.Report(ResultEventArgs.Success("Information injection complete!"));
}
// Get user-modifiable information if confugured to
// Get user-modifiable information if configured to
if (_options.PromptForDiscInformation && processUserInfo != null)
{
resultProgress?.Report(ResultEventArgs.Success("Waiting for additional disc information..."));
bool? filledInfo = processUserInfo(ref submissionInfo);
resultProgress.Report(ResultEventArgs.Success("Waiting for additional media information..."));
bool? filledInfo = processUserInfo.Invoke(_options, ref submissionInfo);
if (filledInfo == true)
resultProgress?.Report(ResultEventArgs.Success("Additional disc information added!"));
resultProgress.Report(ResultEventArgs.Success("Additional media information added!"));
else
resultProgress?.Report(ResultEventArgs.Success("Disc information skipped!"));
resultProgress.Report(ResultEventArgs.Success("Media information skipped!"));
}
// Process special fields for site codes
resultProgress?.Report(ResultEventArgs.Success("Processing site codes..."));
Formatter.ProcessSpecialFields(submissionInfo);
resultProgress?.Report(ResultEventArgs.Success("Processing complete!"));
resultProgress.Report(ResultEventArgs.Success("Processing site codes..."));
Formatter.ProcessSpecialFields(submissionInfo!);
resultProgress.Report(ResultEventArgs.Success("Processing complete!"));
// Format the information for the text output
resultProgress?.Report(ResultEventArgs.Success("Formatting information..."));
resultProgress.Report(ResultEventArgs.Success("Formatting information..."));
var formattedValues = Formatter.FormatOutputData(submissionInfo, _options.EnableRedumpCompatibility, out string? formatResult);
if (formattedValues == null)
resultProgress?.Report(ResultEventArgs.Failure(formatResult));
resultProgress.Report(ResultEventArgs.Failure(formatResult));
else
resultProgress?.Report(ResultEventArgs.Success(formatResult));
resultProgress.Report(ResultEventArgs.Success(formatResult));
// Get the filename suffix for auto-generated files
var filenameSuffix = _options.AddFilenameSuffix ? Path.GetFileNameWithoutExtension(outputFilename) : null;
// Write the text output
resultProgress?.Report(ResultEventArgs.Success("Writing submission information file..."));
resultProgress.Report(ResultEventArgs.Success("Writing submission information file..."));
bool txtSuccess = WriteOutputData(outputDirectory, filenameSuffix, formattedValues, out string txtResult);
if (txtSuccess)
resultProgress?.Report(ResultEventArgs.Success(txtResult));
resultProgress.Report(ResultEventArgs.Success(txtResult));
else
resultProgress?.Report(ResultEventArgs.Failure(txtResult));
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 information file..."));
resultProgress.Report(ResultEventArgs.Success("Writing protection information file..."));
bool scanSuccess = WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo, _options.HideDriveLetters);
if (scanSuccess)
resultProgress?.Report(ResultEventArgs.Success("Writing complete!"));
resultProgress.Report(ResultEventArgs.Success("Writing complete!"));
else
resultProgress?.Report(ResultEventArgs.Failure("Writing could not complete!"));
resultProgress.Report(ResultEventArgs.Failure("Writing could not complete!"));
}
}
// Write the JSON output, if required
if (_options.OutputSubmissionJSON)
{
resultProgress?.Report(ResultEventArgs.Success($"Writing submission information JSON file{(_options.IncludeArtifacts ? " with artifacts" : 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!"));
resultProgress.Report(ResultEventArgs.Success("Writing complete!"));
else
resultProgress?.Report(ResultEventArgs.Failure("Writing could not complete!"));
resultProgress.Report(ResultEventArgs.Failure("Writing could not complete!"));
}
// Compress the logs, if required
if (_options.CompressLogFiles)
{
resultProgress?.Report(ResultEventArgs.Success("Compressing log files..."));
if (_processor == null)
resultProgress.Report(ResultEventArgs.Success("Compressing log files..."));
#if NET40
await Task.Factory.StartNew(() =>
#else
await Task.Run(() =>
#endif
{
resultProgress?.Report(ResultEventArgs.Failure("No processor provided!"));
}
else
{
bool compressSuccess = _processor.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, out string compressResult);
bool compressSuccess = _processor.CompressLogFiles(mediaType, outputDirectory, outputFilename, filenameSuffix, out string compressResult);
if (compressSuccess)
resultProgress?.Report(ResultEventArgs.Success(compressResult));
resultProgress.Report(ResultEventArgs.Success(compressResult));
else
resultProgress?.Report(ResultEventArgs.Failure(compressResult));
}
resultProgress.Report(ResultEventArgs.Failure(compressResult));
return compressSuccess;
});
}
// Delete unnecessary files, if required
if (_options.DeleteUnnecessaryFiles)
{
resultProgress?.Report(ResultEventArgs.Success("Deleting unnecessary files..."));
if (_processor == null)
{
resultProgress?.Report(ResultEventArgs.Failure("No processor provided!"));
}
resultProgress.Report(ResultEventArgs.Success("Deleting unnecessary files..."));
bool deleteSuccess = _processor.DeleteUnnecessaryFiles(mediaType, outputDirectory, outputFilename, out string deleteResult);
if (deleteSuccess)
resultProgress.Report(ResultEventArgs.Success(deleteResult));
else
{
bool deleteSuccess = _processor.DeleteUnnecessaryFiles(outputDirectory, outputFilename, out string deleteResult);
if (deleteSuccess)
resultProgress?.Report(ResultEventArgs.Success(deleteResult));
else
resultProgress?.Report(ResultEventArgs.Failure(deleteResult));
}
resultProgress.Report(ResultEventArgs.Failure(deleteResult));
}
// Create PS3 IRD, if required
if (_options.CreateIRDAfterDumping && _system == RedumpSystem.SonyPlayStation3 && _type == MediaType.BluRay)
if (_options.CreateIRDAfterDumping && _system == RedumpSystem.SonyPlayStation3 && mediaType == MediaType.BluRay)
{
resultProgress?.Report(ResultEventArgs.Success("Creating IRD... please wait!"));
bool deleteSuccess = await WriteIRD(OutputPath, submissionInfo?.Extras?.DiscKey, submissionInfo?.Extras?.DiscID, submissionInfo?.Extras?.PIC, submissionInfo?.SizeAndChecksums?.Layerbreak, submissionInfo?.SizeAndChecksums?.CRC32);
resultProgress.Report(ResultEventArgs.Success("Creating IRD... please wait!"));
bool deleteSuccess = await IRDTool.WriteIRD(OutputPath, submissionInfo?.Extras?.DiscKey, submissionInfo?.Extras?.DiscID, submissionInfo?.Extras?.PIC, submissionInfo?.SizeAndChecksums?.Layerbreak, submissionInfo?.SizeAndChecksums?.CRC32);
if (deleteSuccess)
resultProgress?.Report(ResultEventArgs.Success("IRD created!"));
resultProgress.Report(ResultEventArgs.Success("IRD created!"));
else
resultProgress?.Report(ResultEventArgs.Failure("Failed to create IRD"));
resultProgress.Report(ResultEventArgs.Failure("Failed to create IRD"));
}
resultProgress?.Report(ResultEventArgs.Success("Submission information process complete!"));
resultProgress.Report(ResultEventArgs.Success("Submission information process complete!"));
return ResultEventArgs.Success();
}
@@ -582,18 +625,18 @@ namespace MPF.Frontend
/// Checks if the parameters are valid
/// </summary>
/// <returns>True if the configuration is valid, false otherwise</returns>
internal bool ParametersValid()
internal bool ParametersValid(MediaType? mediaType)
{
// Missing drive means it can never be valid
if (_drive == null)
return false;
bool parametersValid = _executionContext?.IsValid() ?? false;
bool floppyValid = !(_drive.InternalDriveType == InternalDriveType.Floppy ^ _type == MediaType.FloppyDisk);
bool floppyValid = !(_drive.InternalDriveType == InternalDriveType.Floppy ^ mediaType == MediaType.FloppyDisk);
// TODO: HardDisk being in the Removable category is a hack, fix this later
bool removableDiskValid = !((_drive.InternalDriveType == InternalDriveType.Removable || _drive.InternalDriveType == InternalDriveType.HardDisk)
^ (_type == MediaType.CompactFlash || _type == MediaType.SDCard || _type == MediaType.FlashDrive || _type == MediaType.HardDisk));
^ (mediaType == MediaType.CompactFlash || mediaType == MediaType.SDCard || mediaType == MediaType.FlashDrive || mediaType == MediaType.HardDisk));
return parametersValid && floppyValid && removableDiskValid;
}
@@ -602,10 +645,10 @@ namespace MPF.Frontend
/// Validate the current environment is ready for a dump
/// </summary>
/// <returns>Result instance with the outcome</returns>
private ResultEventArgs IsValidForDump()
private ResultEventArgs IsValidForDump(MediaType? mediaType)
{
// Validate that everything is good
if (_executionContext == null || !ParametersValid())
if (_executionContext == null || !ParametersValid(mediaType))
return ResultEventArgs.Failure("Error! Current configuration is not supported!");
// Fix the output paths, just in case
@@ -625,7 +668,7 @@ namespace MPF.Frontend
return ResultEventArgs.Failure("Error! Cannot dump same drive that executable resides on!");
// Validate that the current configuration is supported
return GetSupportStatus();
return GetSupportStatus(mediaType);
}
#endregion
@@ -635,11 +678,11 @@ namespace MPF.Frontend
/// <summary>
/// Write the data to the output folder
/// </summary>
/// <param name="outputDirectory">Output folder to write to</param>
/// <param name="outputDirectory">Output folder to use as the base path</param>
/// <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>
/// <param name="lines">Preformatted string of lines to write out to the file</param>
/// <returns>True on success, false on error</returns>
private static bool WriteOutputData(string? outputDirectory, string? filenameSuffix, List<string>? lines, out string status)
private static bool WriteOutputData(string? outputDirectory, string? filenameSuffix, string? lines, out string status)
{
// Check to see if the inputs are valid
if (lines == null)
@@ -663,10 +706,7 @@ namespace MPF.Frontend
path = Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.txt");
using var sw = new StreamWriter(File.Open(path, FileMode.Create, FileAccess.Write), Encoding.UTF8);
foreach (string line in lines)
{
sw.WriteLine(line);
}
sw.Write(lines);
}
catch (Exception ex)
{
@@ -682,7 +722,7 @@ namespace MPF.Frontend
/// <summary>
/// Write the data to the output folder
/// </summary>
/// <param name="outputDirectory">Output folder to write to</param>
/// <param name="outputDirectory">Output folder to use as the base path</param>
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
/// <param name="info">SubmissionInfo object representing the JSON to write out to the file</param>
/// <param name="includedArtifacts">True if artifacts were included, false otherwise</param>
@@ -695,48 +735,43 @@ namespace MPF.Frontend
try
{
// Serialize the JSON and get it writable
string json = JsonConvert.SerializeObject(info, Formatting.Indented);
byte[] jsonBytes = Encoding.UTF8.GetBytes(json);
// Get the output path
var path = string.Empty;
if (string.IsNullOrEmpty(outputDirectory) && string.IsNullOrEmpty(filenameSuffix))
path = "!submissionInfo.json";
else if (string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
path = $"!submissionInfo_{filenameSuffix}.json";
else if (!string.IsNullOrEmpty(outputDirectory) && string.IsNullOrEmpty(filenameSuffix))
path = Path.Combine(outputDirectory, "!submissionInfo.json");
else if (!string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
path = Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json");
// Ensure the extension is correct for the output
if (includedArtifacts)
path += ".gz";
// Create and open the output file
using var fs = File.Create(path);
// Create the JSON serializer
var serializer = new JsonSerializer { Formatting = Formatting.Indented };
// If we included artifacts, write to a GZip-compressed file
if (includedArtifacts)
{
var path = string.Empty;
if (string.IsNullOrEmpty(outputDirectory) && string.IsNullOrEmpty(filenameSuffix))
path = "!submissionInfo.json.gz";
else if (string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
path = $"!submissionInfo_{filenameSuffix}.json.gz";
else if (!string.IsNullOrEmpty(outputDirectory) && string.IsNullOrEmpty(filenameSuffix))
path = Path.Combine(outputDirectory, "!submissionInfo.json.gz");
else if (!string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
path = Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json.gz");
using var fs = File.Create(path);
using var gs = new GZipStream(fs, CompressionMode.Compress);
gs.Write(jsonBytes, 0, jsonBytes.Length);
using var sw = new StreamWriter(gs, Encoding.UTF8);
serializer.Serialize(sw, info);
}
// Otherwise, write out to a normal JSON
else
{
var path = string.Empty;
if (string.IsNullOrEmpty(outputDirectory) && string.IsNullOrEmpty(filenameSuffix))
path = "!submissionInfo.json";
else if (string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
path = $"!submissionInfo_{filenameSuffix}.json";
else if (!string.IsNullOrEmpty(outputDirectory) && string.IsNullOrEmpty(filenameSuffix))
path = Path.Combine(outputDirectory, "!submissionInfo.json");
else if (!string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
path = Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json");
using var fs = File.Create(path);
fs.Write(jsonBytes, 0, jsonBytes.Length);
using var sw = new StreamWriter(fs, Encoding.UTF8);
serializer.Serialize(sw, info);
}
}
catch
{
// We don't care what the error is right now
// We don't care what the error is
return false;
}
@@ -747,7 +782,7 @@ namespace MPF.Frontend
/// <summary>
/// Write the protection data to the output folder
/// </summary>
/// <param name="outputDirectory">Output folder to write to</param>
/// <param name="outputDirectory">Output folder to use as the base path</param>
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
/// <param name="info">SubmissionInfo object containing the protection information</param>
/// <param name="hideDriveLetters">True if drive letters are to be removed from output, false otherwise</param>
@@ -755,7 +790,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
@@ -773,7 +808,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;
@@ -790,73 +827,13 @@ namespace MPF.Frontend
}
catch
{
// We don't care what the error is right now
// We don't care what the error is
return false;
}
return true;
}
/// <summary>
/// Create an IRD and write it to the specified output directory with optional filename suffix
/// </summary>
/// <param name="outputDirectory">Output folder to write to</param>
/// <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> WriteIRD(string isoPath,
string? discKeyString,
string? discIDString,
string? picString,
long? layerbreak,
string? crc32)
{
try
{
// Output IRD file path
string irdPath = Path.ChangeExtension(isoPath, ".ird");
// Parse disc key from submission info (Required)
byte[]? discKey = ProcessingTool.ParseHexKey(discKeyString);
if (discKey == null)
return false;
// Parse Disc ID from submission info (Optional)
byte[]? discID = ProcessingTool.ParseDiscID(discIDString);
// Parse PIC from submission info (Optional)
byte[]? pic = ProcessingTool.ParsePIC(picString);
// Parse CRC32 strings into ISO hash for Unique ID field (Optional)
uint? uid = ProcessingTool.ParseCRC32(crc32);
// Ensure layerbreak value is valid (Optional)
layerbreak = ProcessingTool.ParseLayerbreak(layerbreak);
// Create Redump-style reproducible IRD
#if NET40
LibIRD.ReIRD ird = await Task.Factory.StartNew(() =>
#else
LibIRD.ReIRD ird = await Task.Run(() =>
#endif
new LibIRD.ReIRD(isoPath, discKey, layerbreak, uid));
if (pic != null)
ird.PIC = pic;
if (discID != null && ird.DiscID[15] != 0x00)
ird.DiscID = discID;
// Write IRD to file
ird.Write(irdPath);
return true;
}
catch (Exception)
{
// We don't care what the error is
return false;
}
}
#endregion
}
}

View File

@@ -6,6 +6,7 @@ using System.Collections.Concurrent;
#endif
using System.Reflection;
using SabreTools.RedumpLib.Data;
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
@@ -49,7 +50,7 @@ namespace MPF.Frontend
}
if (method != null)
return method.Invoke(null, new[] { value }) as string ?? string.Empty;
return method.Invoke(null, [value]) as string ?? string.Empty;
else
return string.Empty;
}
@@ -67,7 +68,7 @@ namespace MPF.Frontend
/// <returns>String representing the value, if possible</returns>
public static string LongName(this InternalProgram? prog)
{
return (prog) switch
return prog switch
{
#region Dumping support
@@ -98,11 +99,10 @@ namespace MPF.Frontend
/// <returns>String representing the value, if possible</returns>
public static string LongName(this RedumperReadMethod? method)
{
return (method) switch
return method switch
{
RedumperReadMethod.D8 => "D8",
RedumperReadMethod.BE => "BE",
RedumperReadMethod.BE_CDDA => "BE_CDDA",
RedumperReadMethod.NONE => "Default",
_ => "Unknown",
@@ -116,7 +116,7 @@ namespace MPF.Frontend
/// <returns>String representing the value, if possible</returns>
public static string LongName(this RedumperSectorOrder? order)
{
return (order) switch
return order switch
{
RedumperSectorOrder.DATA_C2_SUB => "DATA_C2_SUB",
RedumperSectorOrder.DATA_SUB_C2 => "DATA_SUB_C2",
@@ -128,10 +128,110 @@ namespace MPF.Frontend
};
}
/// <summary>
/// Get the string representation of the RedumperDriveType enum values
/// </summary>
/// <param name="order">RedumperDriveType value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this RedumperDriveType? type)
{
return type switch
{
RedumperDriveType.GENERIC => "GENERIC",
RedumperDriveType.PLEXTOR => "PLEXTOR",
RedumperDriveType.LG_ASU8A => "LG_ASU8A",
RedumperDriveType.LG_ASU8B => "LG_ASU8B",
RedumperDriveType.LG_ASU8C => "LG_ASU8C",
RedumperDriveType.LG_ASU3 => "LG_ASU3",
RedumperDriveType.LG_ASU2 => "LG_ASU2",
RedumperDriveType.NONE => "Default",
_ => "Unknown",
};
}
#endregion
#region Convert to Short Name
/// <summary>
/// Get the short string representation of the InternalProgram enum values
/// </summary>
/// <param name="prog">InternalProgram value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string ShortName(this InternalProgram? prog)
{
return prog switch
{
#region Dumping support
InternalProgram.Aaru => "aaru",
InternalProgram.DiscImageCreator => "dic",
InternalProgram.Redumper => "redumper",
#endregion
#region Verification support only
InternalProgram.CleanRip => "cleanrip",
InternalProgram.PS3CFW => "ps3cfw",
InternalProgram.UmdImageCreator => "uic",
InternalProgram.XboxBackupCreator => "xbc",
#endregion
InternalProgram.NONE => "Unknown",
_ => "Unknown",
};
}
#endregion
#region Convert from String
/// <summary>
/// Get the InternalProgram enum value for a given string
/// </summary>
/// <param name="internalProgram">String value to convert</param>
/// <returns>InternalProgram represented by the string, if possible</returns>
public static InternalProgram ToInternalProgram(this string? internalProgram)
{
return (internalProgram?.ToLowerInvariant()) switch
{
// Dumping support
"aaru"
or "chef"
or "dichef"
or "discimagechef" => InternalProgram.Aaru,
"creator"
or "dic"
or "dicreator"
or "discimagecreator" => InternalProgram.DiscImageCreator,
"rd"
or "redumper" => InternalProgram.Redumper,
// Verification support only
"cleanrip"
or "cr" => InternalProgram.CleanRip,
"ps3cfw"
or "ps3"
or "getkey"
or "managunz"
or "multiman" => InternalProgram.PS3CFW,
"uic"
or "umd"
or "umdcreator"
or "umdimagecreator" => InternalProgram.UmdImageCreator,
"xbc"
or "xbox"
or "xbox360"
or "xboxcreator"
or "xboxbackupcreator" => InternalProgram.XboxBackupCreator,
_ => InternalProgram.NONE,
};
}
/// <summary>
/// Get the RedumperReadMethod enum value for a given string
/// </summary>
@@ -143,10 +243,6 @@ namespace MPF.Frontend
{
"d8" => RedumperReadMethod.D8,
"be" => RedumperReadMethod.BE,
"be_cdda"
or "be cdda"
or "be-cdda"
or "becdda" => RedumperReadMethod.BE_CDDA,
_ => RedumperReadMethod.NONE,
};
@@ -182,6 +278,47 @@ namespace MPF.Frontend
};
}
/// <summary>
/// Get the RedumperDriveType enum value for a given string
/// </summary>
/// <param name="order">String value to convert</param>
/// <returns>RedumperDriveType represented by the string, if possible</returns>
public static RedumperDriveType ToRedumperDriveType(this string? type)
{
return (type?.ToLowerInvariant()) switch
{
"generic" => RedumperDriveType.GENERIC,
"plextor" => RedumperDriveType.PLEXTOR,
"lg_asus8a"
or "lg-asus8a"
or "lgasus8a"
or "lg_asus_8a"
or "lg-asus-8a" => RedumperDriveType.LG_ASU8A,
"lg_asus8b"
or "lg-asus8b"
or "lgasus8b"
or "lg_asus_8b"
or "lg-asus-8b" => RedumperDriveType.LG_ASU8B,
"lg_asus8c"
or "lg-asus8c"
or "lgasus8c"
or "lg_asus_8c"
or "lg-asus-8c" => RedumperDriveType.LG_ASU8C,
"lg_asus3"
or "lg-asus3"
or "lgasus3"
or "lg_asus_3"
or "lg-asus-3" => RedumperDriveType.LG_ASU3,
"lg_asus2"
or "lg-asus2"
or "lgasus2"
or "lg_asus_2"
or "lg-asus-2" => RedumperDriveType.LG_ASU2,
_ => RedumperDriveType.NONE,
};
}
#endregion
#region Functionality Support

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend
@@ -12,39 +11,39 @@ namespace MPF.Frontend
/// <summary>
/// Set of all accepted speed values
/// </summary>
private static readonly List<int> _speedValues = [1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72];
private static readonly List<int> _speedValues = [0, 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72];
/// <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 => [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
{
@@ -54,7 +53,8 @@ namespace MPF.Frontend
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => DVD,
MediaType.HDDVD => HDDVD,
MediaType.BluRay => BD,
MediaType.BluRay
or MediaType.NintendoWiiUOpticalDisc => BD,
_ => Unknown,
};
}

View File

@@ -2,68 +2,43 @@
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<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.3</VersionPrefix>
<VersionPrefix>3.4.0</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>
<Copyright>Copyright (c) Matt Nadareski 2019-2025</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="MPF.Test" />
<InternalsVisibleTo Include="MPF.Frontend.Test" />
</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`))">
<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</TargetFrameworks>
</PropertyGroup>
<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(`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.16" 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.4.4" />
<PackageReference Include="BinaryObjectScanner" Version="3.4.2" />
<PackageReference Include="LibIRD" Version="1.0.0" />
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
<PackageReference Include="Microsoft.Net.Http" Version="2.2.29" Condition="$(TargetFramework.StartsWith(`net452`))" />
<PackageReference Include="MinThreadingBridge" Version="0.11.4" Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
<PackageReference Include="System.Net.Http" Version="4.3.4" Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))" />
</ItemGroup>
</Project>

45
MPF.Frontend/OldDotNet.cs Normal file
View File

@@ -0,0 +1,45 @@
#if NET20 || NET35
using System.Collections;
using System.Collections.Generic;
namespace MPF.Frontend
{
internal interface IReadOnlyCollection<T> : IEnumerable<T>
{
int Count { get; }
}
internal sealed class ConcurrentQueue<T> : IReadOnlyCollection<T>
{
private Queue<T> _queue = new Queue<T>();
private object _lock = new object();
public int Count => _queue.Count;
public void Enqueue(T item)
{
lock (_lock)
{
_queue.Enqueue(item);
}
}
public bool TryDequeue(out T item)
{
lock (_lock)
{
item = default(T)!;
if (_queue.Count == 0)
return false;
item = _queue.Dequeue();
return true;
}
}
IEnumerator<T> IEnumerable<T>.GetEnumerator() => _queue.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _queue.GetEnumerator();
}
}
#endif

View File

@@ -2,6 +2,7 @@
using SabreTools.RedumpLib.Data;
using AaruSettings = MPF.ExecutionContexts.Aaru.SettingConstants;
using DICSettings = MPF.ExecutionContexts.DiscImageCreator.SettingConstants;
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
using RedumperSettings = MPF.ExecutionContexts.Redumper.SettingConstants;
@@ -61,7 +62,7 @@ namespace MPF.Frontend
get
{
var valueString = GetStringSetting(Settings, "InternalProgram", InternalProgram.Redumper.ToString());
var valueEnum = ToInternalProgram(valueString);
var valueEnum = valueString.ToInternalProgram();
return valueEnum == InternalProgram.NONE ? InternalProgram.Redumper : valueEnum;
}
set
@@ -86,6 +87,7 @@ namespace MPF.Frontend
/// <summary>
/// Enable purple mode for UI elements
/// </summary>
/// <remarks>This is a hidden setting</remarks>
public bool EnablePurpMode
{
get { return GetBooleanSetting(Settings, "EnablePurpMode", false); }
@@ -95,6 +97,7 @@ namespace MPF.Frontend
/// <summary>
/// Custom color setting
/// </summary>
/// <remarks>This is a hidden setting</remarks>
public string? CustomBackgroundColor
{
get { return GetStringSetting(Settings, "CustomBackgroundColor", null); }
@@ -104,6 +107,7 @@ namespace MPF.Frontend
/// <summary>
/// Custom color setting
/// </summary>
/// <remarks>This is a hidden setting</remarks>
public string? CustomTextColor
{
get { return GetStringSetting(Settings, "CustomTextColor", null); }
@@ -119,6 +123,15 @@ namespace MPF.Frontend
set { Settings["CheckForUpdatesOnStartup"] = value.ToString(); }
}
/// <summary>
/// Try to copy the update URL to the clipboard if one is found
/// </summary>
public bool CopyUpdateUrlToClipboard
{
get { return GetBooleanSetting(Settings, "CopyUpdateUrlToClipboard", true); }
set { Settings["CopyUpdateUrlToClipboard"] = value.ToString(); }
}
/// <summary>
/// Fast update label - Skips disc checks and updates path only
/// </summary>
@@ -144,7 +157,7 @@ namespace MPF.Frontend
{
get
{
var valueString = GetStringSetting(Settings, "DefaultSystem", null);
var valueString = GetStringSetting(Settings, "DefaultSystem", RedumpSystem.IBMPCcompatible.LongName());
var valueEnum = Extensions.ToRedumpSystem(valueString ?? string.Empty);
return valueEnum;
}
@@ -155,7 +168,7 @@ namespace MPF.Frontend
}
/// <summary>
/// Default output path for dumps
/// Show the debug menu item
/// </summary>
/// <remarks>This is a hidden setting</remarks>
public bool ShowDebugViewMenuItem
@@ -330,21 +343,13 @@ namespace MPF.Frontend
#region Redumper
/// <summary>
/// Enable debug output while dumping by default
/// Enable skeleton output while dumping by default
/// </summary>
public bool RedumperEnableDebug
/// <remarks>This is a hidden setting</remarks>
public bool RedumperEnableSkeleton
{
get { return GetBooleanSetting(Settings, RedumperSettings.EnableDebug, RedumperSettings.EnableDebugDefault); }
set { Settings[RedumperSettings.EnableDebug] = value.ToString(); }
}
/// <summary>
/// Enable Redumper custom lead-in retries for Plextor drives
/// </summary>
public bool RedumperEnableLeadinRetry
{
get { return GetBooleanSetting(Settings, RedumperSettings.EnableLeadinRetry, RedumperSettings.EnableLeadinRetryDefault); }
set { Settings[RedumperSettings.EnableLeadinRetry] = value.ToString(); }
get { return GetBooleanSetting(Settings, RedumperSettings.EnableSkeleton, RedumperSettings.EnableSkeletonDefault); }
set { Settings[RedumperSettings.EnableSkeleton] = value.ToString(); }
}
/// <summary>
@@ -375,12 +380,19 @@ namespace MPF.Frontend
}
/// <summary>
/// Enable generic drive type by default with Redumper
/// Currently selected default redumper drive type
/// </summary>
public bool RedumperUseGenericDriveType
public RedumperDriveType RedumperDriveType
{
get { return GetBooleanSetting(Settings, RedumperSettings.UseGenericDriveType, RedumperSettings.UseGenericDriveTypeDefault); }
set { Settings[RedumperSettings.UseGenericDriveType] = value.ToString(); }
get
{
var valueString = GetStringSetting(Settings, RedumperSettings.DriveType, RedumperSettings.DriveTypeDefault);
return valueString.ToRedumperDriveType();
}
set
{
Settings[RedumperSettings.DriveType] = value.ToString();
}
}
/// <summary>
@@ -424,6 +436,15 @@ namespace MPF.Frontend
set { Settings[RedumperSettings.RereadCount] = value.ToString(); }
}
/// <summary>
/// Enable the refine sector mode flag by default
/// </summary>
public bool RedumperRefineSectorMode
{
get { return GetBooleanSetting(Settings, RedumperSettings.RefineSectorMode, RedumperSettings.RefineSectorModeDefault); }
set { Settings[RedumperSettings.RefineSectorMode] = value.ToString(); }
}
#endregion
#region Extra Dumping Options
@@ -447,7 +468,7 @@ namespace MPF.Frontend
}
/// <summary>
/// Show the disc information window after dumping
/// Show the media information window after dumping
/// </summary>
public bool PromptForDiscInformation
{
@@ -469,7 +490,7 @@ namespace MPF.Frontend
/// </summary>
public bool EnableTabsInInputFields
{
get { return GetBooleanSetting(Settings, "EnableTabsInInputFields", false); }
get { return GetBooleanSetting(Settings, "EnableTabsInInputFields", true); }
set { Settings["EnableTabsInInputFields"] = value.ToString(); }
}
@@ -483,7 +504,7 @@ namespace MPF.Frontend
}
/// <summary>
/// Show disc eject reminder before the disc information window is shown
/// Show disc eject reminder before the media information window is shown
/// </summary>
public bool ShowDiscEjectReminder
{
@@ -589,15 +610,6 @@ namespace MPF.Frontend
set { Settings["ScanArchivesForProtection"] = value.ToString(); }
}
/// <summary>
/// Scan for executable packers during protection scanning
/// </summary>
public bool ScanPackersForProtection
{
get { return GetBooleanSetting(Settings, "ScanPackersForProtection", false); }
set { Settings["ScanPackersForProtection"] = value.ToString(); }
}
/// <summary>
/// Include debug information with scan results
/// </summary>
@@ -642,6 +654,15 @@ namespace MPF.Frontend
#region Redump Login Information
/// <summary>
/// Enable retrieving match information from Redump
/// </summary>
public bool RetrieveMatchInformation
{
get { return GetBooleanSetting(Settings, "RetrieveMatchInformation", true); }
set { Settings["RetrieveMatchInformation"] = value.ToString(); }
}
public string? RedumpUsername
{
get { return GetStringSetting(Settings, "RedumpUsername", ""); }
@@ -651,18 +672,10 @@ namespace MPF.Frontend
// TODO: Figure out a way to keep this encrypted in some way, BASE64 to start?
public string? RedumpPassword
{
get
{
return GetStringSetting(Settings, "RedumpPassword", "");
}
get { return GetStringSetting(Settings, "RedumpPassword", ""); }
set { Settings["RedumpPassword"] = value; }
}
/// <summary>
/// Determine if a complete set of Redump credentials might exist
/// </summary>
public bool HasRedumpLogin { get => !string.IsNullOrEmpty(RedumpUsername) && !string.IsNullOrEmpty(RedumpPassword); }
#endregion
/// <summary>
@@ -671,7 +684,7 @@ namespace MPF.Frontend
/// <param name="settings"></param>
public Options(Dictionary<string, string?>? settings = null)
{
this.Settings = settings ?? [];
Settings = settings ?? [];
}
/// <summary>
@@ -688,55 +701,12 @@ 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
/// <summary>
/// Get the InternalProgram enum value for a given string
/// </summary>
/// <param name="internalProgram">String value to convert</param>
/// <returns>InternalProgram represented by the string, if possible</returns>
public static InternalProgram ToInternalProgram(string? internalProgram)
{
return (internalProgram?.ToLowerInvariant()) switch
{
// Dumping support
"aaru"
or "chef"
or "dichef"
or "discimagechef" => InternalProgram.Aaru,
"creator"
or "dic"
or "dicreator"
or "discimagecreator" => InternalProgram.DiscImageCreator,
"rd"
or "redumper" => InternalProgram.Redumper,
// Verification support only
"cleanrip"
or "cr" => InternalProgram.CleanRip,
"ps3cfw"
or "ps3"
or "getkey"
or "managunz"
or "multiman" => InternalProgram.PS3CFW,
"uic"
or "umd"
or "umdcreator"
or "umdimagecreator" => InternalProgram.UmdImageCreator,
"xbc"
or "xbox"
or "xbox360"
or "xboxcreator"
or "xboxbackupcreator" => InternalProgram.XboxBackupCreator,
_ => InternalProgram.NONE,
};
}
/// <summary>
/// Get a Boolean setting from a settings, dictionary
/// </summary>
@@ -744,7 +714,7 @@ namespace MPF.Frontend
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private static bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
internal static bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
{
if (settings.ContainsKey(key))
{
@@ -766,7 +736,7 @@ namespace MPF.Frontend
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private static int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
internal static int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
{
if (settings.ContainsKey(key))
{
@@ -788,7 +758,7 @@ namespace MPF.Frontend
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private static string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
internal static string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
{
if (settings.ContainsKey(key))
return settings[key];

View File

@@ -5,7 +5,8 @@ namespace MPF.Frontend
/// <summary>
/// Determines how user information is processed, if at all
/// </summary>
/// <param name="options">Options set that may impact processing</params>
/// <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);
public delegate bool? ProcessUserInfoDelegate(Options? options, ref SubmissionInfo? info);
}

View File

@@ -1,102 +0,0 @@
using System;
#if NET20 || NET35
using System.Collections.Generic;
#else
using System.Collections.Concurrent;
#endif
using System.Threading;
using System.Threading.Tasks;
namespace MPF.Frontend
{
public sealed class ProcessingQueue<T> : IDisposable
{
/// <summary>
/// Internal queue to hold data to process
/// </summary>
#if NET20 || NET35
private readonly Queue<T> _internalQueue;
#else
private readonly ConcurrentQueue<T> _internalQueue;
#endif
/// <summary>
/// Custom processing step for dequeued data
/// </summary>
private readonly Action<T> _customProcessing;
/// <summary>
/// Cancellation method for the processing task
/// </summary>
private readonly CancellationTokenSource _tokenSource;
public ProcessingQueue(Action<T> customProcessing)
{
#if NET20 || NET35
_internalQueue = new Queue<T>();
#else
_internalQueue = new ConcurrentQueue<T>();
#endif
_customProcessing = customProcessing;
_tokenSource = new CancellationTokenSource();
#if NET20 || NET35
Task.Run(() => ProcessQueue());
#elif NET40
Task.Factory.StartNew(() => ProcessQueue());
#else
Task.Run(() => ProcessQueue(), _tokenSource.Token);
#endif
}
/// <summary>
/// Dispose the current instance
/// </summary>
public void Dispose() => _tokenSource.Cancel();
/// <summary>
/// Enqueue a new item for processing
/// </summary>
/// <param name="item"></param>
public void Enqueue(T? item)
{
// Only accept new data when not cancelled
if (item != null && !_tokenSource.IsCancellationRequested)
_internalQueue.Enqueue(item);
}
/// <summary>
/// Process
/// </summary>
private void ProcessQueue()
{
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;
Thread.Sleep(1);
continue;
}
#if NET20 || NET35
// Get the next item from the queue and invoke the lambda, if possible
_customProcessing?.Invoke(_internalQueue.Dequeue());
#else
// Get the next item from the queue
if (!_internalQueue.TryDequeue(out var nextItem))
continue;
// Invoke the lambda, if possible
_customProcessing?.Invoke(nextItem);
#endif
}
}
}
}

108
MPF.Frontend/Progress.cs Normal file
View File

@@ -0,0 +1,108 @@
#if NET20 || NET35 || NET40
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Threading;
using System.Diagnostics;
namespace MPF.Frontend
{
/// <summary>
/// Provides an IProgress{T} that invokes callbacks for each reported progress value.
/// </summary>
/// <typeparam name="T">Specifies the type of the progress report value.</typeparam>
/// <remarks>
/// Any handler provided to the constructor or event handlers registered with
/// the <see cref="ProgressChanged"/> event are invoked through a
/// <see cref="SynchronizationContext"/> instance captured
/// when the instance is constructed. If there is no current SynchronizationContext
/// at the time of construction, the callbacks will be invoked on the ThreadPool.
/// </remarks>
/// <see href="https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Progress.cs"/>
internal class Progress<T> : IProgress<T> where T : EventArgs
{
/// <summary>The synchronization context captured upon construction. This will never be null.</summary>
private readonly SynchronizationContext? _synchronizationContext;
/// <summary>The handler specified to the constructor. This may be null.</summary>
private readonly Action<T>? _handler;
/// <summary>A cached delegate used to post invocation to the synchronization context.</summary>
private readonly SendOrPostCallback _invokeHandlers;
/// <summary>Initializes the <see cref="Progress{T}"/>.</summary>
public Progress()
{
// Capture the current synchronization context.
// If there is no current context, we use a default instance targeting the ThreadPool.
_synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext;
Debug.Assert(_synchronizationContext != null);
_invokeHandlers = new SendOrPostCallback(InvokeHandlers);
}
/// <summary>Initializes the <see cref="Progress{T}"/> with the specified callback.</summary>
/// <param name="handler">
/// A handler to invoke for each reported progress value. This handler will be invoked
/// in addition to any delegates registered with the <see cref="ProgressChanged"/> event.
/// Depending on the <see cref="SynchronizationContext"/> instance captured by
/// the <see cref="Progress{T}"/> at construction, it's possible that this handler instance
/// could be invoked concurrently with itself.
/// </param>
/// <exception cref="ArgumentNullException">The <paramref name="handler"/> is null (<see langword="Nothing" /> in Visual Basic).</exception>
public Progress(Action<T> handler) : this()
{
_handler = handler ?? throw new ArgumentNullException(nameof(handler));
}
/// <summary>Raised for each reported progress value.</summary>
/// <remarks>
/// Handlers registered with this event will be invoked on the
/// <see cref="SynchronizationContext"/> captured when the instance was constructed.
/// </remarks>
public event EventHandler<T>? ProgressChanged;
/// <summary>Reports a progress change.</summary>
/// <param name="value">The value of the updated progress.</param>
protected virtual void OnReport(T value)
{
// If there's no handler, don't bother going through the sync context.
// Inside the callback, we'll need to check again, in case
// an event handler is removed between now and then.
Action<T>? handler = _handler;
EventHandler<T>? changedEvent = ProgressChanged;
if (handler != null || changedEvent != null)
{
// Post the processing to the sync context.
// (If T is a value type, it will get boxed here.)
_synchronizationContext?.Post(_invokeHandlers, value);
}
}
/// <summary>Reports a progress change.</summary>
/// <param name="value">The value of the updated progress.</param>
void IProgress<T>.Report(T value) { OnReport(value); }
/// <summary>Invokes the action and event callbacks.</summary>
/// <param name="state">The progress value.</param>
private void InvokeHandlers(object? state)
{
T value = (T)state!;
Action<T>? handler = _handler;
EventHandler<T>? changedEvent = ProgressChanged;
handler?.Invoke(value);
changedEvent?.Invoke(this, value);
}
}
/// <summary>Holds static values for <see cref="Progress{T}"/>.</summary>
/// <remarks>This avoids one static instance per type T.</remarks>
internal static class ProgressStatics
{
/// <summary>A default synchronization context that targets the ThreadPool.</summary>
internal static readonly SynchronizationContext DefaultContext = new();
}
}
#endif

View File

@@ -1,8 +1,8 @@
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend.Tools
@@ -32,6 +32,7 @@ namespace MPF.Frontend.Tools
// BD dump speed
MediaType.BluRay => options.PreferredDumpSpeedBD,
MediaType.NintendoWiiUOpticalDisc => options.PreferredDumpSpeedBD,
// Default
_ => options.PreferredDumpSpeedCD,
@@ -59,15 +60,26 @@ namespace MPF.Frontend.Tools
return RedumpSystem.MicrosoftXbox;
// Microsoft Xbox 360
if (volumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase))
if (volumeLabel.Equals("XBOX360"))
return RedumpSystem.MicrosoftXbox360;
else if (volumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase))
else if (volumeLabel.Equals("XGD2DVD_NTSC"))
return RedumpSystem.MicrosoftXbox360;
// Microsoft Xbox 360 - Too overly broad even if a lot of discs use this
//if (volumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase))
// return RedumpSystem.MicrosoftXbox360; // Also for Xbox One?
//if (volumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
else if (volumeLabel.Equals("XBOX_TINYTEST"))
return RedumpSystem.MicrosoftXbox360;
else if (volumeLabel.Equals("13599"))
return RedumpSystem.MicrosoftXbox360;
else if (volumeLabel.Equals("14719"))
return RedumpSystem.MicrosoftXbox360;
else if (volumeLabel.Equals("15574"))
return RedumpSystem.MicrosoftXbox360;
else if (volumeLabel.Equals("16197"))
return RedumpSystem.MicrosoftXbox360;
else if (volumeLabel.Equals("16197"))
return RedumpSystem.MicrosoftXbox360;
else if (volumeLabel.Equals("17349"))
return RedumpSystem.MicrosoftXbox360;
// DVD_ROM and CD_ROM have too many false positives
//else if (volumeLabel.Equals("DVD_ROM"))
// return RedumpSystem.MicrosoftXbox360;
// Sega Mega-CD / Sega-CD
@@ -115,7 +127,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
@@ -143,7 +155,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)
@@ -511,7 +523,6 @@ namespace MPF.Frontend.Tools
/// <param name="path">Path value to normalize</param>
public static string NormalizeOutputPaths(string? path, bool getFullPath)
{
// The easy way
try
{
// If we have an invalid path
@@ -519,30 +530,33 @@ namespace MPF.Frontend.Tools
return string.Empty;
// Remove quotes and angle brackets from path
path = path!.Replace("\"", string.Empty);
path = path!.Replace("<", string.Empty);
path = path!.Replace(">", string.Empty);
path = path!.Trim('\"');
path = path!.Trim('<');
path = path!.Trim('>');
// Remove invalid path characters
foreach (char c in Path.GetInvalidPathChars())
{
path = path.Replace(c, '_');
}
// Try getting the combined path and returning that directly
string fullPath = getFullPath ? Path.GetFullPath(path) : path;
var fullDirectory = Path.GetDirectoryName(fullPath);
string fullFile = Path.GetFileName(fullPath);
// Remove invalid path characters
if (fullDirectory != null)
{
foreach (char c in Path.GetInvalidPathChars())
fullDirectory = fullDirectory.Replace(c, '_');
}
var fullDirectory = Path.GetDirectoryName(fullPath)?.Trim();
string fullFile = Path.GetFileName(fullPath).Trim();
// Remove invalid filename characters
foreach (char c in Path.GetInvalidFileNameChars())
{
fullFile = fullFile.Replace(c, '_');
}
if (string.IsNullOrEmpty(fullDirectory))
return fullFile;
else
return Path.Combine(fullDirectory, fullFile);
// Rebuild the path, if necessary
if (!string.IsNullOrEmpty(fullDirectory))
fullFile = Path.Combine(fullDirectory, fullFile);
// Remove spaces before and after separators
return Regex.Replace(fullFile, @"\s*([\\|/])\s*", @"$1");
}
catch { }

View File

@@ -0,0 +1,99 @@
using System;
using System.IO;
using System.Threading.Tasks;
using MPF.Processors;
namespace MPF.Frontend.Tools
{
internal static class IRDTool
{
/// <summary>
/// Create an IRD and write it next to the input ISO path
/// </summary>
/// <param name="isoPath">Path to the original ISO</param>
/// <param name="discKeyString">Required hexadecimal disc key as a string</param>
/// <param name="discIDString">Optional hexadecimal disc ID as a string</param>
/// <param name="picString">Optional string representation of the PIC information</param>
/// <param name="layerbreak">Optional disc layerbreak value</param>
/// <param name="crc32">Optional ISO CRC-32 value for the Unique ID field</param>
/// <returns>True on success, false on error</returns>
public static async Task<bool> WriteIRD(string isoPath,
string? discKeyString,
string? discIDString,
string? picString,
long? layerbreak,
string? crc32)
{
try
{
// Parse string values into required formats
byte[]? discKey = ProcessingTool.ParseHexKey(discKeyString);
byte[]? discID = ProcessingTool.ParseDiscID(discIDString);
byte[]? pic = ProcessingTool.ParsePIC(picString);
uint? uid = ProcessingTool.ParseCRC32(crc32);
return await WriteIRD(isoPath, discKey, discID, pic, layerbreak, uid);
}
catch (Exception)
{
// We don't care what the error is
return false;
}
}
/// <summary>
/// Create an IRD and write it next to the input ISO path
/// </summary>
/// <param name="isoPath">Path to the original ISO</param>
/// <param name="discKey">Required hexadecimal disc key as a byte array</param>
/// <param name="discID">Optional hexadecimal disc ID as a byte array</param>
/// <param name="pic">Optional byte array representation of the PIC information</param>
/// <param name="layerbreak">Optional disc layerbreak value</param>
/// <param name="uid">Optional ISO CRC-32 value for the Unique ID field</param>
/// <returns>True on success, false on error</returns>
public static async Task<bool> WriteIRD(string isoPath,
byte[]? discKey,
byte[]? discID,
byte[]? pic,
long? layerbreak,
uint? uid)
{
try
{
// Fail on a missing disc key
if (discKey == null)
return false;
// Output IRD file path
string irdPath = Path.ChangeExtension(isoPath, ".ird");
// Ensure layerbreak value is valid
layerbreak = ProcessingTool.ParseLayerbreak(layerbreak);
// Create Redump-style reproducible IRD
#if NET40
LibIRD.ReIRD ird = await Task.Factory.StartNew(() =>
#else
LibIRD.ReIRD ird = await Task.Run(() =>
#endif
new LibIRD.ReIRD(isoPath, discKey, layerbreak, uid));
// Set optional fields if valid
if (pic != null)
ird.PIC = pic;
if (discID != null && ird.DiscID[15] != 0x00)
ird.DiscID = discID;
// Write IRD to file
ird.Write(irdPath);
return true;
}
catch (Exception)
{
// We don't care what the error is
return false;
}
}
}
}

View File

@@ -9,14 +9,26 @@ namespace MPF.Frontend.Tools
{
public static class OptionsLoader
{
/// <summary>
/// Configuration file name
/// </summary>
private const string ConfigurationFileName = "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
private static string ConfigurationPath
{
get
{
if (_configPath != null)
return _configPath;
_configPath = GetConfigurationPath();
return _configPath;
}
}
private static string? _configPath = null;
#region Arguments
@@ -27,9 +39,15 @@ namespace MPF.Frontend.Tools
public static bool? ProcessStandaloneArguments(string[] args)
{
// Help options
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?" || args[0] == "--help")
return null;
if (args[0] == "--version")
{
Console.WriteLine(FrontendTool.GetCurrentVersion() ?? "Unknown version");
return true;
}
// List options
if (args[0] == "-lc" || args[0] == "--listcodes")
{
@@ -75,6 +93,32 @@ namespace MPF.Frontend.Tools
return false;
}
/// <summary>
/// Process common arguments for all functionality
/// </summary>
/// <returns>True if all arguments pass, false otherwise</returns>
public static bool ProcessCommonArguments(string[] args, out RedumpSystem? system, out string? message)
{
// All other use requires at least 3 arguments
if (args.Length < 2)
{
system = null;
message = "Invalid number of arguments";
return false;
}
// Check the RedumpSystem
system = Extensions.ToRedumpSystem(args[0].Trim('"'));
if (system == null)
{
message = $"{args[0]} is not a recognized system";
return false;
}
message = null;
return true;
}
/// <summary>
/// Process common arguments for all functionality
/// </summary>
@@ -111,32 +155,14 @@ namespace MPF.Frontend.Tools
return true;
}
/// <summary>
/// List all programs with their short usable names
/// </summary>
private static List<string> ListPrograms()
{
var programs = new List<string>();
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
{
if (((InternalProgram)val!) == InternalProgram.NONE)
continue;
programs.Add($"{((InternalProgram?)val).LongName()}");
}
return programs;
}
/// <summary>
/// Get the MediaType enum value for a given string
/// </summary>
/// <param name="type">String value to convert</param>
/// <returns>MediaType represented by the string, if possible</returns>
private static MediaType ToMediaType(string type)
public static MediaType ToMediaType(string? type)
{
return (type.ToLowerInvariant()) switch
return (type?.ToLowerInvariant()) switch
{
#region Punched Media
@@ -272,6 +298,24 @@ namespace MPF.Frontend.Tools
};
}
/// <summary>
/// List all programs with their short usable names
/// </summary>
private static List<string> ListPrograms()
{
var programs = new List<string>();
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
{
if (((InternalProgram)val!) == InternalProgram.NONE)
continue;
programs.Add($"{((InternalProgram?)val).ShortName()} - {((InternalProgram?)val).LongName()}");
}
return programs;
}
#endregion
#region Configuration
@@ -281,7 +325,12 @@ namespace MPF.Frontend.Tools
/// </summary>
public static Options LoadFromConfig()
{
if (!File.Exists(ConfigurationPath))
// If no options path can be found
if (string.IsNullOrEmpty(ConfigurationPath))
return new Options();
// Ensure the file exists
if (!File.Exists(ConfigurationPath) || new FileInfo(ConfigurationPath).Length == 0)
{
File.Create(ConfigurationPath).Dispose();
return new Options();
@@ -298,25 +347,26 @@ namespace MPF.Frontend.Tools
/// <summary>
/// Save the current set of options to the application configuration
/// </summary>
public static void SaveToConfig(Options options, bool saveDefault = false)
public static void SaveToConfig(Options options)
{
// If default values should be saved as well
if (saveDefault)
// If no options path can be found
if (string.IsNullOrEmpty(ConfigurationPath))
return;
// Ensure default values are included
PropertyInfo[] properties = typeof(Options).GetProperties();
foreach (var property in properties)
{
PropertyInfo[] properties = typeof(Options).GetProperties();
foreach (var property in properties)
{
// Skip dictionary properties
if (property.Name == "Item")
continue;
// Skip dictionary properties
if (property.Name == "Item")
continue;
// Skip non-option properties
if (property.Name == "Settings" || property.Name == "HasRedumpLogin")
continue;
// Skip non-option properties
if (property.Name == "Settings" || property.Name == "HasRedumpLogin")
continue;
var val = property.GetValue(options, null);
property.SetValue(options, val, null);
}
var val = property.GetValue(options, null);
property.SetValue(options, val, null);
}
// Handle a very strange edge case
@@ -329,6 +379,53 @@ namespace MPF.Frontend.Tools
serializer.Serialize(writer, options.Settings, typeof(Dictionary<string, string>));
}
/// <summary>
/// Attempt to determine the configuration path
/// </summary>
private static string GetConfigurationPath()
{
// User home directory
#if NET20 || NET35
string homeDir = Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%");
homeDir = Path.Combine(Path.Combine(homeDir, ".config"), "mpf");
#else
string homeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
homeDir = Path.Combine(homeDir, ".config", "mpf");
#endif
if (File.Exists(Path.Combine(homeDir, ConfigurationFileName)))
return Path.Combine(homeDir, ConfigurationFileName);
// Local folder
#if NET20 || NET35 || NET40 || NET452
string runtimeDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
#else
string runtimeDir = AppContext.BaseDirectory;
#endif
if (File.Exists(Path.Combine(runtimeDir, ConfigurationFileName)))
return Path.Combine(runtimeDir, ConfigurationFileName);
// Attempt to use local folder
try
{
Directory.CreateDirectory(runtimeDir);
File.Create(Path.Combine(runtimeDir, ConfigurationFileName)).Dispose();
return Path.Combine(runtimeDir, ConfigurationFileName);
}
catch { }
// Attempt to use home directory
try
{
Directory.CreateDirectory(homeDir);
File.Create(Path.Combine(homeDir, ConfigurationFileName)).Dispose();
return Path.Combine(homeDir, ConfigurationFileName);
}
catch { }
// This should not happen
return string.Empty;
}
#endregion
}
}

View File

@@ -1,11 +1,11 @@
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SabreTools.IO;
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend.Tools
{
@@ -13,7 +13,7 @@ namespace MPF.Frontend.Tools
{
#region Generic
/// <summary>s
/// <summary>
/// Get the last modified date for a file from a physical disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
@@ -38,20 +38,66 @@ namespace MPF.Frontend.Tools
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;
try
{
// 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;
// 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");
// Format and return the string
var dt = new DateTime(year, month, day);
return dt.ToString("yyyy-MM-dd");
}
catch
{
// We don't care what the error is
return null;
}
}
/// <summary>
/// Get the first numBytes bytes from a disc drive
/// </summary>
/// <param name="drive">Drive to get sector from</param>
/// <param name="numBytes">Number of bytes to read from drive, maximum of one sector (2048 bytes)</param>
/// <returns>Byte array of first sector of data, null on error</returns>
public static byte[]? GetFirstBytes(Drive? drive, int numBytes)
{
if (drive == null || drive.Letter == null || drive.Letter == '\0')
return null;
// Must read between 1 and 2048 bytes
if (numBytes < 1)
return null;
else if (numBytes > 2048)
numBytes = 2048;
string drivePath = $"\\\\.\\{drive.Letter}:";
var firstSector = new byte[numBytes];
try
{
// Open the drive as a raw device
using var driveStream = new FileStream(drivePath, FileMode.Open, FileAccess.Read);
// Read the first sector
int bytesRead = driveStream.Read(firstSector, 0, numBytes);
if (bytesRead < numBytes)
return null;
}
catch
{
// We don't care what the error is
return null;
}
return firstSector;
}
#endregion
@@ -103,7 +149,7 @@ namespace MPF.Frontend.Tools
}
catch
{
// We don't care what the error is right now
// We don't care what the error is
return false;
}
}
@@ -131,32 +177,36 @@ namespace MPF.Frontend.Tools
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))
try
{
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)", RegexOptions.Compiled);
if (match.Groups.Count > 1)
// 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))
{
string? serial = match.Groups[1].Value;
// Some games may have the EXE in a subfolder
serial = Path.GetFileName(serial);
return serial;
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)", RegexOptions.Compiled);
if (match.Groups.Count > 1)
{
// Some games may have the EXE in a subfolder
string? serial = match.Groups[1].Value;
return Path.GetFileName(serial);
}
}
}
catch
{
// We don't care what the error is, assume SYSTEM.CNF doesn't exist
}
// If the SYSTEM.CNF value can't be found, try PSX.EXE
if (File.Exists(psxExePath))
@@ -212,13 +262,21 @@ namespace MPF.Frontend.Tools
// 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"];
try
{
// 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;
// If "VER" can't be found, we can't do much
return null;
}
catch
{
// We don't care what the error is
return null;
}
}
/// <summary>
@@ -453,6 +511,59 @@ namespace MPF.Frontend.Tools
}
}
/// <summary>
/// Get the app.pkg info from a PlayStation 4 disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>PKG info if possible, null on error</returns>
public static string? GetPlayStation4PkgInfo(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;
// Try parse the app.pkg (multiple if they exist)
try
{
string? pkgInfo = "";
string[] appDirs = Directory.GetDirectories(Path.Combine(drive.Name, "app"), "?????????", SearchOption.TopDirectoryOnly);
foreach (string dir in appDirs)
{
string appPkgPath = Path.Combine(dir, "app.pkg");
if (!File.Exists(appPkgPath))
continue;
long appPkgSize = new FileInfo(appPkgPath).Length;
if (appPkgSize < 4096)
continue;
// Read the app.pkg header
using var fileStream = new FileStream(appPkgPath, FileMode.Open, FileAccess.Read);
var appPkgHeaderDeserializer = new SabreTools.Serialization.Deserializers.AppPkgHeader();
var appPkgHeader = appPkgHeaderDeserializer.Deserialize(fileStream);
if (appPkgHeader != null)
pkgInfo += $"{appPkgHeader.ContentID}{Environment.NewLine}";
}
if (pkgInfo == "")
return null;
else
return pkgInfo;
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the internal serial from a PlayStation 5 disc, if possible
/// </summary>
@@ -531,7 +642,9 @@ namespace MPF.Frontend.Tools
private static JObject? GetPlayStation5ParamsJsonFromFile(string? filename)
{
// If the file doesn't exist
if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
if (string.IsNullOrEmpty(filename))
return null;
if (!File.Exists(filename))
return null;
// Let's try reading param.json to find the version in the unencrypted JSON
@@ -549,6 +662,59 @@ namespace MPF.Frontend.Tools
}
}
/// <summary>
/// Get the app.pkg info from a PlayStation 5 disc, if possible
/// </summary>
/// <param name="drive">Drive to extract information from</param>
/// <returns>PKG info if possible, null on error</returns>
public static string? GetPlayStation5PkgInfo(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;
// Try parse the app_sc.pkg (multiple if they exist)
try
{
string? pkgInfo = "";
string[] appDirs = Directory.GetDirectories(Path.Combine(drive.Name, "app"), "?????????", SearchOption.TopDirectoryOnly);
foreach (string dir in appDirs)
{
string appPkgPath = Path.Combine(dir, "app_sc.pkg");
if (!File.Exists(appPkgPath))
continue;
long appPkgSize = new FileInfo(appPkgPath).Length;
if (appPkgSize < 4096)
continue;
// Read the app_sc.pkg header
using var fileStream = new FileStream(appPkgPath, FileMode.Open, FileAccess.Read);
var appPkgHeaderDeserializer = new SabreTools.Serialization.Deserializers.AppPkgHeader();
var appPkgHeader = appPkgHeaderDeserializer.Deserialize(fileStream);
if (appPkgHeader != null)
pkgInfo += $"{appPkgHeader.ContentID}{Environment.NewLine}";
}
if (pkgInfo == "")
return null;
else
return pkgInfo;
}
catch
{
// We don't care what the error was
return null;
}
}
#endregion
#region Xbox
@@ -576,16 +742,77 @@ namespace MPF.Frontend.Tools
try
{
var files = Directory.GetFiles(msxc, "*", SearchOption.TopDirectoryOnly);
var filenames = files.Select(Path.GetFileName).ToArray();
var filenames = Array.ConvertAll(files, Path.GetFileName);
return string.Join("\n", filenames);
}
catch
{
// We don't care what the error is right now
// We don't care what the error is
return null;
}
}
#endregion
#region Sega
/// <summary>
/// Detect the Sega system based on the CD ROM header
/// </summary>
/// <param name="drive">Drive to detect system from</param>
/// <returns>Detected RedumpSystem if detected, null otherwise</returns>
public static RedumpSystem? DetectSegaSystem(Drive? drive)
{
if (drive == null)
return null;
byte[]? firstSector = GetFirstBytes(drive, 0x10);
if (firstSector == null || firstSector.Length < 0x10)
return null;
string systemType = Encoding.ASCII.GetString(firstSector, 0x00, 0x10);
if (systemType.Equals("SEGA SEGASATURN ", StringComparison.Ordinal))
return RedumpSystem.SegaSaturn;
else if (systemType.Equals("SEGA SEGAKATANA ", StringComparison.Ordinal))
return RedumpSystem.SegaDreamcast;
else if (systemType.Equals("SEGADISCSYSTEM ", StringComparison.Ordinal))
return RedumpSystem.SegaMegaCDSegaCD;
else if (systemType.Equals("SEGA MEGA DRIVE ", StringComparison.Ordinal))
return RedumpSystem.SegaMegaCDSegaCD;
else if (systemType.Equals("SEGA GENESIS ", StringComparison.Ordinal))
return RedumpSystem.SegaMegaCDSegaCD;
return null;
}
#endregion
#region Other
/// <summary>
/// Detect a 3DO disc based on the CD ROM header
/// </summary>
/// <param name="drive">Drive to detect 3DO disc from</param>
/// <returns>RedumpSystem.Panasonic3DOInteractiveMultiplayer if detected, null otherwise</returns>
public static RedumpSystem? Detect3DOSystem(Drive? drive)
{
if (drive == null)
return null;
byte[]? firstSector = GetFirstBytes(drive, 0xC0);
if (firstSector == null || firstSector.Length < 0xC0)
return null;
string systemType = Encoding.ASCII.GetString(firstSector, 0xB0, 0x10);
if (systemType.Equals("iamaduckiamaduck", StringComparison.Ordinal))
return RedumpSystem.Panasonic3DOInteractiveMultiplayer;
return null;
}
#endregion
}
}

View File

@@ -1,15 +1,70 @@
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;
using SabreTools.IO.Extensions;
namespace MPF.Frontend.Tools
{
public static class ProtectionTool
{
/// <summary>
/// Set of protection prefixes to filter on
/// </summary>
/// <remarks>Based on Redump requirements</remarks>
private static readonly string[] FilterPrefixes = [
#region Game Engine
"RenderWare",
#endregion
#region Packers
".NET Reactor",
"7-Zip SFX",
"ASPack",
"AutoPlay Media Studio",
"Caphyon Advanced Installer",
"CExe",
"dotFuscator",
"Embedded",
"EXE Stealth",
"Gentee Installer",
"HyperTech CrackProof",
"Inno Setup",
"InstallAnywhere",
"Installer VISE",
"Intel Installation Framework",
"Microsoft CAB SFX",
"NeoLite",
"NSIS",
"PE Compact",
"PEtite",
"Setup Factory",
"Shrinker",
"UPX",
"WinRAR SFX",
"WinZip SFX",
"Wise Installation",
#endregion
#region Protections
"CD-Key / Serial",
"EA CdKey",
"Executable-Based CD Check",
"Executable-Based Online Registration",
#endregion
];
/// <summary>
/// Run protection scan on a given path
/// </summary>
@@ -17,8 +72,8 @@ 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<ProtectionDictionary> RunProtectionScanOnPath(string path,
Frontend.Options options,
public static async Task<Dictionary<string, List<string>>> RunProtectionScanOnPath(string path,
Options options,
IProgress<ProtectionProgress>? progress = null)
{
#if NET40
@@ -30,9 +85,8 @@ namespace MPF.Frontend.Tools
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
scanSubdirectories: true, // Hardcoded value to avoid issues
options.IncludeDebugProtectionInformation,
progress);
@@ -43,9 +97,6 @@ namespace MPF.Frontend.Tools
if (found == null || found.Count == 0)
return [];
// Filter out any empty protections
found.ClearEmptyKeys();
// Return the filtered set of protections
return found;
}
@@ -55,7 +106,7 @@ 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(ProtectionDictionary? protections)
public static string? FormatProtections(Dictionary<string, List<string>>? protections)
{
// If the filtered list is empty in some way, return
if (protections == null)
@@ -63,14 +114,32 @@ namespace MPF.Frontend.Tools
else if (protections.Count == 0)
return "None found [OMIT FROM SUBMISSION]";
// Get an ordered list of distinct found protections
var orderedDistinctProtections = protections
// Sanitize context-sensitive protections
protections = SanitizeContextSensitiveProtections(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]";
@@ -90,37 +159,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
{
@@ -136,280 +182,374 @@ namespace MPF.Frontend.Tools
return false;
});
#endif
}
/// <summary>
/// Sanitize unnecessary protections where context matters
/// </summary>
/// <param name="protections">Dictionary of file to list of protection mappings</param>
/// <returns>Dictionary with all necessary items filtered out</returns>
public static Dictionary<string, List<string>> SanitizeContextSensitiveProtections(Dictionary<string, List<string>> protections)
{
// Setup the output dictionary
Dictionary<string, List<string>> filtered = [];
// Setup a list for keys that need additional processing
List<string> foundKeys = [];
// Loop through the keys and add relevant ones
string[] paths = [.. protections.Keys];
foreach (var path in paths)
{
if (!protections.TryGetValue(path, out var values) || values == null || values.Count == 0)
continue;
// Always copy the values if they're valid
filtered[path] = values;
if (values.Exists(s => s.StartsWith("SecuROM Release Control -")))
foundKeys.Add(path);
}
// If there are no keys found
if (foundKeys.Count == 0)
return filtered;
// Process the keys as necessary
foreach (var key in foundKeys)
{
// Get all matching paths
var matchingPaths = Array.FindAll(paths, s => s != key && s.StartsWith(key));
if (matchingPaths.Length == 0)
continue;
// Loop through the matching paths
foreach (var path in matchingPaths)
{
if (!filtered.TryGetValue(path, out var values) || values == null || values.Count == 0)
continue;
if (values.Exists(s => !s.Contains("GitHub") &&
(s.Contains("SecuROM 7")
|| s.Contains("SecuROM 8")
|| s.Contains("SecuROM Content Activation")
|| s.Contains("SecuROM Data File Activation")
|| s.Contains("Unlock"))))
{
filtered.Remove(path);
}
}
}
return filtered;
}
/// <summary>
/// Sanitize unnecessary protection duplication from output
/// </summary>
/// <param name="foundProtections">Enumerable of found protections</param>
public static string SanitizeFoundProtections(IEnumerable<string> foundProtections)
/// <remarks>
/// This filtering only impacts the information that goes into the single-line
/// protection field in the output submission info. The filtering performed by
/// this method applies to the needs of Redump and not necessarily any other
/// application. The full protection list should be used as a reference in all
/// other cases.
/// </remarks>
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") && !p.StartsWith("[Access issue when opening file"));
foundProtections.Add("Exception occurred while scanning [RESCAN NEEDED]");
}
if (foundProtections.Exists(p => p.StartsWith("[Access issue when opening file")))
{
foundProtections = foundProtections.FindAll(p => !p.StartsWith("[Exception opening file") && !p.StartsWith("[Access issue when opening file"));
foundProtections.Add("Exception occurred while scanning [RESCAN NEEDED]");
}
// Filtered prefixes
foreach (string prefix in FilterPrefixes)
{
foundProtections = foundProtections.FindAll(p => !p.StartsWith(prefix));
}
// 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");
// Cactus Data Shield / SafeDisc
if (foundProtections.Exists(p => p == "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)"))
{
foundProtections = foundProtections
.FindAll(p => p != "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)");
if (foundProtections.Exists(p => !p.StartsWith("SafeDisc")))
foundProtections.Add("Cactus Data Shield 300");
}
// CD-Cops
if (foundProtections.Any(p => p == "CD-Cops") && foundProtections.Any(p => p.StartsWith("CD-Cops") && p.Length > "CD-Cops".Length))
foundProtections = foundProtections.Where(p => p != "CD-Cops");
// CD-Key / Serial
foundProtections = foundProtections.Where(p => p != "CD-Key / Serial");
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");
}
// 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 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");
}
}
// LaserLok
// TODO: Figure this one out
// Online Registration
foundProtections = foundProtections.Where(p => !p.StartsWith("Executable-Based Online Registration"));
// ProtectDISC / VOB ProtectCD/DVD
// TODO: Figure this one out
// SafeCast
// TODO: Figure this one out
// Cactus Data Shield / SafeDisc
if (foundProtections.Any(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)");
if (foundProtections.Any(p => !p.StartsWith("SafeDisc")))
{
#if NET20 || NET35 || NET40 || NET452 || NET462
var tempList = new List<string>();
tempList.AddRange(foundProtections);
tempList.Add("Cactus Data Shield 300");
foundProtections = tempList;
#else
foundProtections = foundProtections.Append("Cactus Data Shield 300");
#endif
}
}
// SafeDisc
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
if (foundProtections.Exists(p => p.StartsWith("SafeDisc")))
{
// 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.
// 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.
// 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.Any(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")))
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 Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => !p.Contains("SRV Tool APP"))
.Where(p => p != "SafeDisc")
.Where(p => !p.StartsWith("Macrovision Protected Application [Version Expunged]"))
.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 => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\/4\+", RegexOptions.Compiled)))
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+")
.Where(p => p != "SafeDisc 3+ (DVD)");
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)");
}
// Next best case for SafeDisc 2+: A full SafeDisc version is found from the "SafeDisc SRV Tool APP".
else if (foundProtections.Any(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")))
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 Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => p != "SafeDisc")
.Where(p => !p.StartsWith("Macrovision Protected Application [Version Expunged]"))
.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 => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\/4\+", RegexOptions.Compiled)))
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+")
.Where(p => p != "SafeDisc 3+ (DVD)");
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)");
}
// 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.Any(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.Any(p => p == "SafeDisc 3+ (DVD)"))
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 Protection File"))
.Where(p => !p.StartsWith("Macrovision Protected Application [Version Expunged]"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [1-2]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [1-2]\.[0-9]{2}\.[0-9]{3}-[1-2]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.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+");
}
// Best case for SafeDisc 1.X: A full SafeDisc version is found that isn't part of a version range.
else if (foundProtections.Any(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))))
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 Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.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")
.Where(p => p != "SafeDisc 1")
.Where(p => p != "SafeDisc 1/Lite");
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");
}
// 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.Any(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}$")))
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 Protection File"))
.Where(p => !p.StartsWith("Macrovision Protected Application [Version Expunged]"))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1")
.Where(p => p != "SafeDisc 1/Lite");
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");
}
// 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.Any(p => p.StartsWith("Macrovision Security Driver")))
else if (foundProtections.Exists(p => p.StartsWith("Macrovision Security Driver")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !p.StartsWith("Macrovision Protected Application [Version Expunged]"))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+")
.Where(p => p != "SafeDisc 3+ (DVD)");
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.Any(p => p == "SafeDisc Lite"))
else if (foundProtections.Exists(p => p == "SafeDisc Lite"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => !(Regex.IsMatch(p, @"SafeDisc 1\.[0-9]{2}\.[0-9]{3}-1\.[0-9]{2}\.[0-9]{3}\/Lite", RegexOptions.Compiled)));
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.Any(p => p == "SafeDisc 3+ (DVD)"))
else if (foundProtections.Exists(p => p == "SafeDisc 3+ (DVD)"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 2+")
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)));
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.Any(p => p == "SafeDisc 2+"))
else if (foundProtections.Exists(p => p == "SafeDisc 2+"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}\+", RegexOptions.Compiled)));
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));
}
}
// SecuROM
// TODO: Figure this one out
if (foundProtections.Exists(p => p.StartsWith("SecuROM Release Control")))
{
foundProtections = foundProtections.FindAll(p => !p.StartsWith("SecuROM Release Control"));
foundProtections.Add("SecuROM Release Control");
}
// SolidShield
// 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,10 @@
using System;
using System.Collections.Generic;
using System.IO;
#if NET35_OR_GREATER || NETCOREAPP
using System.Linq;
#endif
using System.Text;
using System.Threading.Tasks;
using BinaryObjectScanner;
using MPF.Processors;
@@ -43,21 +46,17 @@ namespace MPF.Frontend.Tools
Drive? drive,
RedumpSystem? system,
MediaType? mediaType,
Frontend.Options options,
Options options,
BaseProcessor processor,
IProgress<ResultEventArgs>? resultProgress = null,
IProgress<ProtectionProgress>? protectionProgress = null)
{
// Ensure the current disc combination should exist
if (!system.MediaTypes().Contains(mediaType))
return null;
// Split the output path for easier use
var outputDirectory = Path.GetDirectoryName(outputPath);
string outputFilename = Path.GetFileName(outputPath);
// Check that all of the relevant files are there
List<string> missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
List<string> missingFiles = processor.FoundAllFiles(mediaType, outputDirectory, outputFilename);
if (missingFiles.Count > 0)
{
resultProgress?.Report(ResultEventArgs.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
@@ -65,38 +64,32 @@ namespace MPF.Frontend.Tools
return null;
}
// Sanitize the output filename to strip off any potential extension
outputFilename = Path.GetFileNameWithoutExtension(outputFilename);
// Create the SubmissionInfo object with all user-inputted values by default
string combinedBase;
if (string.IsNullOrEmpty(outputDirectory))
combinedBase = outputFilename;
else
combinedBase = Path.Combine(outputDirectory, outputFilename);
// Assemble a base path
string basePath = Path.GetFileNameWithoutExtension(outputFilename);
if (!string.IsNullOrEmpty(outputDirectory))
basePath = Path.Combine(outputDirectory, basePath);
// Create the default submission info
SubmissionInfo info = CreateDefaultSubmissionInfo(processor, system, mediaType, options.AddPlaceholders);
// Get specific tool output handling
processor?.GenerateSubmissionInfo(info, combinedBase, options.EnableRedumpCompatibility);
processor.GenerateSubmissionInfo(info, mediaType, basePath, options.EnableRedumpCompatibility);
if (options.IncludeArtifacts)
info.Artifacts = processor?.GenerateArtifacts(combinedBase);
info.Artifacts = processor.GenerateArtifacts(mediaType, outputDirectory, outputFilename);
// Add a placeholder for the logs link
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.LogsLink] = "[Please provide a link to your logs here]";
// 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
if (!string.IsNullOrEmpty(info.TracksAndWriteOffsets!.ClrMameProData))
_ = await FillFromRedump(options, info, resultProgress);
#endif
// If we have both ClrMamePro and Size and Checksums data, remove the ClrMamePro
if (!string.IsNullOrEmpty(info.SizeAndChecksums?.CRC32))
info.TracksAndWriteOffsets.ClrMameProData = null;
// Add the volume label to comments, if possible or necessary
string? volLabels = FormatVolumeLabels(drive?.VolumeLabel, processor?.VolumeLabels);
string? volLabels = FormatVolumeLabels(drive?.VolumeLabel, processor.VolumeLabels);
if (volLabels != null)
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.VolumeLabel] = volLabels;
@@ -104,7 +97,7 @@ namespace MPF.Frontend.Tools
ProcessMediaType(info, mediaType, options.AddPlaceholders);
// Extract info based specifically on RedumpSystem
ProcessSystem(info, system, drive, options.AddPlaceholders, processor is DiscImageCreator, combinedBase);
ProcessSystem(info, system, drive, options.AddPlaceholders, processor is DiscImageCreator, basePath);
// Run anti-modchip check, if necessary
if (drive != null && system.SupportsAntiModchipScans() && info.CopyProtection!.AntiModchip == YesNo.NULL)
@@ -119,7 +112,7 @@ namespace MPF.Frontend.Tools
{
resultProgress?.Report(ResultEventArgs.Success("Running copy protection scan... this might take a while!"));
ProtectionDictionary? protections = null;
Dictionary<string, List<string>>? protections = null;
try
{
if (options.ScanForProtection && drive?.Name != null)
@@ -159,29 +152,33 @@ 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>
public async static Task<bool> FillFromRedump(Frontend.Options options, SubmissionInfo info, IProgress<ResultEventArgs>? resultProgress = null)
public async static Task<bool> FillFromRedump(Options options,
SubmissionInfo info,
IProgress<ResultEventArgs>? resultProgress = null)
{
// If no username is provided
if (string.IsNullOrEmpty(options.RedumpUsername) || string.IsNullOrEmpty(options.RedumpPassword))
// If information should not be pulled at all
if (!options.RetrieveMatchInformation)
return false;
// Set the current dumper based on username
info.DumpersAndStatus ??= new DumpersAndStatusSection();
info.DumpersAndStatus.Dumpers = [options.RedumpUsername!];
info.DumpersAndStatus.Dumpers = [options.RedumpUsername ?? "Anonymous User"];
info.PartiallyMatchedIDs = [];
// Login to Redump
// Login to Redump, if possible
var wc = new RedumpClient();
bool? loggedIn = await wc.Login(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty);
if (loggedIn == null)
if (options.RedumpUsername != null && options.RedumpPassword != null)
{
resultProgress?.Report(ResultEventArgs.Failure("There was an unknown error connecting to Redump"));
return false;
}
else if (loggedIn == false)
{
// Don't log the as a failure or error
return false;
bool? loggedIn = await wc.Login(options.RedumpUsername, options.RedumpPassword);
if (loggedIn == null)
{
resultProgress?.Report(ResultEventArgs.Failure("There was an unknown error connecting to Redump, skipping..."));
return false;
}
else if (loggedIn == false)
{
resultProgress?.Report(ResultEventArgs.Failure("Provided Redump credentials were invalid, not using..."));
}
}
// Setup the checks
@@ -203,14 +200,34 @@ namespace MPF.Frontend.Tools
}
// If the line ends in a known extra track names, skip them for checking
if (hashData.Contains("(Track 0).bin")
// TODO: Smarter method to ignore all tracks that start with 0. 00. A. or AA.
if (hashData.Contains(".dmi")
|| hashData.Contains(".pfi")
|| hashData.Contains(".ss")
|| hashData.Contains("(Track 0).bin")
|| hashData.Contains("(Track 0.1).bin")
|| hashData.Contains("(Track 0.2).bin")
|| hashData.Contains("(Track 0.3).bin")
|| hashData.Contains("(Track 0.4).bin")
|| hashData.Contains("(Track 0.5).bin")
|| hashData.Contains("(Track 00).bin")
|| hashData.Contains("(Track 00.1).bin")
|| hashData.Contains("(Track 00.2).bin")
|| hashData.Contains("(Track 00.3).bin")
|| hashData.Contains("(Track 00.4).bin")
|| hashData.Contains("(Track 00.5).bin")
|| hashData.Contains("(Track A).bin")
|| hashData.Contains("(Track A.1).bin")
|| hashData.Contains("(Track A.2).bin")
|| hashData.Contains("(Track A.3).bin")
|| hashData.Contains("(Track A.4).bin")
|| hashData.Contains("(Track A.5).bin")
|| hashData.Contains("(Track AA).bin")
|| hashData.Contains("(Track AA.2).bin"))
|| hashData.Contains("(Track AA.1).bin")
|| hashData.Contains("(Track AA.2).bin")
|| hashData.Contains("(Track AA.3).bin")
|| hashData.Contains("(Track AA.4).bin")
|| hashData.Contains("(Track AA.5).bin"))
{
trackCount--;
resultProgress?.Report(ResultEventArgs.Success("Extra track found, skipping!"));
@@ -236,32 +253,32 @@ namespace MPF.Frontend.Tools
foundIdSets.Add(foundIds?.ToArray() ?? []);
// Ensure that all tracks are found
allFound &= (foundIds != null && foundIds.Count == 1);
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)
{
string sha1 = info.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash];
var foundIds = await Validator.ValidateUniversalHash(wc, info);
@@ -276,50 +293,44 @@ namespace MPF.Frontend.Tools
allFound = (foundIds != null && foundIds.Count == 1);
// If we found a match, then the disc is a match
if ((foundIds != null && foundIds.Count == 1) && 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;
}
@@ -402,6 +413,37 @@ namespace MPF.Frontend.Tools
return internalProgram.LongName();
}
/// <summary>
/// Simplifies a volume label into uppercase alphanumeric only string
/// </summary>
/// <param name="labels">Volume label to simplify</param>
/// <returns>Simplified volume label, null if empty or invalid</returns>
private static string? SimplifyVolumeLabel(string? label)
{
// Ignore empty labels
if (label == null || label.Length == 0)
return null;
// Take only ASCII alphanumeric characters
var labelBuilder = new StringBuilder();
foreach (char c in label)
{
if (c >= '0' && c <= '9')
labelBuilder.Append(c);
else if (c >= 'A' && c <= 'Z')
labelBuilder.Append(c);
else if (c >= 'a' && c <= 'z')
labelBuilder.Append(char.ToUpper(c));
}
// Ignore non-ASCII labels
string? simpleLabel = labelBuilder.ToString();
if (simpleLabel == null || simpleLabel.Length == 0)
return null;
return simpleLabel;
}
/// <summary>
/// Formats a list of volume labels and their corresponding filesystems
/// </summary>
@@ -409,6 +451,15 @@ namespace MPF.Frontend.Tools
/// <returns>Formatted string of volume labels and their filesystems</returns>
private static string? FormatVolumeLabels(string? driveLabel, Dictionary<string, List<string>>? labels)
{
// Treat empty label as null
if (driveLabel != null && driveLabel.Length == 0)
driveLabel = null;
// Treat "path" labels as null -- Indicates a mounted path
// This can over-match if a label contains a directory separator somehow
if (driveLabel != null && (driveLabel.Contains("/") || driveLabel.Contains("\\")))
driveLabel = null;
// Must have at least one label to format
if (driveLabel == null && (labels == null || labels.Count == 0))
return null;
@@ -423,8 +474,64 @@ namespace MPF.Frontend.Tools
return driveLabel;
}
// If only one label, don't mention fs
// Get the default label to compare against
// TODO: Full pairwise comparison of all labels, not just comparing against drive/UDF label.
string? defaultLabel = null;
if (driveLabel != null && driveLabel.Length != 0)
{
defaultLabel = SimplifyVolumeLabel(driveLabel);
}
#if NET35_OR_GREATER || NETCOREAPP
else
{
defaultLabel = labels
.Where(label => label.Value.Contains("UDF"))
.Select(label => label.Key)
.FirstOrDefault();
}
#endif
// Remove duplicate/useless volume labels
if (defaultLabel != null && defaultLabel.Length != 0)
{
List<string> keys = [.. labels.Keys];
foreach (var label in keys)
{
// Always remove "DVD_ROM" / "CD_ROM" labels
if (label == "DVD_ROM" || label == "CD_ROM")
{
labels.Remove(label);
continue;
}
// Get upper-case ASCII variant of the label
string? tempLabel = SimplifyVolumeLabel(label);
if (tempLabel == null)
continue;
// Remove duplicate volume labels
if (defaultLabel == tempLabel)
labels.Remove(label);
}
}
// If no labels are left, use drive label
if (labels == null || labels.Count == 0)
{
// Ignore common volume labels
if (FrontendTool.GetRedumpSystemFromVolumeLabel(driveLabel) != null)
return null;
return driveLabel;
}
// If only one unique label left, don't mention fs
#if NET20
string[] keyArr = [.. labels.Keys];
string firstLabel = keyArr[0];
#else
string firstLabel = labels.First().Key;
#endif
if (labels.Count == 1 && (firstLabel == driveLabel || driveLabel == null))
{
// Ignore common volume labels
@@ -442,15 +549,15 @@ namespace MPF.Frontend.Tools
volLabels.Add(driveLabel);
// Add remaining labels with their corresponding filesystems
foreach (KeyValuePair<string, List<string>> label in labels)
foreach (var kvp in labels)
{
// Ignore common volume labels
if (FrontendTool.GetRedumpSystemFromVolumeLabel(label.Key) == null)
volLabels.Add($"{label.Key} ({string.Join(", ", [.. label.Value])})");
if (FrontendTool.GetRedumpSystemFromVolumeLabel(kvp.Key) == null)
volLabels.Add($"{kvp.Key} ({string.Join(", ", [.. kvp.Value])})");
}
// Ensure that no labels are empty
volLabels = volLabels.Where(l => !string.IsNullOrEmpty(l?.Trim())).ToList();
volLabels = volLabels.FindAll(label => !string.IsNullOrEmpty(label?.Trim()));
// Print each label separated by a comma and a space
if (volLabels.Count == 0)
@@ -558,6 +665,7 @@ namespace MPF.Frontend.Tools
break;
case MediaType.NintendoWiiOpticalDisc:
case MediaType.NintendoWiiUOpticalDisc:
// If we have a single-layer disc
if (info.SizeAndChecksums!.Layerbreak == default)
@@ -828,17 +936,19 @@ namespace MPF.Frontend.Tools
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStation3Serial);
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation3Version);
SetCommentFieldIfNotExists(info, SiteCode.Patches, drive, FormatPlayStation3FirmwareVersion);
SetContentFieldIfNotExists(info, SiteCode.Patches, drive, FormatPlayStation3FirmwareVersion);
break;
case RedumpSystem.SonyPlayStation4:
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStation4Serial);
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation4Version);
SetContentFieldIfNotExists(info, SiteCode.Games, drive, PhysicalTool.GetPlayStation4PkgInfo);
break;
case RedumpSystem.SonyPlayStation5:
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStation5Serial);
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation5Version);
SetContentFieldIfNotExists(info, SiteCode.Games, drive, PhysicalTool.GetPlayStation5PkgInfo);
break;
case RedumpSystem.TomyKissSite:
@@ -888,14 +998,50 @@ namespace MPF.Frontend.Tools
/// <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)
private static void SetCommentFieldIfNotExists(SubmissionInfo info, SiteCode key, Drive? drive, System.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;
string? value = valueFunc(drive);
if (value != null)
info.CommonDiscInfo!.CommentsSpecialFields![key] = value;
}
/// <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
string? value = valueFunc(drive);
if (value != null)
info.CommonDiscInfo!.ContentsSpecialFields![key] = value;
}
/// <summary>
@@ -916,7 +1062,7 @@ namespace MPF.Frontend.Tools
/// </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)
private static Dictionary<string, List<string>?> ReformatProtectionDictionary(Dictionary<string, List<string>>? oldDict)
{
// Null or empty protections return empty
if (oldDict == null || oldDict.Count == 0)

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;
@@ -21,11 +20,11 @@ namespace MPF.Frontend.ViewModels
/// <summary>
/// Access to the current options
/// </summary>
public Frontend.Options Options
public Options Options
{
get => _options;
}
private readonly Frontend.Options _options;
private readonly Options _options;
/// <summary>
/// Indicates if SelectionChanged events can be executed
@@ -67,34 +66,6 @@ namespace MPF.Frontend.ViewModels
}
private bool _systemTypeComboBoxEnabled;
/// <summary>
/// Currently selected media type value
/// </summary>
public MediaType? CurrentMediaType
{
get => _currentMediaType;
set
{
_currentMediaType = value;
TriggerPropertyChanged(nameof(CurrentMediaType));
}
}
private MediaType? _currentMediaType;
/// <summary>
/// Indicates the status of the media type combo box
/// </summary>
public bool MediaTypeComboBoxEnabled
{
get => _mediaTypeComboBoxEnabled;
set
{
_mediaTypeComboBoxEnabled = value;
TriggerPropertyChanged(nameof(MediaTypeComboBoxEnabled));
}
}
private bool _mediaTypeComboBoxEnabled;
/// <summary>
/// Currently provided input path
/// </summary>
@@ -165,6 +136,39 @@ namespace MPF.Frontend.ViewModels
}
private bool _dumpingProgramComboBoxEnabled;
/// <summary>
/// Currently displayed status
/// </summary>
public string Status
{
get => _status;
set
{
_status = value;
TriggerPropertyChanged(nameof(Status));
TriggerPropertyChanged(nameof(StatusFirstLine));
}
}
private string _status;
/// <summary>
/// Currently displayed status trimmed to one line
/// </summary>
public string StatusFirstLine
{
get
{
if (string.IsNullOrEmpty(Status))
return string.Empty;
var statusLines = Status.Split('\n');
if (statusLines.Length > 1)
return statusLines[0] + " (...)";
return statusLines[0];
}
}
/// <summary>
/// Indicates the status of the check dump button
/// </summary>
@@ -197,20 +201,6 @@ namespace MPF.Frontend.ViewModels
#region List Properties
/// <summary>
/// Current list of supported media types
/// </summary>
public List<Element<MediaType>>? MediaTypes
{
get => _mediaTypes;
set
{
_mediaTypes = value;
TriggerPropertyChanged(nameof(MediaTypes));
}
}
private List<Element<MediaType>>? _mediaTypes;
/// <summary>
/// Current list of supported system profiles
/// </summary>
@@ -250,20 +240,18 @@ namespace MPF.Frontend.ViewModels
_internalPrograms = [];
_inputPath = string.Empty;
_systems = [];
_status = string.Empty;
SystemTypeComboBoxEnabled = true;
InputPathTextBoxEnabled = true;
InputPathBrowseButtonEnabled = true;
MediaTypeComboBoxEnabled = true;
DumpingProgramComboBoxEnabled = true;
CheckDumpButtonEnabled = false;
CancelButtonEnabled = true;
MediaTypes = [];
Systems = RedumpSystemComboBoxItem.GenerateElements().ToList();
Systems = RedumpSystemComboBoxItem.GenerateElements();
InternalPrograms = [];
PopulateMediaType();
PopulateInternalPrograms();
EnableEventHandlers();
}
@@ -295,16 +283,7 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public void ChangeSystem()
{
PopulateMediaType();
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
/// Change the currently selected media type
/// </summary>
public void ChangeMediaType()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
@@ -312,7 +291,7 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public void ChangeDumpingProgram()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
@@ -320,7 +299,7 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public void ChangeInputPath()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
#endregion
@@ -336,7 +315,6 @@ namespace MPF.Frontend.ViewModels
InputPathTextBoxEnabled = true;
InputPathBrowseButtonEnabled = true;
DumpingProgramComboBoxEnabled = true;
PopulateMediaType();
CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
CancelButtonEnabled = true;
}
@@ -349,7 +327,6 @@ namespace MPF.Frontend.ViewModels
SystemTypeComboBoxEnabled = false;
InputPathTextBoxEnabled = false;
InputPathBrowseButtonEnabled = false;
MediaTypeComboBoxEnabled = false;
DumpingProgramComboBoxEnabled = false;
CheckDumpButtonEnabled = false;
CancelButtonEnabled = false;
@@ -359,35 +336,6 @@ namespace MPF.Frontend.ViewModels
#region Population
/// <summary>
/// Populate media type according to system type
/// </summary>
private void PopulateMediaType()
{
// Disable other UI updates
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
if (this.CurrentSystem != null)
{
var mediaTypeValues = this.CurrentSystem.MediaTypes();
int index = mediaTypeValues.FindIndex(m => m == this.CurrentMediaType);
MediaTypes = mediaTypeValues.Select(m => new Element<MediaType>(m ?? MediaType.NONE)).ToList();
this.MediaTypeComboBoxEnabled = MediaTypes.Count > 1;
this.CurrentMediaType = (index > -1 ? MediaTypes[index] : MediaTypes[0]);
}
else
{
this.MediaTypeComboBoxEnabled = false;
this.MediaTypes = null;
this.CurrentMediaType = null;
}
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
/// <summary>
/// Populate media type according to system type
/// </summary>
@@ -398,15 +346,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 +366,7 @@ namespace MPF.Frontend.ViewModels
private bool ShouldEnableCheckDumpButton()
{
return this.CurrentSystem != null && this.CurrentMediaType != null && !string.IsNullOrEmpty(this.InputPath);
return CurrentSystem != null && !string.IsNullOrEmpty(InputPath);
}
/// <summary>
@@ -450,7 +398,7 @@ namespace MPF.Frontend.ViewModels
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
@@ -458,15 +406,25 @@ namespace MPF.Frontend.ViewModels
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
// Populate an environment
var env = new DumpEnvironment(Options, Path.GetFullPath(this.InputPath.Trim('"')), null, this.CurrentSystem, this.CurrentMediaType, this.CurrentProgram, parameters: null);
// Make new Progress objects
// Get progress indicators
var resultProgress = new Progress<ResultEventArgs>();
resultProgress.ProgressChanged += ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ProgressUpdated;
// Populate an environment
var env = new DumpEnvironment(Options,
Path.GetFullPath(InputPath.Trim('"')),
null,
CurrentSystem,
CurrentProgram);
env.SetProcessor();
// Finally, attempt to do the output dance
var result = await env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress, processUserInfo);
var result = await env.VerifyAndSaveDumpOutput(
resultProgress: resultProgress,
protectionProgress: protectionProgress,
processUserInfo: processUserInfo);
// Reenable UI and event handlers, if necessary
EnableUIElements();
@@ -476,6 +434,23 @@ namespace MPF.Frontend.ViewModels
return result.Message;
}
/// <summary>
/// Handler for Result ProgressChanged event
/// </summary>
private void ProgressUpdated(object? sender, ResultEventArgs value)
{
Status = value?.Message ?? string.Empty;
}
/// <summary>
/// Handler for ProtectionProgress ProgressChanged event
/// </summary>
private void ProgressUpdated(object? sender, ProtectionProgress value)
{
string message = $"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}";
Status = message;
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,12 @@
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;
namespace MPF.Frontend.ViewModels
{
public class DiscInformationViewModel
public class MediaInformationViewModel
{
#region Fields
@@ -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,14 +186,15 @@ 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
/// <summary>
/// Constructor
/// </summary>
public DiscInformationViewModel(Options options, SubmissionInfo? submissionInfo)
public MediaInformationViewModel(Options options, SubmissionInfo? submissionInfo)
{
Options = options;
SubmissionInfo = submissionInfo?.Clone() as SubmissionInfo ?? new SubmissionInfo();
@@ -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

@@ -1,9 +1,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using MPF.Frontend.ComboBoxItems;
using SabreTools.RedumpLib.Web;
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
@@ -62,10 +60,15 @@ namespace MPF.Frontend.ViewModels
/// </summary>
public static List<Element<RedumperSectorOrder>> RedumperSectorOrders => PopulateRedumperSectorOrders();
/// <summary>
/// Current list of supported Redumper drive types
/// </summary>
public static List<Element<RedumperDriveType>> RedumperDriveTypes => PopulateRedumperDriveTypes();
/// <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 +96,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>
@@ -101,8 +104,8 @@ namespace MPF.Frontend.ViewModels
/// </summary>
private static List<Element<RedumperReadMethod>> PopulateRedumperReadMethods()
{
var readMethods = new List<RedumperReadMethod> { RedumperReadMethod.NONE, RedumperReadMethod.D8, RedumperReadMethod.BE, RedumperReadMethod.BE_CDDA };
return readMethods.Select(rm => new Element<RedumperReadMethod>(rm)).ToList();
var readMethods = new List<RedumperReadMethod> { RedumperReadMethod.NONE, RedumperReadMethod.D8, RedumperReadMethod.BE };
return readMethods.ConvertAll(rm => new Element<RedumperReadMethod>(rm));
}
/// <summary>
@@ -111,7 +114,16 @@ 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));
}
/// <summary>
/// Get a complete list of supported redumper drive types
/// </summary>
private static List<Element<RedumperDriveType>> PopulateRedumperDriveTypes()
{
var driveTypes = new List<RedumperDriveType> { RedumperDriveType.NONE, RedumperDriveType.GENERIC, RedumperDriveType.PLEXTOR, RedumperDriveType.LG_ASU8A, RedumperDriveType.LG_ASU8B, RedumperDriveType.LG_ASU8C, RedumperDriveType.LG_ASU3, RedumperDriveType.LG_ASU2 };
return driveTypes.ConvertAll(dt => new Element<RedumperDriveType>(dt));
}
#endregion
@@ -138,7 +150,7 @@ namespace MPF.Frontend.ViewModels
{
Options.RedumperReadMethod = RedumperReadMethod.NONE;
Options.RedumperSectorOrder = RedumperSectorOrder.NONE;
Options.RedumperUseGenericDriveType = false;
Options.RedumperDriveType = RedumperDriveType.NONE;
TriggerPropertyChanged(nameof(Options));
}

View File

@@ -0,0 +1,568 @@
using System;
using System.IO;
using SabreTools.RedumpLib.Data;
using Schemas;
using Xunit;
#pragma warning disable CS0618 // Ignore "Type or member is obsolete"
namespace MPF.Processors.Test
{
// TODO: Add tests around remaining helper methods
public class AaruTests
{
// TODO: Create minimal sidecars for all other supported types
#region DetermineMediaType
[Fact]
public void DetermineMediaType_Empty_Null()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Null(actual);
}
[Fact]
public void DetermineMediaType_Invalid_Null()
{
string? outputDirectory = null;
string outputFilename = "INVALID";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Null(actual);
}
[Fact]
public void DetermineMediaType_CD_Valid_CD()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "Aaru", "CDROM");
string outputFilename = "test";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Equal(MediaType.CDROM, actual);
}
#endregion
#region GetOutputFiles
[Fact]
public void GetOutputFiles_Null_Empty()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GetOutputFiles(null, outputDirectory, outputFilename);
Assert.Empty(actual);
}
[Fact]
public void GetOutputFiles_CDROM_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GetOutputFiles(MediaType.CDROM, outputDirectory, outputFilename);
Assert.Equal(8, actual.Count);
}
[Fact]
public void GetOutputFiles_DVD_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GetOutputFiles(MediaType.DVD, outputDirectory, outputFilename);
Assert.Equal(7, actual.Count);
}
[Fact]
public void GetOutputFiles_HDDVD_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GetOutputFiles(MediaType.HDDVD, outputDirectory, outputFilename);
Assert.Equal(7, actual.Count);
}
[Fact]
public void GetOutputFiles_BluRay_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GetOutputFiles(MediaType.BluRay, outputDirectory, outputFilename);
Assert.Equal(7, actual.Count);
}
[Fact]
public void GetOutputFiles_Other_Empty()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GetOutputFiles(MediaType.ApertureCard, outputDirectory, outputFilename);
Assert.Empty(actual);
}
#endregion
#region FoundAllFiles
[Fact]
public void FoundAllFiles_Invalid_Filled()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.FoundAllFiles(MediaType.CDROM, outputDirectory, outputFilename);
Assert.Equal(7, actual.Count);
}
[Fact]
public void FoundAllFiles_Valid_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "Aaru", "CDROM");
string outputFilename = "test.aaruf";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.FoundAllFiles(MediaType.CDROM, outputDirectory, outputFilename);
Assert.Empty(actual);
}
#endregion
#region FoundAnyFiles
[Fact]
public void FoundAnyFiles_Invalid_Filled()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.FoundAnyFiles(MediaType.CDROM, outputDirectory, outputFilename);
Assert.False(actual);
}
[Fact]
public void FoundAnyFiles_Valid_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "Aaru", "CDROM");
string outputFilename = "test.aaruf";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.FoundAnyFiles(MediaType.CDROM, outputDirectory, outputFilename);
Assert.True(actual);
}
[Fact]
public void FoundAnyFiles_ValidZip_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "Aaru", "CDROM-zip");
string outputFilename = "test.aaruf";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.FoundAnyFiles(MediaType.CDROM, outputDirectory, outputFilename);
Assert.True(actual);
}
#endregion
#region GenerateArtifacts
[Fact]
public void GenerateArtifacts_Invalid_Empty()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GenerateArtifacts(MediaType.CDROM, outputDirectory, outputFilename);
Assert.Empty(actual);
}
[Fact]
public void GenerateArtifacts_Valid_Filled()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "Aaru", "CDROM");
string outputFilename = "test.aaruf";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GenerateArtifacts(MediaType.CDROM, outputDirectory, outputFilename);
Assert.Equal(7, actual.Count);
}
#endregion
#region GetDeleteableFilePaths
[Fact]
public void GetDeleteableFilePaths_Invalid_Empty()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GetDeleteableFilePaths(MediaType.CDROM, outputDirectory, outputFilename);
Assert.Empty(actual);
}
[Fact]
public void GetDeleteableFilePaths_Valid_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "Aaru", "CDROM");
string outputFilename = "test.aaruf";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GetDeleteableFilePaths(MediaType.CDROM, outputDirectory, outputFilename);
Assert.Empty(actual);
}
#endregion
#region GetZippableFilePaths
[Fact]
public void GetZippableFilePaths_Invalid_Empty()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GetZippableFilePaths(MediaType.CDROM, outputDirectory, outputFilename);
Assert.Empty(actual);
}
[Fact]
public void GetZippableFilePaths_Valid_Filled()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "Aaru", "CDROM");
string outputFilename = "test.aaruf";
var processor = new Aaru(RedumpSystem.IBMPCcompatible);
var actual = processor.GetZippableFilePaths(MediaType.CDROM, outputDirectory, outputFilename);
Assert.Equal(7, actual.Count);
}
#endregion
#region GenerateCuesheet
[Fact]
public void GenerateCuesheet_Null_Null()
{
CICMMetadataType? cicmSidecar = null;
string basePath = "test";
string? actual = Aaru.GenerateCuesheet(cicmSidecar, basePath);
Assert.Null(actual);
}
[Fact]
public void GenerateCuesheet_Empty_Null()
{
CICMMetadataType? cicmSidecar = new CICMMetadataType();
string basePath = "test";
string? actual = Aaru.GenerateCuesheet(cicmSidecar, basePath);
Assert.Null(actual);
}
[Fact]
public void GenerateCuesheet_Valid_Filled()
{
TrackType trackType = new TrackType
{
BytesPerSector = 2352,
Flags = new TrackFlagsType { Quadraphonic = true },
Indexes = [new TrackIndexType { index = 1, Value = 0 }],
ISRC = "isrc",
Sequence = new TrackSequenceType { TrackNumber = 1 },
TrackType1 = TrackTypeTrackType.mode1,
};
OpticalDiscType opticalDiscType = new OpticalDiscType
{
DiscType = "CD-ROM",
MediaCatalogueNumber = "mcn",
Track = [trackType],
Tracks = [1],
};
CICMMetadataType? cicmSidecar = new CICMMetadataType
{
OpticalDisc = [opticalDiscType],
Performer = ["performer"],
};
string basePath = "test";
string? actual = Aaru.GenerateCuesheet(cicmSidecar, basePath);
// TODO: Unexpected outcome -- Non-null but empty cuesheet generated
// TODO: Add structure validation
Assert.NotNull(actual);
}
#endregion
#region GenerateDatafile
[Fact]
public void GenerateDatafile_Null_Null()
{
CICMMetadataType? cicmSidecar = null;
string basePath = "test";
var actual = Aaru.GenerateDatafile(cicmSidecar, basePath);
Assert.Null(actual);
}
[Fact]
public void GenerateDatafile_Empty_Null()
{
CICMMetadataType? cicmSidecar = new CICMMetadataType();
string basePath = "test";
var actual = Aaru.GenerateDatafile(cicmSidecar, basePath);
Assert.Null(actual);
}
[Fact]
public void GenerateDatafile_Valid_Filled()
{
TrackType trackType = new TrackType
{
Checksums =
[
new ChecksumType { type = ChecksumTypeType.crc32, Value = "00000000" },
new ChecksumType { type = ChecksumTypeType.md5, Value = "d41d8cd98f00b204e9800998ecf8427e" },
new ChecksumType { type = ChecksumTypeType.sha1, Value = "da39a3ee5e6b4b0d3255bfef95601890afd80709" },
],
Sequence = new TrackSequenceType { TrackNumber = 1 },
Size = 12345,
};
OpticalDiscType opticalDiscType = new OpticalDiscType
{
DiscType = "CD-ROM",
MediaCatalogueNumber = "mcn",
Track = [trackType],
Tracks = [1],
};
CICMMetadataType? cicmSidecar = new CICMMetadataType
{
OpticalDisc = [opticalDiscType],
};
string basePath = "test";
var actual = Aaru.GenerateDatafile(cicmSidecar, basePath);
// TODO: Add structure validation
Assert.NotNull(actual);
}
#endregion
#region GeneratePVD
[Fact]
public void GeneratePVD_Null_Null()
{
CICMMetadataType? cicmSidecar = null;
var actual = Aaru.GeneratePVD(cicmSidecar);
Assert.Null(actual);
}
[Fact]
public void GeneratePVD_Empty_Null()
{
CICMMetadataType? cicmSidecar = new CICMMetadataType();
var actual = Aaru.GeneratePVD(cicmSidecar);
Assert.Null(actual);
}
[Fact]
public void GeneratePVD_Valid_Filled()
{
FileSystemType fileSystemType = new FileSystemType
{
CreationDate = DateTime.UtcNow,
CreationDateSpecified = true,
ModificationDate = DateTime.UtcNow,
ModificationDateSpecified = true,
ExpirationDate = DateTime.UtcNow,
ExpirationDateSpecified = true,
EffectiveDate = DateTime.UtcNow,
EffectiveDateSpecified = true,
};
PartitionType partitionType = new PartitionType
{
FileSystems = [fileSystemType],
};
TrackType trackType = new TrackType
{
FileSystemInformation = [partitionType],
};
OpticalDiscType opticalDiscType = new OpticalDiscType
{
Track = [trackType],
};
CICMMetadataType? cicmSidecar = new CICMMetadataType
{
OpticalDisc = [opticalDiscType],
};
string? actual = Aaru.GeneratePVD(cicmSidecar);
// TODO: Add structure validation
Assert.NotNull(actual);
}
#endregion
#region GetDiscTypeFromStrings
[Theory]
[InlineData(null, "ANY", null)]
[InlineData("", "ANY", null)]
[InlineData("INVALID", "ANY", null)]
[InlineData("3\" floppy", "ANY", MediaType.FloppyDisk)]
[InlineData("3.5\" floppy", "ANY", MediaType.FloppyDisk)]
[InlineData("3.5\" magneto-optical", "ANY", MediaType.Floptical)]
[InlineData("3.5\" SyQuest cartridge", "ANY", null)]
[InlineData("3.9\" SyQuest cartridge", "ANY", null)]
[InlineData("5.25\" floppy", "ANY", MediaType.FloppyDisk)]
[InlineData("5.25\" magneto-optical", "ANY", MediaType.Floptical)]
[InlineData("5.25\" SyQuest cartridge", "ANY", null)]
[InlineData("8\" floppy", "ANY", MediaType.FloppyDisk)]
[InlineData("300mm magneto optical", "ANY", MediaType.Floptical)]
[InlineData("356mm magneto-optical", "ANY", MediaType.Floptical)]
[InlineData("Advanced Digital Recording", "ANY", null)]
[InlineData("Advanced Intelligent Tape", "ANY", null)]
[InlineData("Archival Disc", "ANY", null)]
[InlineData("BeeCard", "ANY", null)]
[InlineData("Blu-ray", "Wii U Optical Disc", MediaType.NintendoWiiUOpticalDisc)]
[InlineData("Blu-ray", "ANY", MediaType.BluRay)]
[InlineData("Borsu", "ANY", null)]
[InlineData("Compact Cassette", "ANY", MediaType.Cassette)]
[InlineData("Compact Disc", "ANY", MediaType.CDROM)]
[InlineData("Compact Flash", "ANY", MediaType.CompactFlash)]
[InlineData("CompacTape", "ANY", null)]
[InlineData("CRVdisc", "ANY", null)]
[InlineData("Data8", "ANY", null)]
[InlineData("DataPlay", "ANY", null)]
[InlineData("DataStore", "ANY", null)]
[InlineData("DDCD", "ANY", MediaType.CDROM)]
[InlineData("DECtape", "ANY", null)]
[InlineData("DemiDiskette", "ANY", null)]
[InlineData("Digital Audio Tape", "ANY", null)]
[InlineData("Digital Data Storage", "ANY", MediaType.DataCartridge)]
[InlineData("Digital Linear Tape", "ANY", null)]
[InlineData("DIR", "ANY", null)]
[InlineData("DST", "ANY", null)]
[InlineData("DTF", "ANY", null)]
[InlineData("DTF2", "ANY", null)]
[InlineData("DV tape", "ANY", null)]
[InlineData("DVD", "GameCube Game Disc", MediaType.NintendoGameCubeGameDisc)]
[InlineData("DVD", "Wii Optical Disc", MediaType.NintendoWiiOpticalDisc)]
[InlineData("DVD", "ANY", MediaType.DVD)]
[InlineData("EVD", "ANY", null)]
[InlineData("Exatape", "ANY", null)]
[InlineData("Express Card", "ANY", null)]
[InlineData("FDDVD", "ANY", null)]
[InlineData("Flextra", "ANY", null)]
[InlineData("Floptical", "ANY", MediaType.Floptical)]
[InlineData("FVD", "ANY", null)]
[InlineData("GD", "ANY", MediaType.GDROM)]
[InlineData("Hard Disk Drive", "ANY", MediaType.HardDisk)]
[InlineData("HD DVD", "ANY", MediaType.HDDVD)]
[InlineData("HD VMD", "ANY", null)]
[InlineData("HiFD", "ANY", MediaType.FloppyDisk)]
[InlineData("HiTC", "ANY", null)]
[InlineData("HuCard", "ANY", MediaType.Cartridge)]
[InlineData("HVD", "ANY", null)]
[InlineData("HyperFlex", "ANY", null)]
[InlineData("IBM 3470", "ANY", null)]
[InlineData("IBM 3480", "ANY", null)]
[InlineData("IBM 3490", "ANY", null)]
[InlineData("IBM 3490E", "ANY", null)]
[InlineData("IBM 3592", "ANY", null)]
[InlineData("Iomega Bernoulli Box", "ANY", MediaType.IomegaBernoulliDisk)]
[InlineData("Iomega Bernoulli Box II", "ANY", MediaType.IomegaBernoulliDisk)]
[InlineData("Iomega Ditto", "ANY", null)]
[InlineData("Iomega Jaz", "ANY", MediaType.IomegaJaz)]
[InlineData("Iomega PocketZip", "ANY", MediaType.IomegaZip)]
[InlineData("Iomega REV", "ANY", null)]
[InlineData("Iomega ZIP", "ANY", MediaType.IomegaZip)]
[InlineData("Kodak Verbatim", "ANY", null)]
[InlineData("LaserDisc", "ANY", MediaType.LaserDisc)]
[InlineData("Linear Tape-Open", "ANY", null)]
[InlineData("LT1", "ANY", null)]
[InlineData("Magneto-optical", "ANY", MediaType.Floptical)]
[InlineData("Memory Stick", "ANY", MediaType.SDCard)]
[InlineData("MiniCard", "ANY", null)]
[InlineData("MiniDisc", "ANY", null)]
[InlineData("MultiMediaCard", "ANY", MediaType.SDCard)]
[InlineData("Nintendo 3DS Game Card", "ANY", MediaType.Cartridge)]
[InlineData("Nintendo 64 Disk", "ANY", MediaType.Nintendo64DD)]
[InlineData("Nintendo 64 Game Pak", "ANY", MediaType.Cartridge)]
[InlineData("Nintendo Disk Card", "ANY", MediaType.NintendoFamicomDiskSystem)]
[InlineData("Nintendo DS Game Card", "ANY", MediaType.Cartridge)]
[InlineData("Nintendo DSi Game Card", "ANY", MediaType.Cartridge)]
[InlineData("Nintendo Entertainment System Game Pak", "ANY", MediaType.Cartridge)]
[InlineData("Nintendo Famicom Game Pak", "ANY", MediaType.Cartridge)]
[InlineData("Nintendo Game Boy Advance Game Pak", "ANY", MediaType.Cartridge)]
[InlineData("Nintendo Game Boy Game Pak", "ANY", MediaType.Cartridge)]
[InlineData("Nintendo Switch Game Card", "ANY", MediaType.Cartridge)]
[InlineData("Optical Disc Archive", "ANY", null)]
[InlineData("Orb", "ANY", null)]
[InlineData("PCMCIA Card", "ANY", null)]
[InlineData("PD650", "ANY", null)]
[InlineData("PlayStation Memory Card", "ANY", null)]
[InlineData("Quarter-inch cartridge", "ANY", MediaType.DataCartridge)]
[InlineData("Quarter-inch mini cartridge", "ANY", MediaType.DataCartridge)]
[InlineData("QuickDisk", "ANY", null)]
[InlineData("RDX", "ANY", null)]
[InlineData("SACD", "ANY", MediaType.DVD)]
[InlineData("Scalable Linear Recording", "ANY", null)]
[InlineData("Secure Digital", "ANY", MediaType.SDCard)]
[InlineData("SmartMedia", "ANY", MediaType.SDCard)]
[InlineData("Sony Professional Disc", "ANY", null)]
[InlineData("Sony Professional Disc for DATA", "ANY", null)]
[InlineData("STK 4480", "ANY", null)]
[InlineData("STK 4490", "ANY", null)]
[InlineData("STK 9490", "ANY", null)]
[InlineData("STK T-9840", "ANY", null)]
[InlineData("STK T-9940", "ANY", null)]
[InlineData("STK T-10000", "ANY", null)]
[InlineData("Super Advanced Intelligent Tape", "ANY", null)]
[InlineData("Super Digital Linear Tape", "ANY", null)]
[InlineData("Super Nintendo Game Pak", "ANY", MediaType.Cartridge)]
[InlineData("Super Nintendo Game Pak (US)", "ANY", MediaType.Cartridge)]
[InlineData("SuperDisk", "ANY", MediaType.FloppyDisk)]
[InlineData("SVOD", "ANY", null)]
[InlineData("Travan", "ANY", null)]
[InlineData("UDO", "ANY", null)]
[InlineData("UHD144", "ANY", null)]
[InlineData("UMD", "ANY", MediaType.UMD)]
[InlineData("Unknown", "ANY", null)]
[InlineData("USB flash drive", "ANY", MediaType.FlashDrive)]
[InlineData("VCDHD", "ANY", null)]
[InlineData("VideoFloppy", "ANY", null)]
[InlineData("VideoNow", "ANY", MediaType.CDROM)]
[InlineData("VXA", "ANY", MediaType.FlashDrive)]
[InlineData("Wafer", "ANY", null)]
[InlineData("xD", "ANY", null)]
[InlineData("XQD", "ANY", null)]
[InlineData("Zoned Hard Disk Drive", "ANY", MediaType.HardDisk)]
[InlineData("ZX Microdrive", "ANY", null)]
public void GetDiscTypeFromStringsTest(string? discType, string? discSubType, MediaType? expected)
{
var actual = Aaru.GetDiscTypeFromStrings(discType, discSubType);
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -0,0 +1,313 @@
using System;
using System.IO;
using SabreTools.Models.Logiqx;
using Xunit;
namespace MPF.Processors.Test
{
public class BaseProcessorTests
{
#region GetGeneratedFilenames
[Fact]
public void GetGeneratedFilenames_NullSuffix_Standard()
{
string? filenameSuffix = null;
var actual = BaseProcessor.GetGeneratedFilenames(filenameSuffix);
Assert.Equal(4, actual.Count);
Assert.Equal("!protectionInfo.txt", actual[0]);
Assert.Equal("!submissionInfo.json", actual[1]);
Assert.Equal("!submissionInfo.json.gz", actual[2]);
Assert.Equal("!submissionInfo.txt", actual[3]);
}
[Fact]
public void GetGeneratedFilenames_EmptySuffix_Standard()
{
string? filenameSuffix = string.Empty;
var actual = BaseProcessor.GetGeneratedFilenames(filenameSuffix);
Assert.Equal(4, actual.Count);
Assert.Equal("!protectionInfo.txt", actual[0]);
Assert.Equal("!submissionInfo.json", actual[1]);
Assert.Equal("!submissionInfo.json.gz", actual[2]);
Assert.Equal("!submissionInfo.txt", actual[3]);
}
[Fact]
public void GetGeneratedFilenames_ValidSuffix_Modified()
{
string? filenameSuffix = "suffix";
var actual = BaseProcessor.GetGeneratedFilenames(filenameSuffix);
Assert.Equal(4, actual.Count);
Assert.Equal("!protectionInfo_suffix.txt", actual[0]);
Assert.Equal("!submissionInfo_suffix.json", actual[1]);
Assert.Equal("!submissionInfo_suffix.json.gz", actual[2]);
Assert.Equal("!submissionInfo_suffix.txt", actual[3]);
}
#endregion
#region GetGeneratedFilePaths
[Fact]
public void GetGeneratedFilePaths_NulloutputDirectory_Empty()
{
string? outputDirectory = null;
var actual = BaseProcessor.GetGeneratedFilePaths(outputDirectory, filenameSuffix: null);
Assert.Empty(actual);
}
[Fact]
public void GetGeneratedFilePaths_EmptyoutputDirectory_Empty()
{
string? outputDirectory = string.Empty;
var actual = BaseProcessor.GetGeneratedFilePaths(outputDirectory, filenameSuffix: null);
Assert.Empty(actual);
}
[Fact]
public void GetGeneratedFilePaths_InvalidoutputDirectory_Empty()
{
string? outputDirectory = "INVALID";
var actual = BaseProcessor.GetGeneratedFilePaths(outputDirectory, filenameSuffix: null);
Assert.Empty(actual);
}
[Fact]
public void GetGeneratedFilePaths_ValidoutputDirectory_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "BaseProcessor");
var actual = BaseProcessor.GetGeneratedFilePaths(outputDirectory, filenameSuffix: null);
Assert.Equal(4, actual.Count);
}
#endregion
#region GenerateDatafile
[Fact]
public void GenerateDatafile_Empty_Null()
{
string iso = string.Empty;
Datafile? actual = BaseProcessor.GenerateDatafile(iso);
Assert.Null(actual);
}
[Fact]
public void GenerateDatafile_Invalid_Null()
{
string iso = "INVALID";
Datafile? actual = BaseProcessor.GenerateDatafile(iso);
Assert.Null(actual);
}
[Fact]
public void GenerateDatafile_Valid_Filled()
{
string iso = Path.Combine(Environment.CurrentDirectory, "TestData", "PS3CFW", "BluRay", "test.iso");
var actual = BaseProcessor.GenerateDatafile(iso);
Assert.NotNull(actual);
Assert.NotNull(actual.Game);
var game = Assert.Single(actual.Game);
Assert.NotNull(game.Rom);
var rom = Assert.Single(game.Rom);
Assert.Equal("9", rom.Size);
Assert.Equal("560b9f59", rom.CRC);
Assert.Equal("edbb6676247e65c2245dd4883ed9fc24", rom.MD5);
Assert.Equal("1b33ad54d78085be5ecb1cf1b3e9da821e708075", rom.SHA1);
}
#endregion
#region GetPIC
[Fact]
public void GetPIC_InvalidPath_Null()
{
string picPath = "INVALID";
int trimLength = -1;
string? actual = BaseProcessor.GetPIC(picPath, trimLength);
Assert.Null(actual);
}
[Fact]
public void GetPIC_ValidPathZeroTrim_Empty()
{
string picPath = Path.Combine(Environment.CurrentDirectory, "TestData", "BaseProcessor", "pic.bin");
int trimLength = 0;
string? actual = BaseProcessor.GetPIC(picPath, trimLength);
Assert.NotNull(actual);
Assert.Empty(actual);
}
[Fact]
public void GetPIC_ValidPathDefaultTrim_Formatted()
{
string expected = "000102030405060708090A0B0C0D0E0F\n000102030405060708090A0B0C0D0E0F\n000102030405060708090A0B0C0D0E0F\n000102030405060708090A0B0C0D0E0F\n000102030405060708090A0B0C0D0E0F\n000102030405060708090A0B0C0D0E0F\n000102030405060708090A0B0C0D0E0F\n000102030405060708090A0B0C0D0E0F\n000102030405060708090A0B0C0D0E0F\n000102030405060708090A0B0C0D0E0F\n";
string picPath = Path.Combine(Environment.CurrentDirectory, "TestData", "BaseProcessor", "pic.bin");
int trimLength = -1;
string? actual = BaseProcessor.GetPIC(picPath, trimLength);
Assert.NotNull(actual);
Assert.Equal(expected, actual);
}
[Fact]
public void GetPIC_ValidPathCustomTrim_Formatted()
{
string expected = "000102030405060708090A0B0C0D0E0F\n";
string picPath = Path.Combine(Environment.CurrentDirectory, "TestData", "BaseProcessor", "pic.bin");
int trimLength = 32;
string? actual = BaseProcessor.GetPIC(picPath, trimLength);
Assert.NotNull(actual);
Assert.Equal(expected, actual);
}
#endregion
#region GetPVD
// TODO: Create fake, 00-filled ISO for tests and implement
#endregion
#region IsAudio
[Fact]
public void IsAudio_Null_False()
{
string? cue = null;
bool actual = BaseProcessor.IsAudio(cue);
Assert.False(actual);
}
[Fact]
public void IsAudio_Empty_False()
{
string? cue = string.Empty;
bool actual = BaseProcessor.IsAudio(cue);
Assert.False(actual);
}
[Fact]
public void IsAudio_Invalid_False()
{
string? cue = @"INVALID";
bool actual = BaseProcessor.IsAudio(cue);
Assert.False(actual);
}
[Fact]
public void IsAudio_NoAudio_False()
{
string? cue = @"FILE ""track (Track 1).bin"" BINARY
TRACK 01 MODE1/2352
INDEX 01 00:00:00
FILE ""track (Track 2).bin"" BINARY
TRACK 02 MODE1/2352
INDEX 01 00:00:00";
bool actual = BaseProcessor.IsAudio(cue);
Assert.False(actual);
}
[Fact]
public void IsAudio_MixedTracks_False()
{
string? cue = @"FILE ""track (Track 1).bin"" BINARY
TRACK 01 MODE1/2352
INDEX 01 00:00:00
FILE ""track (Track 2).bin"" BINARY
TRACK 02 AUDIO
INDEX 00 00:00:00
INDEX 01 00:02:00";
bool actual = BaseProcessor.IsAudio(cue);
Assert.False(actual);
}
[Fact]
public void IsAudio_AllAudio_True()
{
string? cue = @"FILE ""track (Track 1).bin"" BINARY
TRACK 01 AUDIO
INDEX 00 00:00:00
INDEX 01 00:02:00
FILE ""track (Track 2).bin"" BINARY
TRACK 02 AUDIO
INDEX 00 00:00:00
INDEX 01 00:02:00";
bool actual = BaseProcessor.IsAudio(cue);
Assert.True(actual);
}
#endregion
#region SplitString
[Fact]
public void SplitString_NullString_Empty()
{
string? str = null;
int count = 0;
bool trim = false;
string actual = BaseProcessor.SplitString(str, count, trim);
Assert.Empty(actual);
}
[Fact]
public void SplitString_EmptyString_Empty()
{
string? str = string.Empty;
int count = 0;
bool trim = false;
string actual = BaseProcessor.SplitString(str, count, trim);
Assert.Empty(actual);
}
[Fact]
public void SplitString_ValidStringInvalidCount_Original()
{
string expected = "VALID\n";
string? str = "VALID";
int count = 0;
bool trim = false;
string actual = BaseProcessor.SplitString(str, count, trim);
Assert.Equal(expected, actual);
}
[Fact]
public void SplitString_ValidStringLessThanCount_Original()
{
string expected = "VALID\n";
string? str = "VALID";
int count = 10;
bool trim = false;
string actual = BaseProcessor.SplitString(str, count, trim);
Assert.Equal(expected, actual);
}
[Fact]
public void SplitString_ValidStringGreaterThanCount_Split()
{
string expected = "VA\nLI\nD\n";
string? str = "VALID";
int count = 2;
bool trim = false;
string actual = BaseProcessor.SplitString(str, count, trim);
Assert.Equal(expected, actual);
}
#endregion
}
}

View File

@@ -0,0 +1,425 @@
using System;
using System.IO;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Processors.Test
{
public class CleanRipTests
{
#region DetermineMediaType
[Fact]
public void DetermineMediaType_GC_Empty_GC()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Equal(MediaType.NintendoGameCubeGameDisc, actual);
}
[Fact]
public void DetermineMediaType_Wii_Null_Wii()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new CleanRip(RedumpSystem.NintendoWii);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Equal(MediaType.NintendoWiiOpticalDisc, actual);
}
[Fact]
public void DetermineMediaType_Other_Null_Null()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new CleanRip(RedumpSystem.IBMPCcompatible);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Null(actual);
}
[Fact]
public void DetermineMediaType_GC_Invalid_GC()
{
string? outputDirectory = null;
string outputFilename = "INVALID";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Equal(MediaType.NintendoGameCubeGameDisc, actual);
}
[Fact]
public void DetermineMediaType_Wii_Invalid_Wii()
{
string? outputDirectory = null;
string outputFilename = "INVALID";
var processor = new CleanRip(RedumpSystem.NintendoWii);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Equal(MediaType.NintendoWiiOpticalDisc, actual);
}
[Fact]
public void DetermineMediaType_Other_Invalid_Invalid()
{
string? outputDirectory = null;
string outputFilename = "INVALID";
var processor = new CleanRip(RedumpSystem.IBMPCcompatible);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Null(actual);
}
[Fact]
public void DetermineMediaType_GC_Valid_GC()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD");
string outputFilename = "test";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Equal(MediaType.NintendoGameCubeGameDisc, actual);
}
[Fact]
public void DetermineMediaType_Wii_Valid_Wii()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD");
string outputFilename = "test";
var processor = new CleanRip(RedumpSystem.NintendoWii);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Equal(MediaType.NintendoWiiOpticalDisc, actual);
}
[Fact]
public void DetermineMediaType_Other_Valid_Valid()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD");
string outputFilename = "test";
var processor = new CleanRip(RedumpSystem.IBMPCcompatible);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Null(actual);
}
#endregion
#region GetOutputFiles
[Fact]
public void GetOutputFiles_Null_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.GetOutputFiles(null, outputDirectory, outputFilename);
Assert.Equal(3, actual.Count);
}
[Fact]
public void GetOutputFiles_DVD_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.GetOutputFiles(MediaType.DVD, outputDirectory, outputFilename);
Assert.Equal(3, actual.Count);
}
[Fact]
public void GetOutputFiles_NintendoGameCubeGameDisc_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.GetOutputFiles(MediaType.NintendoGameCubeGameDisc, outputDirectory, outputFilename);
Assert.Equal(3, actual.Count);
}
[Fact]
public void GetOutputFiles_NintendoWiiOpticalDisc_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new CleanRip(RedumpSystem.NintendoWii);
var actual = processor.GetOutputFiles(MediaType.NintendoWiiOpticalDisc, outputDirectory, outputFilename);
Assert.Equal(3, actual.Count);
}
[Fact]
public void GetOutputFiles_Other_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.GetOutputFiles(MediaType.ApertureCard, outputDirectory, outputFilename);
Assert.Equal(3, actual.Count);
}
#endregion
#region FoundAllFiles
[Fact]
public void FoundAllFiles_Invalid_Filled()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.FoundAllFiles(MediaType.DVD, outputDirectory, outputFilename);
Assert.Equal(3, actual.Count);
}
[Fact]
public void FoundAllFiles_Valid_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD");
string outputFilename = "test.iso";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.FoundAllFiles(MediaType.DVD, outputDirectory, outputFilename);
Assert.Empty(actual);
}
#endregion
#region FoundAnyFiles
[Fact]
public void FoundAnyFiles_Invalid_Filled()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.FoundAnyFiles(MediaType.DVD, outputDirectory, outputFilename);
Assert.False(actual);
}
[Fact]
public void FoundAnyFiles_Valid_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD");
string outputFilename = "test.iso";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.FoundAnyFiles(MediaType.DVD, outputDirectory, outputFilename);
Assert.True(actual);
}
[Fact]
public void FoundAnyFiles_ValidZip_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD-zip");
string outputFilename = "test.iso";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.FoundAnyFiles(MediaType.DVD, outputDirectory, outputFilename);
Assert.True(actual);
}
#endregion
#region GenerateArtifacts
[Fact]
public void GenerateArtifacts_Invalid_Empty()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.GenerateArtifacts(MediaType.DVD, outputDirectory, outputFilename);
Assert.Empty(actual);
}
[Fact]
public void GenerateArtifacts_Valid_Filled()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD");
string outputFilename = "test.iso";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.GenerateArtifacts(MediaType.DVD, outputDirectory, outputFilename);
Assert.Equal(2, actual.Count);
}
#endregion
#region GetDeleteableFilePaths
[Fact]
public void GetDeleteableFilePaths_Invalid_Empty()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.GetDeleteableFilePaths(MediaType.DVD, outputDirectory, outputFilename);
Assert.Empty(actual);
}
[Fact]
public void GetDeleteableFilePaths_Valid_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD");
string outputFilename = "test.iso";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.GetDeleteableFilePaths(MediaType.DVD, outputDirectory, outputFilename);
Assert.Empty(actual);
}
#endregion
#region GetZippableFilePaths
[Fact]
public void GetZippableFilePaths_Invalid_Empty()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.GetZippableFilePaths(MediaType.DVD, outputDirectory, outputFilename);
Assert.Empty(actual);
}
[Fact]
public void GetZippableFilePaths_Valid_Filled()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD");
string outputFilename = "test.iso";
var processor = new CleanRip(RedumpSystem.NintendoGameCube);
var actual = processor.GetZippableFilePaths(MediaType.DVD, outputDirectory, outputFilename);
Assert.Equal(2, actual.Count);
}
#endregion
#region GenerateCleanripDatafile
[Fact]
public void GenerateCleanripDatafile_NoIsoNoDumpinfo_Null()
{
string iso = "INVALID";
string dumpinfo = "INVALID";
var actual = CleanRip.GenerateCleanripDatafile(iso, dumpinfo);
Assert.Null(actual);
}
[Fact]
public void GenerateCleanripDatafile_IsoOnly_Filled()
{
string iso = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD", "test.iso");
string dumpinfo = "INVALID";
var actual = CleanRip.GenerateCleanripDatafile(iso, dumpinfo);
Assert.NotNull(actual);
Assert.NotNull(actual.Game);
var game = Assert.Single(actual.Game);
Assert.NotNull(game.Rom);
var rom = Assert.Single(game.Rom);
Assert.Equal("9", rom.Size);
Assert.Equal("560b9f59", rom.CRC);
Assert.Equal("edbb6676247e65c2245dd4883ed9fc24", rom.MD5);
Assert.Equal("1b33ad54d78085be5ecb1cf1b3e9da821e708075", rom.SHA1);
}
[Fact]
public void GenerateCleanripDatafile_DumpinfoOnly_Filled()
{
string iso = "INVALID";
string dumpinfo = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD", "test-dumpinfo.txt");
var actual = CleanRip.GenerateCleanripDatafile(iso, dumpinfo);
Assert.NotNull(actual);
Assert.NotNull(actual.Game);
var game = Assert.Single(actual.Game);
Assert.NotNull(game.Rom);
var rom = Assert.Single(game.Rom);
Assert.Equal("-1", rom.Size);
Assert.Equal("00000000", rom.CRC);
Assert.Equal("d41d8cd98f00b204e9800998ecf8427e", rom.MD5);
Assert.Equal("da39a3ee5e6b4b0d3255bfef95601890afd80709", rom.SHA1);
}
[Fact]
public void GenerateCleanripDatafile_BothValid_Filled()
{
string iso = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD", "test.iso");
string dumpinfo = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD", "test-dumpinfo.txt");
var actual = CleanRip.GenerateCleanripDatafile(iso, dumpinfo);
Assert.NotNull(actual);
Assert.NotNull(actual.Game);
var game = Assert.Single(actual.Game);
Assert.NotNull(game.Rom);
var rom = Assert.Single(game.Rom);
Assert.Equal("9", rom.Size);
Assert.Equal("00000000", rom.CRC);
Assert.Equal("d41d8cd98f00b204e9800998ecf8427e", rom.MD5);
Assert.Equal("da39a3ee5e6b4b0d3255bfef95601890afd80709", rom.SHA1);
}
#endregion
#region GetBCA
[Fact]
public void GetBCA_InvalidPath_Null()
{
string bcaPath = "INVALID";
string? actual = CleanRip.GetBCA(bcaPath);
Assert.Null(actual);
}
[Fact]
public void GetBCA_ValidPath_Formatted()
{
string expected = "0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n0001 0203 0405 0607 0809 0A0B 0C0D 0E0F\n";
string bcaPath = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD", "test.bca");
string? actual = CleanRip.GetBCA(bcaPath);
Assert.NotNull(actual);
Assert.Equal(expected, actual);
}
#endregion
#region GetGameCubeWiiInformation
[Fact]
public void GetGameCubeWiiInformation_NoFile_False()
{
string dumpinfo = string.Empty;
bool actual = CleanRip.GetGameCubeWiiInformation(dumpinfo, out Region? region, out string? version, out string? name, out string? serial);
Assert.False(actual);
Assert.Null(region);
Assert.Null(version);
Assert.Null(name);
Assert.Null(serial);
}
[Fact]
public void GetGameCubeWiiInformation_Filled_True()
{
Region? expectedRegion = Region.World;
string? expectedVersion = "version";
string? expectedName = "name";
string? expectedSerial = "000A00";
string dumpinfo = Path.Combine(Environment.CurrentDirectory, "TestData", "CleanRip", "DVD", "test-dumpinfo.txt");
bool actual = CleanRip.GetGameCubeWiiInformation(dumpinfo, out Region? region, out string? version, out string? name, out string? serial);
Assert.True(actual);
Assert.Equal(expectedRegion, region);
Assert.Equal(expectedVersion, version);
Assert.Equal(expectedName, name);
Assert.Equal(expectedSerial, serial);
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,48 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<None Remove="TestData\*" />
</ItemGroup>
<ItemGroup>
<Content Include="TestData\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Processors\MPF.Processors.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="17.14.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.7.4" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="1.24.0" />
<PackageReference Include="xunit.assert" Version="2.9.3" />
<PackageReference Include="xunit.core" Version="2.9.3" />
<PackageReference Include="xunit.extensibility.core" Version="2.9.3" />
<PackageReference Include="xunit.extensibility.execution" Version="2.9.3" />
<PackageReference Include="xunit.runner.console" Version="2.9.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,213 @@
using System;
using System.IO;
using Xunit;
namespace MPF.Processors.Test
{
public class OutputFileTests
{
#region Properties
[Theory]
[InlineData(OutputFileFlags.None, false)]
[InlineData(OutputFileFlags.Required, true)]
[InlineData(OutputFileFlags.Artifact, false)]
[InlineData(OutputFileFlags.Binary, false)]
[InlineData(OutputFileFlags.Deleteable, false)]
[InlineData(OutputFileFlags.Zippable, false)]
public void IsRequiredTest(OutputFileFlags flags, bool expected)
{
var of = new OutputFile("file", flags, "key");
var cof = new CustomOutputFile("file", flags, "key", File.Exists);
var rof = new RegexOutputFile("file", flags, "key");
Assert.Equal(expected, of.IsRequired);
Assert.Equal(expected, cof.IsRequired);
Assert.Equal(expected, rof.IsRequired);
}
[Theory]
[InlineData(OutputFileFlags.None, false)]
[InlineData(OutputFileFlags.Required, false)]
[InlineData(OutputFileFlags.Artifact, true)]
[InlineData(OutputFileFlags.Binary, true)]
[InlineData(OutputFileFlags.Deleteable, false)]
[InlineData(OutputFileFlags.Zippable, false)]
public void IsArtifactTest(OutputFileFlags flags, bool expected)
{
var of = new OutputFile("file", flags, "key");
var cof = new CustomOutputFile("file", flags, "key", File.Exists);
var rof = new RegexOutputFile("file", flags, "key");
Assert.Equal(expected, of.IsArtifact);
Assert.Equal(expected, cof.IsArtifact);
Assert.Equal(expected, rof.IsArtifact);
}
[Theory]
[InlineData(OutputFileFlags.None, false)]
[InlineData(OutputFileFlags.Required, false)]
[InlineData(OutputFileFlags.Artifact, false)]
[InlineData(OutputFileFlags.Binary, true)]
[InlineData(OutputFileFlags.Deleteable, false)]
[InlineData(OutputFileFlags.Zippable, false)]
public void IsBinaryArtifactTest(OutputFileFlags flags, bool expected)
{
var of = new OutputFile("file", flags, "key");
var cof = new CustomOutputFile("file", flags, "key", File.Exists);
var rof = new RegexOutputFile("file", flags, "key");
Assert.Equal(expected, of.IsBinaryArtifact);
Assert.Equal(expected, cof.IsBinaryArtifact);
Assert.Equal(expected, rof.IsBinaryArtifact);
}
[Theory]
[InlineData(OutputFileFlags.None, false)]
[InlineData(OutputFileFlags.Required, false)]
[InlineData(OutputFileFlags.Artifact, false)]
[InlineData(OutputFileFlags.Binary, false)]
[InlineData(OutputFileFlags.Deleteable, true)]
[InlineData(OutputFileFlags.Zippable, false)]
public void IsDeleteableTest(OutputFileFlags flags, bool expected)
{
var of = new OutputFile("file", flags, "key");
var cof = new CustomOutputFile("file", flags, "key", File.Exists);
var rof = new RegexOutputFile("file", flags, "key");
Assert.Equal(expected, of.IsDeleteable);
Assert.Equal(expected, cof.IsDeleteable);
Assert.Equal(expected, rof.IsDeleteable);
}
[Theory]
[InlineData(OutputFileFlags.None, false)]
[InlineData(OutputFileFlags.Required, false)]
[InlineData(OutputFileFlags.Artifact, false)]
[InlineData(OutputFileFlags.Binary, false)]
[InlineData(OutputFileFlags.Deleteable, false)]
[InlineData(OutputFileFlags.Zippable, true)]
public void IsZippableTest(OutputFileFlags flags, bool expected)
{
var of = new OutputFile("file", flags, "key");
var cof = new CustomOutputFile("file", flags, "key", File.Exists);
var rof = new RegexOutputFile("file", flags, "key");
Assert.Equal(expected, of.IsZippable);
Assert.Equal(expected, cof.IsZippable);
Assert.Equal(expected, rof.IsZippable);
}
#endregion
#region Exists
[Fact]
public void Exists_Empty_False()
{
string outputDirectory = string.Empty;
var of = new OutputFile("pic.bin", OutputFileFlags.None, "key");
var cof = new CustomOutputFile("pic.bin", OutputFileFlags.None, "key", File.Exists);
var rof = new RegexOutputFile("pic.bin", OutputFileFlags.None, "key");
bool ofActual = of.Exists(outputDirectory);
bool cofActual = cof.Exists(outputDirectory);
bool rofActual = rof.Exists(outputDirectory);
Assert.False(ofActual);
Assert.False(cofActual);
Assert.False(rofActual);
}
[Fact]
public void Exists_Invalid_False()
{
string outputDirectory = "INVALID";
var of = new OutputFile("pic.bin", OutputFileFlags.None, "key");
var cof = new CustomOutputFile("pic.bin", OutputFileFlags.None, "key", File.Exists);
var rof = new RegexOutputFile("pic.bin", OutputFileFlags.None, "key");
bool ofActual = of.Exists(outputDirectory);
bool cofActual = cof.Exists(outputDirectory);
bool rofActual = rof.Exists(outputDirectory);
Assert.False(ofActual);
Assert.False(cofActual);
Assert.False(rofActual);
}
[Fact]
public void Exists_Valid_True()
{
string outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "BaseProcessor");
var of = new OutputFile("pic.bin", OutputFileFlags.None, "key");
var cof = new CustomOutputFile("pic.bin", OutputFileFlags.None, "key", File.Exists);
var rof = new RegexOutputFile("pic.bin", OutputFileFlags.None, "key");
bool ofActual = of.Exists(outputDirectory);
bool cofActual = cof.Exists(outputDirectory);
bool rofActual = rof.Exists(outputDirectory);
Assert.True(ofActual);
Assert.True(cofActual);
Assert.True(rofActual);
}
#endregion
#region GetPaths
[Fact]
public void GetPaths_Empty_Empty()
{
string outputDirectory = string.Empty;
var of = new OutputFile("pic.bin", OutputFileFlags.None, "key");
var cof = new CustomOutputFile("pic.bin", OutputFileFlags.None, "key", File.Exists);
var rof = new RegexOutputFile("pic.bin", OutputFileFlags.None, "key");
var ofActual = of.GetPaths(outputDirectory);
var cofActual = cof.GetPaths(outputDirectory);
var rofActual = rof.GetPaths(outputDirectory);
Assert.Empty(ofActual);
Assert.Empty(cofActual);
Assert.Empty(rofActual);
}
[Fact]
public void GetPaths_Invalid_Empty()
{
string outputDirectory = "INVALID";
var of = new OutputFile("pic.bin", OutputFileFlags.None, "key");
var cof = new CustomOutputFile("pic.bin", OutputFileFlags.None, "key", File.Exists);
var rof = new RegexOutputFile("pic.bin", OutputFileFlags.None, "key");
var ofActual = of.GetPaths(outputDirectory);
var cofActual = cof.GetPaths(outputDirectory);
var rofActual = rof.GetPaths(outputDirectory);
Assert.Empty(ofActual);
Assert.Empty(cofActual);
Assert.Empty(rofActual);
}
[Fact]
public void GetPaths_Valid_Filled()
{
string outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "BaseProcessor");
var of = new OutputFile("pic.bin", OutputFileFlags.None, "key");
var cof = new CustomOutputFile("pic.bin", OutputFileFlags.None, "key", File.Exists);
var rof = new RegexOutputFile("pic.bin", OutputFileFlags.None, "key");
var ofActual = of.GetPaths(outputDirectory);
var cofActual = cof.GetPaths(outputDirectory);
var rofActual = rof.GetPaths(outputDirectory);
Assert.Single(ofActual);
Assert.Single(cofActual);
Assert.Single(rofActual);
}
#endregion
}
}

View File

@@ -0,0 +1,211 @@
using System;
using System.IO;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Processors.Test
{
public class PS3CFWTests
{
#region DetermineMediaType
[Fact]
public void DetermineMediaType_Null_BluRay()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Equal(MediaType.BluRay, actual);
}
[Fact]
public void DetermineMediaType_Invalid_BluRay()
{
string? outputDirectory = null;
string outputFilename = "INVALID";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Equal(MediaType.BluRay, actual);
}
[Fact]
public void DetermineMediaType_Valid_BluRay()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "PS3CFW", "BluRay");
string outputFilename = "test";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.DetermineMediaType(outputDirectory, outputFilename);
Assert.Equal(MediaType.BluRay, actual);
}
#endregion
#region GetOutputFiles
[Fact]
public void GetOutputFiles_Null_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.GetOutputFiles(null, outputDirectory, outputFilename);
Assert.Equal(4, actual.Count);
}
[Fact]
public void GetOutputFiles_BluRay_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.GetOutputFiles(MediaType.BluRay, outputDirectory, outputFilename);
Assert.Equal(4, actual.Count);
}
[Fact]
public void GetOutputFiles_Other_Populated()
{
string? outputDirectory = null;
string outputFilename = "test";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.GetOutputFiles(MediaType.ApertureCard, outputDirectory, outputFilename);
Assert.Equal(4, actual.Count);
}
#endregion
#region FoundAllFiles
[Fact]
public void FoundAllFiles_Invalid_Filled()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.FoundAllFiles(MediaType.BluRay, outputDirectory, outputFilename);
Assert.Equal(3, actual.Count);
}
[Fact]
public void FoundAllFiles_Valid_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "PS3CFW", "BluRay");
string outputFilename = "test.iso";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.FoundAllFiles(MediaType.BluRay, outputDirectory, outputFilename);
Assert.Empty(actual);
}
#endregion
#region FoundAnyFiles
[Fact]
public void FoundAnyFiles_Invalid_Filled()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.FoundAnyFiles(MediaType.BluRay, outputDirectory, outputFilename);
Assert.False(actual);
}
[Fact]
public void FoundAnyFiles_Valid_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "PS3CFW", "BluRay");
string outputFilename = "test.iso";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.FoundAnyFiles(MediaType.BluRay, outputDirectory, outputFilename);
Assert.True(actual);
}
[Fact]
public void FoundAnyFiles_ValidZip_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "PS3CFW", "BluRay-zip");
string outputFilename = "test.iso";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.FoundAnyFiles(MediaType.BluRay, outputDirectory, outputFilename);
Assert.True(actual);
}
#endregion
#region GenerateArtifacts
[Fact]
public void GenerateArtifacts_Invalid_Empty()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.GenerateArtifacts(MediaType.BluRay, outputDirectory, outputFilename);
Assert.Empty(actual);
}
[Fact]
public void GenerateArtifacts_Valid_Filled()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "PS3CFW", "BluRay");
string outputFilename = "test.iso";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.GenerateArtifacts(MediaType.BluRay, outputDirectory, outputFilename);
Assert.Equal(2, actual.Count);
}
#endregion
#region GetDeleteableFilePaths
[Fact]
public void GetDeleteableFilePaths_Invalid_Empty()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.GetDeleteableFilePaths(MediaType.BluRay, outputDirectory, outputFilename);
Assert.Empty(actual);
}
[Fact]
public void GetDeleteableFilePaths_Valid_Empty()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "PS3CFW", "BluRay");
string outputFilename = "test.iso";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.GetDeleteableFilePaths(MediaType.BluRay, outputDirectory, outputFilename);
Assert.Empty(actual);
}
#endregion
#region GetZippableFilePaths
[Fact]
public void GetZippableFilePaths_Invalid_Empty()
{
string? outputDirectory = null;
string outputFilename = string.Empty;
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.GetZippableFilePaths(MediaType.BluRay, outputDirectory, outputFilename);
Assert.Empty(actual);
}
[Fact]
public void GetZippableFilePaths_Valid_Filled()
{
string? outputDirectory = Path.Combine(Environment.CurrentDirectory, "TestData", "PS3CFW", "BluRay");
string outputFilename = "test.iso";
var processor = new PS3CFW(RedumpSystem.SonyPlayStation3);
var actual = processor.GetZippableFilePaths(MediaType.BluRay, outputDirectory, outputFilename);
Assert.Equal(2, actual.Count);
}
#endregion
}
}

View File

@@ -0,0 +1,543 @@
using System;
using System.IO;
using SabreTools.Models.Logiqx;
using SabreTools.Models.PIC;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Processors.Test
{
// TODO: Write tests for PlayStation 3 specific tools
// TODO: Write tests for Xbox and Xbox 360
public class ProcessingToolTests
{
#region GenerateDatfile
[Fact]
public void GenerateDatfile_Null_Null()
{
Datafile? datafile = null;
string? actual = ProcessingTool.GenerateDatfile(datafile);
Assert.Null(actual);
}
[Fact]
public void GenerateDatfile_Invalid_Null()
{
Datafile? datafile = new Datafile();
string? actual = ProcessingTool.GenerateDatfile(datafile);
Assert.Null(actual);
}
[Fact]
public void GenerateDatfile_Valid_Filled()
{
string? expected = "<rom name=\"test\" size=\"12345\" crc=\"00000000\" md5=\"d41d8cd98f00b204e9800998ecf8427e\" sha1=\"da39a3ee5e6b4b0d3255bfef95601890afd80709\" />";
Rom rom = new Rom
{
Name = "test",
Size = "12345",
CRC = "00000000",
MD5 = "d41d8cd98f00b204e9800998ecf8427e",
SHA1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709",
};
Game game = new Game { Rom = [rom] };
Datafile? datafile = new Datafile { Game = [game] };
string? actual = ProcessingTool.GenerateDatfile(datafile);
Assert.Equal(expected, actual);
}
#endregion
#region GetBase64
[Fact]
public void GetBase64_Null_Null()
{
string? content = null;
string? actual = ProcessingTool.GetBase64(content);
Assert.Null(actual);
}
[Fact]
public void GetBase64_Empty_Null()
{
string? content = string.Empty;
string? actual = ProcessingTool.GetBase64(content);
Assert.Null(actual);
}
[Fact]
public void GetBase64_Valid_Filled()
{
string? expected = "MTIzNDVBQkNERQ==";
string? content = "12345ABCDE";
string? actual = ProcessingTool.GetBase64(content);
Assert.Equal(expected, actual);
}
#endregion
#region GetDatafile
[Fact]
public void GetDatafile_Null_Null()
{
string? dat = null;
Datafile? actual = ProcessingTool.GetDatafile(dat);
Assert.Null(actual);
}
[Fact]
public void GetDatafile_Empty_Null()
{
string? dat = string.Empty;
Datafile? actual = ProcessingTool.GetDatafile(dat);
Assert.Null(actual);
}
[Fact]
public void GetDatafile_Valid_Filled()
{
string? dat = Path.Combine(Environment.CurrentDirectory, "TestData", "ProcessingTool", "datfile.xml");
Datafile? actual = ProcessingTool.GetDatafile(dat);
// TODO: Add structure validation
Assert.NotNull(actual);
}
#endregion
#region GetDiscInformation
// TODO: Figure out how to mock a PIC file
#endregion
#region GetFileModifiedDate
// TODO: Figure out how to get a statically-dated file
#endregion
#region GetFullFile
[Fact]
public void GetFullFile_Empty_Null()
{
string filename = string.Empty;
string? actual = ProcessingTool.GetFullFile(filename);
Assert.Null(actual);
}
[Fact]
public void GetFullFile_Invalid_Null()
{
string filename = "INVALID";
string? actual = ProcessingTool.GetFullFile(filename);
Assert.Null(actual);
}
[Fact]
public void GetFullFile_ValidBinary_Filled()
{
string? expected = "544553542044415441";
string filename = Path.Combine(Environment.CurrentDirectory, "TestData", "ProcessingTool", "textfile.txt");
string? actual = ProcessingTool.GetFullFile(filename, binary: true);
Assert.Equal(expected, actual);
}
[Fact]
public void GetFullFile_ValidText_Filled()
{
string? expected = "TEST DATA";
string filename = Path.Combine(Environment.CurrentDirectory, "TestData", "ProcessingTool", "textfile.txt");
string? actual = ProcessingTool.GetFullFile(filename, binary: false);
Assert.Equal(expected, actual);
}
#endregion
#region GetISOHashValues
[Fact]
public void GetISOHashValues_Datafile_Null_Null()
{
Datafile? datafile = null;
bool actual = ProcessingTool.GetISOHashValues(datafile,
out long size,
out string? crc32,
out string? md5,
out string? sha1);
Assert.False(actual);
Assert.Equal(-1, size);
Assert.Null(crc32);
Assert.Null(md5);
Assert.Null(sha1);
}
[Fact]
public void GetISOHashValues_Datafile_Empty_Null()
{
Datafile? datafile = new Datafile();
bool actual = ProcessingTool.GetISOHashValues(datafile,
out long size,
out string? crc32,
out string? md5,
out string? sha1);
Assert.False(actual);
Assert.Equal(-1, size);
Assert.Null(crc32);
Assert.Null(md5);
Assert.Null(sha1);
}
[Fact]
public void GetISOHashValues_Datafile_Valid_Filled()
{
long expectedSize = 12345;
string? expectedCrc32 = "00000000";
string? expectedMd5 = "d41d8cd98f00b204e9800998ecf8427e";
string? expectedSha1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
Rom rom = new Rom
{
Name = "test",
Size = "12345",
CRC = "00000000",
MD5 = "d41d8cd98f00b204e9800998ecf8427e",
SHA1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709",
};
Game game = new Game { Rom = [rom] };
Datafile? datafile = new Datafile { Game = [game] };
bool actual = ProcessingTool.GetISOHashValues(datafile,
out long size,
out string? crc32,
out string? md5,
out string? sha1);
Assert.True(actual);
Assert.Equal(expectedSize, size);
Assert.Equal(expectedCrc32, crc32);
Assert.Equal(expectedMd5, md5);
Assert.Equal(expectedSha1, sha1);
}
[Fact]
public void GetISOHashValues_String_Null_Null()
{
string? hashData = null;
bool actual = ProcessingTool.GetISOHashValues(hashData,
out long size,
out string? crc32,
out string? md5,
out string? sha1);
Assert.False(actual);
Assert.Equal(-1, size);
Assert.Null(crc32);
Assert.Null(md5);
Assert.Null(sha1);
}
[Fact]
public void GetISOHashValues_String_Empty_Null()
{
string? hashData = string.Empty;
bool actual = ProcessingTool.GetISOHashValues(hashData,
out long size,
out string? crc32,
out string? md5,
out string? sha1);
Assert.False(actual);
Assert.Equal(-1, size);
Assert.Null(crc32);
Assert.Null(md5);
Assert.Null(sha1);
}
[Fact]
public void GetISOHashValues_String_Invalid_Filled()
{
string? hashData = "INVALID";
bool actual = ProcessingTool.GetISOHashValues(hashData,
out long size,
out string? crc32,
out string? md5,
out string? sha1);
Assert.False(actual);
Assert.Equal(-1, size);
Assert.Null(crc32);
Assert.Null(md5);
Assert.Null(sha1);
}
[Fact]
public void GetISOHashValues_String_Valid_Filled()
{
long expectedSize = 12345;
string? expectedCrc32 = "00000000";
string? expectedMd5 = "d41d8cd98f00b204e9800998ecf8427e";
string? expectedSha1 = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
string? hashData = "<rom name=\"test\" size=\"12345\" crc=\"00000000\" md5=\"d41d8cd98f00b204e9800998ecf8427e\" sha1=\"da39a3ee5e6b4b0d3255bfef95601890afd80709\" />";
bool actual = ProcessingTool.GetISOHashValues(hashData,
out long size,
out string? crc32,
out string? md5,
out string? sha1);
Assert.True(actual);
Assert.Equal(expectedSize, size);
Assert.Equal(expectedCrc32, crc32);
Assert.Equal(expectedMd5, md5);
Assert.Equal(expectedSha1, sha1);
}
#endregion
#region GetLayerbreaks
[Fact]
public void GetLayerbreaks_Null_Null()
{
DiscInformation? di = null;
bool actual = ProcessingTool.GetLayerbreaks(di,
out long? layerbreak1,
out long? layerbreak2,
out long? layerbreak3);
Assert.False(actual);
Assert.Null(layerbreak1);
Assert.Null(layerbreak2);
Assert.Null(layerbreak3);
}
[Fact]
public void GetLayerbreaks_Empty_Null()
{
DiscInformation? di = new DiscInformation();
bool actual = ProcessingTool.GetLayerbreaks(di,
out long? layerbreak1,
out long? layerbreak2,
out long? layerbreak3);
Assert.False(actual);
Assert.Null(layerbreak1);
Assert.Null(layerbreak2);
Assert.Null(layerbreak3);
}
[Fact]
public void GetLayerbreaks_Valid_Filled()
{
long? expectedLayerbreak1 = 67372038;
long? expectedLayerbreak2 = 134744076;
long? expectedLayerbreak3 = 202116114;
DiscInformationUnit layer0 = new DiscInformationUnit
{
Body = new DiscInformationUnitBody
{
FormatDependentContents = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
}
};
DiscInformationUnit layer1 = new DiscInformationUnit
{
Body = new DiscInformationUnitBody
{
FormatDependentContents = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
}
};
DiscInformationUnit layer2 = new DiscInformationUnit
{
Body = new DiscInformationUnitBody
{
FormatDependentContents = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
}
};
DiscInformationUnit layer3 = new DiscInformationUnit
{
Body = new DiscInformationUnitBody
{
FormatDependentContents = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
}
};
DiscInformation? di = new DiscInformation
{
Units = [layer0, layer1, layer2, layer3],
};
bool actual = ProcessingTool.GetLayerbreaks(di,
out long? layerbreak1,
out long? layerbreak2,
out long? layerbreak3);
Assert.True(actual);
Assert.Equal(expectedLayerbreak1, layerbreak1);
Assert.Equal(expectedLayerbreak2, layerbreak2);
Assert.Equal(expectedLayerbreak3, layerbreak3);
}
#endregion
#region GetPICIdentifier
[Fact]
public void GetPICIdentifier_Null_Null()
{
DiscInformation? di = null;
string? actual = ProcessingTool.GetPICIdentifier(di);
Assert.Null(actual);
}
[Fact]
public void GetPICIdentifier_Empty_Null()
{
DiscInformation? di = new DiscInformation();
string? actual = ProcessingTool.GetPICIdentifier(di);
Assert.Null(actual);
}
[Fact]
public void GetPICIdentifier_Valid_Filled()
{
string? expected = "UHD";
DiscInformationUnit layer0 = new DiscInformationUnit
{
Body = new DiscInformationUnitBody
{
DiscTypeIdentifier = "UHD",
}
};
DiscInformation? di = new DiscInformation
{
Units = [layer0],
};
string? actual = ProcessingTool.GetPICIdentifier(di);
Assert.Equal(expected, actual);
}
#endregion
#region NormalizeShiftJIS
[Fact]
public void NormalizeShiftJIS_Null_Empty()
{
byte[]? contents = null;
string? actual = ProcessingTool.NormalizeShiftJIS(contents);
Assert.NotNull(actual);
Assert.Empty(actual);
}
[Fact]
public void NormalizeShiftJIS_Empty_Empty()
{
byte[]? contents = [];
string? actual = ProcessingTool.NormalizeShiftJIS(contents);
Assert.NotNull(actual);
Assert.Empty(actual);
}
[Fact]
public void NormalizeShiftJIS_NoShiftJIS_Valid()
{
string? expected = "ABCDE";
byte[]? contents = [0x41, 0x42, 0x43, 0x44, 0x45];
string? actual = ProcessingTool.NormalizeShiftJIS(contents);
Assert.Equal(expected, actual);
}
[Fact]
public void NormalizeShiftJIS_ShiftJIS_Valid()
{
string? expected = "ABCDE ひらがな";
byte[]? contents = [0x41, 0x42, 0x43, 0x44, 0x45, 0x20, 0x82, 0xD0, 0x82, 0xE7, 0x82, 0xAA, 0x82, 0xC8];
string? actual = ProcessingTool.NormalizeShiftJIS(contents);
Assert.Equal(expected, actual);
}
#endregion
#region GetUMDCategory
[Theory]
[InlineData(null, null)]
[InlineData("", null)]
[InlineData("(GAME)", DiscCategory.Games)]
[InlineData("(VIDEO)", DiscCategory.Video)]
[InlineData("(AUDIO)", DiscCategory.Audio)]
[InlineData("INVALID", null)]
public void GetUMDCategoryTest(string? category, DiscCategory? expected)
{
DiscCategory? actual = ProcessingTool.GetUMDCategory(category);
Assert.Equal(expected, actual);
}
#endregion
#region GetPlayStationRegion
[Theory]
[InlineData(null, null)]
[InlineData("", null)]
[InlineData("S_A", Region.Asia)]
[InlineData("S_C", Region.China)]
[InlineData("S_E", Region.Europe)]
[InlineData("S_K", Region.SouthKorea)]
[InlineData("S_U", Region.UnitedStatesOfAmerica)]
[InlineData("S_PS_46", Region.SouthKorea)]
[InlineData("S_PS_51", Region.Asia)]
[InlineData("S_PS_56", Region.SouthKorea)]
[InlineData("S_PS_55", Region.Asia)]
[InlineData("S_PS_XX", Region.Japan)]
[InlineData("S_PM_645", Region.SouthKorea)]
[InlineData("S_PM_675", Region.SouthKorea)]
[InlineData("S_PM_885", Region.SouthKorea)]
[InlineData("S_PM_XXX", Region.Japan)]
[InlineData("S_PX", Region.Japan)]
[InlineData("PAPX", Region.Japan)]
[InlineData("PABX", null)]
[InlineData("PBPX", null)]
[InlineData("PCBX", Region.Japan)]
[InlineData("PCXC", Region.Japan)]
[InlineData("PDBX", Region.Japan)]
[InlineData("PEBX", Region.Europe)]
[InlineData("PUBX", Region.UnitedStatesOfAmerica)]
public void GetPlayStationRegionTest(string? serial, Region? expected)
{
Region? actual = ProcessingTool.GetPlayStationRegion(serial);
Assert.Equal(expected, actual);
}
#endregion
#region GetXGDRegion
[Theory]
[InlineData(null, null)]
[InlineData(' ', null)]
[InlineData('W', Region.World)]
[InlineData('A', Region.UnitedStatesOfAmerica)]
[InlineData('J', Region.JapanAsia)]
[InlineData('E', Region.Europe)]
[InlineData('K', Region.USAJapan)]
[InlineData('L', Region.USAEurope)]
[InlineData('H', Region.JapanEurope)]
[InlineData('X', null)]
public void GetXGDRegionTest(char? region, Region? expected)
{
Region? actual = ProcessingTool.GetXGDRegion(region);
Assert.Equal(expected, actual);
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1 @@
TEST DATA

View File

@@ -0,0 +1 @@
TEST DATA

View File

@@ -0,0 +1,6 @@
<CICMMetadata>
<OpticalDisc>
<DiscType>Compact Disc</DiscType>
<DiscSubType>CD-ROM</DiscSubType>
</OpticalDisc>
</CICMMetadata>

View File

@@ -0,0 +1 @@
TEST DATA

View File

@@ -0,0 +1 @@
TEST DATA

View File

@@ -0,0 +1 @@
TEST DATA

View File

@@ -0,0 +1 @@
TEST DATA

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