Compare commits

...

527 Commits

Author SHA1 Message Date
Matt Nadareski
299fafe1f8 Update changelog 2026-02-02 16:24:01 -05:00
Matt Nadareski
8f6305a7c4 Extend logging with a new state (fixes #944) 2026-02-02 16:23:48 -05:00
Matt Nadareski
3f61957715 Log the system being used, in case it came from config 2026-01-30 08:45:13 -05:00
Matt Nadareski
57750295fb Validate a system is provided for CLI applications 2026-01-29 20:36:56 -05:00
Matt Nadareski
286ec864ab Add unused Dreamdump execution context 2026-01-27 16:16:34 -05:00
Matt Nadareski
e4fd644c61 Split path creation in OptionsLoader 2026-01-27 15:07:22 -05:00
Matt Nadareski
7502d7e8df Update RedumpLib to 1.9.1 2026-01-27 14:02:54 -05:00
Matt Nadareski
439fb2717c Add MPRESS to packer filters (fixes #938) 2026-01-27 08:40:17 -05:00
Matt Nadareski
089ab1c4a2 Add editorconfig, fix issues 2026-01-25 18:09:00 -05:00
Deterous
89a50ea424 Update Redumper to build 683 (#933) 2026-01-04 20:13:35 -05:00
Matt Nadareski
740d867d7b Update DIC to 20260101 2026-01-03 21:57:14 -05:00
Matt Nadareski
83fa4852c9 Add default subfolder to CLI outputs 2026-01-01 10:21:19 -05:00
Deterous
ecc00a28c2 Update Redumper to build 681 (Dreamcast support) (#930)
* Update redumper to b678

* Update Redumper to build 681 (Dreamcast support)

* build 682
2025-12-26 21:24:02 -05:00
Matt Nadareski
2d90c849e1 Use default media type if not provided (fixes #925) 2025-12-22 10:10:49 -05:00
Deterous
1d065ffd48 Update Redumper to build 676 (rename asus flags to mediatek) (#927)
* Update Redumper to build 676 (rename asus flags to mediatek)

* Fix ToRedumperDriveType enum extension
2025-12-15 09:56:49 -05:00
Deterous
ca885c3b89 Update Redumper to build 671 (#924)
* Update Redumper to build 669

* Fix tests

* Bump to build 670

* Update to build 671

* Bump changelist

* Fix tests
2025-12-10 20:32:08 -05:00
Matt Nadareski
94fd614673 Check range-specific values in layerbreak 2025-12-02 08:36:57 -05:00
Matt Nadareski
9fc5799999 Check for null or empty layerbreak arrays 2025-12-02 08:30:33 -05:00
Matt Nadareski
61f90a635f Bump version 2025-11-28 09:53:54 -05:00
Matt Nadareski
8cc0ff3829 Reenable Zstd PKZIP outputs 2025-11-27 20:09:43 -05:00
Matt Nadareski
cac6bcc4de Add commented fix for Zstd PKZIP 2025-11-26 20:25:51 -05:00
Matt Nadareski
7ac0089e81 Disable Zstd PKZIP outputs 2025-11-25 19:38:30 -05:00
Matt Nadareski
48576b38be Update Redumper to build 665 2025-11-25 18:14:08 -05:00
Matt Nadareski
82dedf1ceb Add support for .NET 10 2025-11-25 09:14:46 -05:00
Matt Nadareski
395cded5ef Clarify the unmounted device case (fixes #921) 2025-11-24 13:14:24 -05:00
Deterous
a48f9d1c83 New Redumper Drive Pregap Start option (#920)
* Pregap start option

* Fix DriveType names

* Fix options window

* Update changelist
2025-11-24 09:18:28 -05:00
Matt Nadareski
3975003686 Update changelog 2025-11-18 18:42:28 -05:00
HeroponRikiBestest
4beae71511 Remove DPM identifier for StarForce Keyless (#918)
* Redump staff does not deem the DPM identifier useful, so clean it from the submissioninfo output

* Add test for sanitization of StarForce Keyless
2025-11-18 18:41:10 -05:00
Matt Nadareski
f0f41c86c5 Ensure volume label is trimmed if used in filenames 2025-11-14 09:14:14 -05:00
Matt Nadareski
b1b6eb2c9d Clean up informational issues 2025-11-11 15:52:26 -05:00
Matt Nadareski
29754b4c0e Name some type parameters 2025-11-11 12:06:30 -05:00
Matt Nadareski
35cda84308 Update Redumper to build 663 2025-11-10 20:37:41 -05:00
Matt Nadareski
a877397fe6 Path scan after image scan 2025-11-10 19:34:17 -05:00
Matt Nadareski
2f0471d596 Move and rename new protection scan method 2025-11-10 13:52:31 -05:00
Matt Nadareski
2037ded792 Scan multi-track images for protection 2025-11-10 13:37:23 -05:00
Matt Nadareski
5f1a68a5f5 Scan disc image if not multi-track 2025-11-10 13:27:54 -05:00
Matt Nadareski
87746c8677 Update packages 2025-11-10 13:17:10 -05:00
Matt Nadareski
2665c29918 Use dated default output filenames 2025-11-09 20:03:46 -05:00
Matt Nadareski
5637cf5201 Update Redumper to build 660 2025-11-07 22:06:20 -05:00
Matt Nadareski
eb1d000e4f Fix tests 2025-11-07 09:58:28 -05:00
Matt Nadareski
8db467128d Add DVD-Video to list of copy protection scanning systems 2025-11-07 09:52:49 -05:00
Deterous
dc90e2609d Pre-compress all skeletons for multi-track CDs (#915)
* Pre-compress all skeletons for multi-track CDs

* Gate behind file existing

* Full track path

* split

* if base path is root
2025-11-06 07:39:41 -05:00
Deterous
e96bd21f1d Update Redumper to build 658 (#914) 2025-11-05 20:04:20 -05:00
Matt Nadareski
693599b986 Clean up submission info use and log link text 2025-11-03 08:08:51 -05:00
Matt Nadareski
d482fe926c Fix issues with path assembly 2025-10-29 20:11:02 -04:00
Deterous
17b5432352 Fix langs (#913)
* Fix langs

* changelist
2025-10-27 12:24:15 -04:00
Deterous
ca480f27ed Add hidden language (NovaAurora) (#912)
* Add hidden language (NovaAurora)

* add test
2025-10-27 10:04:00 -04:00
Matt Nadareski
4bad66e706 Slight tweak to automatic UI links 2025-10-26 22:01:10 -04:00
Matt Nadareski
abe00fe132 Try to add UI links to rolling release 2025-10-26 21:46:39 -04:00
Deterous
ba73479837 Rolling tag fix (#911)
* Rolling tag fix

* Remove recursive pull
2025-10-25 21:40:40 -04:00
Matt Nadareski
f2a28dd36b Update changelog 2025-10-24 09:24:40 -04:00
Piotr Swat
928e30a5de Updated Polish translation (#910) 2025-10-24 09:01:26 -04:00
Matt Nadareski
007fc2f9b9 Stop using long name for default system setting 2025-10-23 08:39:09 -04:00
Matt Nadareski
d2d23ebbdf Limit visibility of Compatible OS text box 2025-10-22 11:45:10 -04:00
Matt Nadareski
e02ab769ec Update RedumpLib to 1.8.0 2025-10-22 09:16:53 -04:00
Matt Nadareski
3df78def00 Minor Spanish cleanup 2025-10-21 10:59:49 -04:00
Matt Nadareski
7eddfc5fed Possibly fix missing options string 2025-10-21 09:50:54 -04:00
Matt Nadareski
f6f154b6db Free disk space for runners 2025-10-21 09:22:10 -04:00
Matt Nadareski
47cd133437 Change CLI first-run wording 2025-10-21 09:01:33 -04:00
Matt Nadareski
74c9641a54 Simplify first-run CLI experience 2025-10-21 08:58:01 -04:00
Matt Nadareski
fc5929db2a Add per-OS executable names 2025-10-21 08:51:24 -04:00
Matt Nadareski
f53b428075 Separate out default program paths 2025-10-21 08:43:52 -04:00
Matt Nadareski
46dc931b3f Readme updates for accuracy 2025-10-21 08:30:53 -04:00
Deterous
9608cfb700 Be selective on which systems to enable skeleton (#909)
* Be selective on which systems to enable skeleton

* Bring back lost brace

* case

* RedumpSystem
2025-10-19 20:41:15 -04:00
Matt Nadareski
3493238849 Make log archive handling more user-apparent 2025-10-19 13:15:48 -04:00
Matt Nadareski
4c364f519e Regex outputs should not name based on pattern 2025-10-19 12:47:21 -04:00
Matt Nadareski
337a7a181c Fix tests broken by last commit 2025-10-19 12:27:50 -04:00
Matt Nadareski
071e3a0024 Redumper state file is not accessed 2025-10-19 12:21:20 -04:00
Matt Nadareski
ff0c742dee Handle log zip path in information extraction 2025-10-19 11:55:37 -04:00
Matt Nadareski
7aba0d1c9c Make .img not required for DIC outputs 2025-10-19 11:53:51 -04:00
Matt Nadareski
ac6a77d9da Allow placeholder files to be used in Check 2025-10-19 11:45:22 -04:00
Matt Nadareski
6eb976c842 Merge pull request #908 from SabreTools/ui-langs
Support UI Languages
2025-10-19 09:51:41 -04:00
Matt Nadareski
ba39a8b22f Update changelog 2025-10-19 09:46:11 -04:00
Deterous
88a6fa71a9 Fix long translated strings (#907)
* Build branch

* Enable debug media info window by default

* Fix bold options window context menu

* Shorten ukr strings

* More shortened strings

* better short ukr

* revert build changes and debug flag

* fix test login button strings
2025-10-19 09:46:00 -04:00
Matt Nadareski
584b8c0109 Allow files to be zipped but not deleted 2025-10-19 09:25:56 -04:00
Matt Nadareski
c6258b5520 Handle a provided log zip in Check operation 2025-10-18 20:15:47 -04:00
Matt Nadareski
d6dbbbe928 Add list configuration commandline feature (fixes #906) 2025-10-18 18:47:27 -04:00
Matt Nadareski
f22b3c4ac0 Add Ukrainian translation (superg) 2025-10-18 18:10:45 -04:00
Matt Nadareski
fae399f8bc Add console print when Check loads from config 2025-10-18 13:08:32 -04:00
Matt Nadareski
d2ed2f81ae Make Check flags toggle if config used 2025-10-18 12:03:37 -04:00
Deterous
7dbcfce46c Add basic translation for 8 more languages (#902)
* Add langs

* Top menu spacing

* need using

* ToInterfaceLanguage

* fix UI strings

* auto menu width

* cleanup strings

* fix spacing

* better strings

* revert CI change

* more translation work

* test remove System.Windows.Forms

* full namespace for WinForms

* WinForms namespace

* App is in System.Windows

* Fix string bugs

* Better swedish

* Update changelog
2025-10-18 21:12:51 +09:00
Matt Nadareski
ebd0552a2a Add 8 more language menu items 2025-10-17 17:36:12 -04:00
Matt Nadareski
343973263c Add skeleton for 8 more languages 2025-10-17 17:26:56 -04:00
Matt Nadareski
e5220a8224 Update changelog 2025-10-17 13:01:51 -04:00
Deterous
146120c210 Complete translation support (#901)
* Translate options window

* build on branch

* ui-langs-dev

* fix dupe string

* Translate Media Information Window

* Revert CI process
2025-10-17 13:01:31 -04:00
Matt Nadareski
807e4655a6 Update changelog 2025-10-17 09:12:37 -04:00
Matt Nadareski
e32ebec197 Ensure readers and writers dispose 2025-10-17 08:50:50 -04:00
Matt Nadareski
8d8886390d Minor formatting cleanup 2025-10-17 08:10:02 -04:00
Deterous
5a2aa9d325 Continue UI translation work (#900)
* No redundant translation

* semicolon

* rename DefaultUILanguage

* finish rename

* Cleanup

* Fix SetInterfaceLanguage

* Fix LanguageMenuItemClick

* Set resources before window loads

* set lang during init

* Update interface language after setting options

* Check language menuitem when updating options

* Clear checks when loading language from options

* SetInterfaceLanguage clears checks

* obj as MenuItem

* safer unchecking

* type naming

* uncheck menuitem

* var

* don't uncheck within setlanguage

* test

* equals

* top left menu margin

* help menu right margin

* Fix title bar

* Margin on help menu item

* margin on stack panel

* more translations

* Translate message boxes

* fix margin

* only change lang if options was saved

* only update language if it has been updated

* smaller negative margin

* padding

* padding on textblock

* Revert GHA changes
2025-10-17 08:02:09 -04:00
Matt Nadareski
37aa1645dd Add UI by default to launch 2025-10-17 07:50:46 -04:00
Matt Nadareski
052d074e92 Fill in some gaps 2025-10-16 23:02:35 -04:00
Matt Nadareski
8c551dc990 Clean up a bit of element work 2025-10-16 22:35:31 -04:00
Matt Nadareski
b2fcc190fd Clean up nullability 2025-10-16 21:09:42 -04:00
Matt Nadareski
2325844bd4 Slight cleanup to main window language handling 2025-10-16 20:33:43 -04:00
Matt Nadareski
f63517bb52 Move to enum-based model for UI language options 2025-10-16 20:28:37 -04:00
Deterous
99d26f177b Initial UI lang code (#899)
* Test using resources

* Include system namespace in resource xaml

* system namespace name

* rename strings

* Default strings

* Window resources

* add keys

* just one key

* Combo box example

* window resources

* Dropdown in menu bar

* nullable

* non nullable

* string list init

* simple

* English default

* Add menu item for language

* parent is menuitem

* fix

* no null lang

* Only build win-x64, GHA storage limit

* Korean

* test

* Set resource strings at app level

* remove lost endif

* Better UI

* Move langs next to buttons

* update

* fix button

* More translations

* Better menu size

* too many semicolons

* top right menu bar

* Tweaks

* Top bar positioning

* title bar width

* try again

* final

* Back to original publish script

* more windows

* pre-merge test

* Test non-latin underscore

* More strings

* fix

* FindResource is a function

* space

* cast spells

* Log about text

* semicolon

* Good

* cast spells

* using System.Windows in Frontend

* Translate in MainViewModel

* Dynamic GetFormattedVolumeLabel

* Nullability

* Fix

* using for dict

* Translate func in MVM

* Don't translate in init

* Update MVM translations

* closing brace

* Deprecate resource string

* test

* test2

* set current system

* trial field

* field is preview

* default empty string

* default null

* fix build

* empty string

* GIve up on no system selected text

* Fix context menu border

* Revert half fix

* Translate more IRD Window strings

* Loose string

* Detect current locale

* fix

* System.Globalization

* Locale detection for default lang

* break on zh case

* default startup lang

* default lang option

* fix

* fix2

* fix3

* nonnullable

* final fix

* final final fix

* default language option

* use default language on startup

* empty entry

* semicolon
2025-10-16 11:47:36 -04:00
Matt Nadareski
d6b28de586 Reduce chance of empty strings 2025-10-12 22:45:54 -04:00
Deterous
e8d1567d07 Parse XboxOne/SX Title IDs (#897)
* Parse XboxOne/SX Title IDs

* Bump RedumpLib

* Fix build

* Don't use lists because net20
2025-10-12 20:14:50 -04:00
Matt Nadareski
cd8b484ae3 Replace "We" comments 2025-10-11 16:06:53 -04:00
Matt Nadareski
65b9735941 Conditionally require state 2025-10-11 09:41:28 -04:00
Matt Nadareski
22f7e2a0ed Move output file implementations to separate namespace 2025-10-11 09:34:00 -04:00
Matt Nadareski
2c5dc7390a Use WriteToFile extension for zip entries 2025-10-11 09:27:55 -04:00
Deterous
c5e01b9578 Support reparsing of MPF-processed outputs (#896)
* Have a go at extracting files from existing log

* no null output dir

* Extract log from archive if it is zipped during mediatype detection

* imports

* fix variable names

* fix null output dir

* Final fixes

* changelist

* assign null

* fix
2025-10-10 22:03:03 -04:00
Matt Nadareski
882243316c Implement file merging in CleanRip 2025-10-10 11:40:02 -04:00
Matt Nadareski
1b62ed0c03 Add file merge method in CleanRip 2025-10-10 11:37:11 -04:00
Matt Nadareski
edb8c08a39 Move Zstd compression helper to base processor 2025-10-10 10:07:32 -04:00
Matt Nadareski
ad93387aea Add BCA to list of files to select in Check UI 2025-10-10 09:05:54 -04:00
Matt Nadareski
9012ff85a9 Bump version 2025-10-10 08:44:07 -04:00
Matt Nadareski
ccc33bebbd Try to handle Windows-specific compression issue 2025-10-10 08:09:49 -04:00
Matt Nadareski
57b07aee02 Support detecting split Wii for CleanRip 2025-10-09 15:35:38 -04:00
Matt Nadareski
069d676492 Fix test broken by last commit 2025-10-09 15:05:57 -04:00
Matt Nadareski
a26dfb7e7a Enable skeleton output for all CLI runs 2025-10-09 12:06:16 -04:00
Matt Nadareski
a9ea457808 Use block-based reading instead of CopyTo 2025-10-09 10:31:59 -04:00
Matt Nadareski
41bc410452 Pre-compress state files with Zstd 2025-10-09 10:01:24 -04:00
Matt Nadareski
bbfdf462d0 Fix broken file count tests 2025-10-08 12:54:18 -04:00
Matt Nadareski
5a1d51c05f Pre-compress skeleton files with Zstd 2025-10-08 12:48:50 -04:00
Matt Nadareski
16e80f75cf Add preemptive helper for Zstd handling 2025-10-08 12:04:04 -04:00
Matt Nadareski
2a6e066707 Add preemptive new file support 2025-10-08 11:53:19 -04:00
Matt Nadareski
d6102107fb Only allow skeleton creation for CD and DVD
This was a hard decision to limit again, but with the inability to make Zstd the default for log compression, this is not a reasonable thing to enable for most users.
2025-10-08 11:31:12 -04:00
Matt Nadareski
79163dcb35 Fix default value tests 2025-10-08 11:15:16 -04:00
Matt Nadareski
c1aa863c91 Allow skeleton creation for all media types 2025-10-08 11:10:13 -04:00
Matt Nadareski
dbd4b55dda Use ZipWriterOptions instead of generic 2025-10-08 10:59:51 -04:00
Matt Nadareski
1f58521f51 Fix incorrect flagging of a failed check 2025-10-08 10:54:20 -04:00
Matt Nadareski
4da1ab9c29 Guard against unzippable files 2025-10-08 10:35:28 -04:00
Matt Nadareski
5771add8c0 Revert "Use Deflate64 instead of Deflate for compression"
This reverts commit 977a71d9cf.
2025-10-08 10:20:10 -04:00
Matt Nadareski
977a71d9cf Use Deflate64 instead of Deflate for compression 2025-10-08 09:59:41 -04:00
Matt Nadareski
b2d09d04ea Use null or empty instead of just null 2025-10-08 09:49:22 -04:00
Matt Nadareski
78df92e5d3 More gracefully handle "missing" media types 2025-10-08 09:29:34 -04:00
Matt Nadareski
9770b7c917 Add more useful credentials inputs for Check 2025-10-07 20:37:11 -04:00
Matt Nadareski
1ddb287977 Minor cleanup on interactive modes 2025-10-07 20:27:22 -04:00
Matt Nadareski
b9e4bbf744 Finalize wire-through and clean up 2025-10-07 19:43:34 -04:00
Matt Nadareski
08829ed811 Fix minor issues with options loading 2025-10-07 18:27:12 -04:00
Matt Nadareski
bf181e2294 Start wiring through log compression changes 2025-10-07 18:07:46 -04:00
Matt Nadareski
49571c6bfc Use GC.SharpCompress as archive handling library 2025-10-07 17:33:01 -04:00
Matt Nadareski
9a0bc868f8 More consistency in commandline programs 2025-10-07 16:40:30 -04:00
Matt Nadareski
bc2c08690d Update packages 2025-10-07 16:21:02 -04:00
Matt Nadareski
493cb80624 Allow but do not require config for Check 2025-10-07 15:19:50 -04:00
Matt Nadareski
49b8ecf6c3 Fix minor typo in verify inputs check 2025-10-06 18:05:14 -04:00
Matt Nadareski
c9b7ad7819 Exit early on parsing failures 2025-10-06 17:53:15 -04:00
Matt Nadareski
fe76387f6a Remove CommandOptions implementations 2025-10-06 17:49:16 -04:00
Matt Nadareski
0a60fe5a37 Fix strange invocations of extension methods 2025-10-06 16:37:28 -04:00
Matt Nadareski
baffdb8b29 Remove duplicate input declarations 2025-10-06 16:34:19 -04:00
Matt Nadareski
4a3c585a8d Assign inputs for interactive modes 2025-10-06 16:25:41 -04:00
Matt Nadareski
1ee7ea1948 Create and use base feature in CLI 2025-10-06 16:13:51 -04:00
Matt Nadareski
ee08bfe0fc Create and use base feature in Check 2025-10-06 16:06:23 -04:00
Matt Nadareski
896caec9cd Create and use main features for CLI and Check 2025-10-06 15:54:14 -04:00
Matt Nadareski
68932ab473 Reduce unnecessary shared code 2025-10-06 15:25:11 -04:00
Matt Nadareski
d105d04146 Add placeholder command set creation 2025-10-06 11:01:25 -04:00
Matt Nadareski
7023c78d40 Minor cleanup around last added 2025-10-06 10:34:11 -04:00
Matt Nadareski
81156a3c63 Create interactive mode features 2025-10-06 10:32:16 -04:00
Matt Nadareski
7b2b06a36f Use CommandLine library for CLI executables 2025-10-06 10:11:44 -04:00
Matt Nadareski
3f974ab336 Update packages 2025-10-05 17:41:38 -04:00
Matt Nadareski
b238616685 Rename log zip on collision 2025-10-04 20:49:56 -04:00
Matt Nadareski
2984459823 Tweaks to how failure cases are reported 2025-10-04 18:57:25 -04:00
Matt Nadareski
ce979b3c3f Add failure if media type could not be determined 2025-10-02 22:06:22 -04:00
Matt Nadareski
cb8e1fd34b Bump version 2025-09-30 12:02:06 -04:00
Matt Nadareski
cc148735f8 Require exact versions for build 2025-09-30 11:18:59 -04:00
Matt Nadareski
8238d14f7b Fix missed package update 2025-09-30 10:55:57 -04:00
Matt Nadareski
a56676501e Fix starting index for CLI 2025-09-29 22:43:33 -04:00
Matt Nadareski
79716ea0b5 Bump version 2025-09-29 17:54:03 -04:00
Matt Nadareski
43477a133f Update BinaryObjectScanner to 3.4.3 2025-09-29 14:30:52 -04:00
Matt Nadareski
8889beef1d Reduction in media type use for dumping 2025-09-29 11:25:57 -04:00
Matt Nadareski
ac4be751b3 Make media type a named parameter for CLI 2025-09-29 10:56:56 -04:00
Matt Nadareski
69f855fc93 Fix tests 2025-09-29 10:45:53 -04:00
Matt Nadareski
cf01095623 Skip trying to set speeds if no drives 2025-09-29 10:43:12 -04:00
Matt Nadareski
3236223e3f Default to CD speed range 2025-09-29 10:40:59 -04:00
Matt Nadareski
36450cd22b Minor tweaks to frontend code 2025-09-29 10:25:24 -04:00
Deterous
6a9b6748d2 Remove SkipMediaTypeDetection option, cleanup options window (#895)
* Remove SkipMediaTypeDetection option, cleanup options window

* Shrink detection section in options

* always try to guess media type
2025-09-26 10:56:25 -04:00
Matt Nadareski
3bf83378a2 Fix logic from around Macrovision security driver filtering (fixes #811) 2025-09-25 11:38:03 -04:00
Matt Nadareski
bc938fd58c Return full result from dump checks 2025-09-25 10:54:29 -04:00
Matt Nadareski
f50a110acd Update media type visibility on system change 2025-09-25 09:26:11 -04:00
Matt Nadareski
fcda2a6e3b Change label with media type visibility 2025-09-25 09:07:41 -04:00
Matt Nadareski
d33526b27e Set media type visibility when options changed 2025-09-25 08:56:06 -04:00
Matt Nadareski
0b0427e9c1 Limit media type visibility further 2025-09-25 08:54:33 -04:00
Matt Nadareski
c34b92bad8 Default media type box to hidden to avoid visual issues 2025-09-25 08:49:04 -04:00
Matt Nadareski
89145df0fa Experiment with only showing media type box for DIC 2025-09-25 08:32:06 -04:00
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
300 changed files with 30392 additions and 10194 deletions

View File

@@ -1,4 +1,167 @@
[*.cs]
# top-most EditorConfig file
root = true
# SYSLIB1045: Convert to 'GeneratedRegexAttribute'.
dotnet_diagnostic.SYSLIB1045.severity = silent
# C# files
[*.cs]
# Indentation and spacing
charset = utf-8
indent_size = 4
indent_style = space
tab_width = 4
trim_trailing_whitespace = true
# New line preferences
end_of_line = lf
insert_final_newline = true
max_line_length = unset
# using directive preferences
csharp_using_directive_placement = outside_namespace
dotnet_diagnostic.IDE0005.severity = error
# Code-block preferences
csharp_style_namespace_declarations = block_scoped
csharp_style_prefer_method_group_conversion = true
csharp_style_prefer_top_level_statements = false
# Expression-level preferences
csharp_prefer_simple_default_expression = true
csharp_style_inlined_variable_declaration = true
csharp_style_unused_value_assignment_preference = discard_variable
csharp_style_unused_value_expression_statement_preference = discard_variable
dotnet_diagnostic.IDE0001.severity = warning
dotnet_diagnostic.IDE0002.severity = warning
dotnet_diagnostic.IDE0004.severity = warning
dotnet_diagnostic.IDE0010.severity = error
dotnet_diagnostic.IDE0051.severity = warning
dotnet_diagnostic.IDE0052.severity = warning
dotnet_diagnostic.IDE0072.severity = warning
dotnet_diagnostic.IDE0080.severity = warning
dotnet_diagnostic.IDE0100.severity = error
dotnet_diagnostic.IDE0110.severity = error
dotnet_diagnostic.IDE0120.severity = warning
dotnet_diagnostic.IDE0121.severity = warning
dotnet_diagnostic.IDE0240.severity = error
dotnet_diagnostic.IDE0241.severity = error
dotnet_style_coalesce_expression = true
dotnet_style_namespace_match_folder = false
dotnet_style_null_propagation = true
dotnet_style_prefer_auto_properties = true
dotnet_style_prefer_collection_expression = when_types_loosely_match
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_compound_assignment = true
csharp_style_prefer_simple_property_accessors = true
dotnet_style_prefer_simplified_interpolation = true
dotnet_style_prefer_simplified_boolean_expressions = true
csharp_style_prefer_unbound_generic_type_in_nameof = true
# Field preferences
dotnet_diagnostic.IDE0044.severity = warning
dotnet_style_readonly_field = true
# Language keyword vs. framework types preferences
dotnet_diagnostic.IDE0049.severity = error
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true
# Modifier preferences
csharp_prefer_static_local_function = true
csharp_style_prefer_readonly_struct = true
dotnet_diagnostic.IDE0036.severity = warning
dotnet_diagnostic.IDE0040.severity = error
dotnet_diagnostic.IDE0380.severity = error
dotnet_style_require_accessibility_modifiers = always
# New-line preferences
dotnet_diagnostic.IDE2000.severity = warning
dotnet_diagnostic.IDE2002.severity = warning
dotnet_diagnostic.IDE2003.severity = warning
dotnet_diagnostic.IDE2004.severity = warning
dotnet_diagnostic.IDE2005.severity = warning
dotnet_diagnostic.IDE2006.severity = warning
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = false
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = false
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = false
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = false
dotnet_style_allow_multiple_blank_lines_experimental = false
dotnet_style_allow_statement_immediately_after_block_experimental = false
# Null-checking preferences
csharp_style_conditional_delegate_call = true
# Parameter preferences
dotnet_code_quality_unused_parameters = all
dotnet_diagnostic.IDE0280.severity = error
# Parentheses preferences
dotnet_diagnostic.IDE0047.severity = warning
dotnet_diagnostic.IDE0048.severity = warning
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_operators = always_for_clarity
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
# Pattern-matching preferences
dotnet_diagnostic.IDE0019.severity = warning
dotnet_diagnostic.IDE0020.severity = warning
dotnet_diagnostic.IDE0038.severity = warning
dotnet_diagnostic.IDE0066.severity = none
dotnet_diagnostic.IDE0083.severity = warning
dotnet_diagnostic.IDE0260.severity = warning
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = true
# this. and Me. preferences
dotnet_style_qualification_for_event = false
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false
dotnet_style_qualification_for_property = false
# var preferences
csharp_style_var_for_built_in_types = false
csharp_style_var_when_type_is_apparent = true
# .NET formatting options
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = true
# C# formatting options
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = false
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false

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 10 running on (Operating System)
**Describe the issue**
A clear and concise description of what the bug is.

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

@@ -0,0 +1,59 @@
name: Build and Test
on:
push:
branches: ["master"]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@v1.3.1
with:
dotnet: false
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x
- name: Run tests
run: dotnet test
- name: Run publish script
run: ./publish-nix.sh -dp
- name: Update rolling tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -f rolling
git push origin :refs/tags/rolling || true
git push origin rolling --force
- name: Upload to rolling
uses: ncipollo/release-action@v1.20.0
with:
allowUpdates: True
artifacts: "*.nupkg,*.snupkg,*.zip"
body: "Last built commit: ${{ github.sha }}
## UI Builds
[Windows x64 UI Release](https://github.com/SabreTools/MPF/releases/download/rolling/MPF.UI_net10.0-windows_win-x64_release.zip)
[Windows x64 UI Debug](https://github.com/SabreTools/MPF/releases/download/rolling/MPF.UI_net10.0-windows_win-x64_debug.zip)"
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

@@ -3,21 +3,29 @@ name: Build PR
on: [pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
build:
runs-on: ubuntu-latest
steps:
- name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@v1.3.1
with:
dotnet: false
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
- uses: actions/checkout@v5
- name: Restore dependencies
run: dotnet restore
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x
- name: Build
run: dotnet build --no-restore
- name: Restore dependencies
run: dotnet restore
- name: Test
run: dotnet test --no-restore --verbosity normal
- name: Build
run: dotnet build
- name: Test
run: dotnet test

1
.gitignore vendored
View File

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

32
.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/net10.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,34 @@
"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/net10.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 Launch (UI)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/MPF.UI/bin/Debug/net10.0-windows/MPF.dll",
"args": [],
"cwd": "${workspaceFolder}/MPF.UI",
// 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,533 @@
### WIP (xxxx-xx-xx)
- Check for null or empty layerbreak arrays
- Check range-specific values in layerbreak
- Update Redumper to build 671
- Update Redumper to build 676 (rename asus flags to mediatek)
- Use default media type if not provided
- Update Redumper to build 682 (Dreamcast support)
- Add default subfolder to CLI outputs
- Update DIC to 20260101
- Update Redumper to build 683
- Add editorconfig, fix issues
- Add MPRESS to packer filters
- Update RedumpLib to 1.9.1
- Split path creation in OptionsLoader
- Add unused Dreamdump execution context
- Validate a system is provided for CLI applications
- Log the system being used, in case it came from config
- Extend logging with a new state
### 3.6.0 (2025-11-28)
- Add BCA to list of files to select in Check UI
- Move Zstd compression helper to base processor
- Add file merge method in CleanRip
- Implement file merging in CleanRip
- Support reparsing of MPF-processed outputs
- Use WriteToFile extension for zip entries
- Move output file implementations to separate namespace
- Conditionally require state
- Replace "We" comments
- Parse XboxOne/SX Title IDs
- Reduce chance of empty strings
- Initial UI lang code
- Move to enum-based model for UI language options
- Slight cleanup to main window language handling
- Clean up nullability
- Clean up a bit of element work
- Fill in some gaps
- Add UI by default to launch
- Continue UI translation work
- Minor formatting cleanup
- Ensure readers and writers dispose
- Complete translation support
- Add skeleton for 8 more languages
- Add 8 more language menu items
- Add basic translation for 8 more languages
- Make Check flags toggle if config used
- Add console print when Check loads from config
- Add Ukrainian translation (superg)
- Add list configuration commandline feature
- Handle a provided log zip in Check operation
- Allow files to be zipped but not deleted
- Fix long translated strings
- Allow placeholder files to be used in Check
- Make .img not required for DIC outputs
- Handle log zip path in information extraction
- Redumper state file is not accessed
- Fix tests broken by last commit
- Regex outputs should not name based on pattern
- Make log archive handling more user-apparent
- Be selective on which systems to enable skeleton
- Readme updates for accuracy
- Separate out default program paths
- Add per-OS executable names
- Simplify first-run CLI experience
- Change CLI first-run wording
- Free disk space for runners
- Possibly fix missing options string
- Minor Spanish cleanup
- Update RedumpLib to 1.8.0
- Limit visibility of Compatible OS text box
- Stop using long name for default system setting
- Updated Polish translation
- Fix rolling tag
- Try to add UI links to rolling release
- Slight tweak to automatic UI links
- Add hidden language (NovaAurora)
- Fix langs
- Fix issues with path assembly
- Clean up submission info use and log link text
- Update Redumper to build 658
- Pre-compress all skeletons for multi-track CDs
- Add DVD-Video to list of copy protection scanning systems
- Update Redumper to build 660
- Use dated default output filenames
- Update packages
- Scan disc image if not multi-track
- Scan multi-track images for protection
- Move and rename new protection scan method
- Path scan after image scan
- Update Redumper to build 663
- Name some type parameters
- Ensure volume label is trimmed if used in filenames
- Remove DPM identifier for StarForce Keyless
- New Redumper Drive Pregap Start option
- Clarify the unmounted device case
- Add support for .NET 10
- Update Redumper to build 665
- Disable Zstd PKZIP outputs
- Add commented fix for Zstd PKZIP
- Reenable Zstd PKZIP outputs
### 3.5.0 (2025-10-10)
- Add failure if media type could not be determined
- Tweaks to how failure cases are reported
- Rename log zip on collision
- Update packages
- Use CommandLine library for CLI executables
- Create interactive mode features
- Minor cleanup around last added
- Add placeholder command set creation
- Reduce unnecessary shared code
- Create and use main features for CLI and Check
- Create and use base feature in Check
- Create and use base feature in CLI
- Assign inputs for interactive modes
- Remove duplicate input declarations
- Fix strange invocations of extension methods
- Remove CommandOptions implementations
- Exit early on parsing failures
- Fix minor typo in verify inputs check
- Allow but do not require config for Check
- Update packages
- More consistency in commandline programs
- Use GC.SharpCompress as archive handling library
- Start wiring through log compression changes
- Fix minor issues with options loading
- Finalize wire-through and clean up
- Minor cleanup on interactive modes
- Add more useful credentials inputs for Check
- More gracefully handle "missing" media types
- Use null or empty instead of just null
- Guard against unzippable files
- Fix incorrect flagging of a failed check
- Use ZipWriterOptions instead of generic
- Allow skeleton creation for all media types
- Fix default value tests
- Only allow skeleton creation for CD and DVD
- Add preemptive new file support
- Add preemptive helper for Zstd handling
- Pre-compress skeleton files with Zstd
- Fix broken file count tests
- Pre-compress state files with Zstd
- Use block-based reading instead of CopyTo
- Enable skeleton output for all CLI runs
- Fix test broken by last commit
- Support detecting split Wii for CleanRip
- Try to handle Windows-specific compression issue
### 3.4.2 (2025-09-30)
- Fix starting index for CLI
- Fix missed package update
- Require exact versions for build
### 3.4.1 (2025-09-29)
- Experiment with only showing media type box for DIC
- Default media type box to hidden to avoid visual issues
- Limit media type visibility further
- Set media type visibility when options changed
- Change label with media type visibility
- Update media type visibility on system change
- Return full result from dump checks
- Fix logic from around Macrovision security driver filtering
- Remove SkipMediaTypeDetection option, cleanup options window
- Minor tweaks to frontend code
- Default to CD speed range
- Skip trying to set speeds if no drives
- Fix tests
- Make media type a named parameter for CLI
- Reduction in media type use for dumping
- Update BinaryObjectScanner to 3.4.3
### 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 +691,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 +767,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 +807,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 +883,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 +909,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 +928,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 +948,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 +1009,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 +1029,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 +1143,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 +1172,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 +1201,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 +1210,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 +1218,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 +1240,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 +1276,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 +1284,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 +1310,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 +1359,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

@@ -0,0 +1,315 @@
using System;
using System.IO;
#if NET40
using System.Threading.Tasks;
#endif
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
using LogCompression = MPF.Processors.LogCompression;
namespace MPF.CLI.Features
{
internal abstract class BaseFeature : SabreTools.CommandLine.Feature
{
#region Properties
/// <summary>
/// User-defined options
/// </summary>
public Options Options { get; protected set; }
/// <summary>
/// Currently-selected system
/// </summary>
public RedumpSystem? System { get; protected set; }
/// <summary>
/// Media type to dump
/// </summary>
/// <remarks>Required for DIC and if custom parameters not set</remarks>
public MediaType? MediaType { get; protected set; }
/// <summary>
/// Path to the device to dump
/// </summary>
/// <remarks>Required if custom parameters are not set</remarks>
public string? DevicePath { get; protected set; }
/// <summary>
/// Path to the mounted filesystem to check
/// </summary>
/// <remarks>Should only be used when the device path is not readable</remarks>
public string? MountedPath { get; protected set; }
/// <summary>
/// Path to the output file
/// </summary>
/// <remarks>Required if custom parameters are not set</remarks>
public string? FilePath { get; protected set; }
/// <summary>
/// Override drive speed
/// </summary>
public int? DriveSpeed { get; protected set; }
/// <summary>
/// Custom parameters for dumping
/// </summary>
public string? CustomParams { get; protected set; }
#endregion
protected BaseFeature(string name, string[] flags, string description, string? detailed = null)
: base(name, flags, description, detailed)
{
Options = new Options()
{
// Internal Program
InternalProgram = InternalProgram.NONE,
// Extra Dumping Options
ScanForProtection = false,
AddPlaceholders = true,
PullAllInformation = false,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
IncludeArtifacts = false,
CompressLogFiles = false,
LogCompression = LogCompression.DeflateMaximum,
DeleteUnnecessaryFiles = false,
CreateIRDAfterDumping = false,
// Protection Scanning Options
ScanArchivesForProtection = true,
IncludeDebugProtectionInformation = false,
HideDriveLetters = false,
// Redump Login Information
RetrieveMatchInformation = true,
RedumpUsername = null,
RedumpPassword = null,
};
}
/// <inheritdoc/>
public override bool Execute()
{
// Validate a system type is provided
if (System == null)
{
Console.Error.WriteLine("A system name needs to be provided");
return false;
}
// Log the system being used, in case it came from config
Console.WriteLine($"Using system: {System.LongName()}");
// 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);
}
// Validate the internal program
#pragma warning disable IDE0010
switch (Options.InternalProgram)
{
case InternalProgram.Aaru:
if (!File.Exists(Options.AaruPath))
{
Console.Error.WriteLine("A path needs to be supplied in config.json for Aaru, exiting...");
return false;
}
break;
case InternalProgram.DiscImageCreator:
if (!File.Exists(Options.DiscImageCreatorPath))
{
Console.Error.WriteLine("A path needs to be supplied in config.json for DIC, exiting...");
return false;
}
break;
// case InternalProgram.Dreamdump:
// if (!File.Exists(Options.DreamdumpPath))
// {
// Console.Error.WriteLine("A path needs to be supplied in config.json for Dreamdump, exiting...");
// return false;
// }
// break;
case InternalProgram.Redumper:
if (!File.Exists(Options.RedumperPath))
{
Console.Error.WriteLine("A path needs to be supplied in config.json for Redumper, exiting...");
return false;
}
break;
default:
Console.Error.WriteLine($"{Options.InternalProgram} is not a supported dumping program, exiting...");
break;
}
#pragma warning restore IDE0010
// Ensure we have the values we need
if (CustomParams is null && DevicePath is null)
{
Console.Error.WriteLine("Either custom parameters or a device path need to be provided, exiting...");
return false;
}
if (Options.InternalProgram == InternalProgram.DiscImageCreator
&& CustomParams is null
&& (MediaType is null || MediaType == SabreTools.RedumpLib.Data.MediaType.NONE))
{
Console.Error.WriteLine("Media type is required for DiscImageCreator, exiting...");
return false;
}
// If no media type is provided, use a default
if (CustomParams is null && (MediaType is null || MediaType == SabreTools.RedumpLib.Data.MediaType.NONE))
{
// Get reasonable default values based on the current system
var mediaTypes = System.MediaTypes();
MediaType = mediaTypes.Count > 0 ? mediaTypes[0] : SabreTools.RedumpLib.Data.MediaType.CDROM;
if (MediaType == SabreTools.RedumpLib.Data.MediaType.NONE)
MediaType = SabreTools.RedumpLib.Data.MediaType.CDROM;
Console.WriteLine($"No media type was provided, using {MediaType.LongName()}");
}
// Normalize the file path
if (DevicePath is not null && FilePath is null)
{
string defaultFileName = $"track_{DateTime.Now:yyyyMMdd-HHmm}";
FilePath = Path.Combine(defaultFileName, $"{defaultFileName}.bin");
if (Options.DefaultOutputPath is not null)
FilePath = Path.Combine(Options.DefaultOutputPath, FilePath);
}
if (FilePath is not null)
FilePath = FrontendTool.NormalizeOutputPaths(FilePath, getFullPath: true);
// Get the speed from the options
int speed = DriveSpeed ?? FrontendTool.GetDefaultSpeedForMediaType(MediaType, Options);
// Populate an environment
var drive = Drive.Create(null, DevicePath ?? string.Empty);
var env = new DumpEnvironment(Options,
FilePath,
drive,
System,
Options.InternalProgram);
env.SetExecutionContext(MediaType, null);
env.SetProcessor();
// Process the parameters
string? paramStr = CustomParams ?? env.GetFullParameters(MediaType, speed);
if (string.IsNullOrEmpty(paramStr))
{
Console.Error.WriteLine("No valid environment could be created, exiting...");
return false;
}
env.SetExecutionContext(MediaType, paramStr);
// Invoke the dumping program
Console.WriteLine($"Invoking {Options.InternalProgram} using '{paramStr}'");
var dumpResult = env.Run(MediaType).GetAwaiter().GetResult();
Console.WriteLine(dumpResult.Message);
if (dumpResult == false)
return false;
// If it was not a dumping command
if (!env.IsDumpingCommand())
{
Console.Error.WriteLine();
Console.WriteLine("Execution not recognized as dumping command, skipping processing...");
return true;
}
// If we have a mounted path, replace the environment
if (MountedPath is not null && Directory.Exists(MountedPath))
{
drive = Drive.Create(null, MountedPath);
env = new DumpEnvironment(Options,
FilePath,
drive,
System,
internalProgram: null);
env.SetExecutionContext(MediaType, null);
env.SetProcessor();
}
// Finally, attempt to do the output dance
var verifyResult = env.VerifyAndSaveDumpOutput()
.ConfigureAwait(false).GetAwaiter().GetResult();
Console.WriteLine(verifyResult.Message);
return true;
}
/// <summary>
/// Display help for MPF.CLI
/// </summary>
/// <param name="error">Error string to prefix the help text with</param>
public static void DisplayHelp()
{
Console.WriteLine("Usage:");
Console.WriteLine("MPF.CLI <system> [options]");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
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("lo, listconfig List current configuration values");
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 configured dumping program name");
Console.WriteLine("-t, --mediatype <mediatype> Set media type for dumping (Required for DIC)");
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 (Recommended, uses defaults otherwise)");
Console.WriteLine("-s, --speed <speed> Override default dumping speed");
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, a drive path is required.");
Console.WriteLine();
Console.WriteLine("Mounted filesystem path is only recommended on OSes that require block");
Console.WriteLine("device dumping, usually Linux and macOS.");
Console.WriteLine();
}
}
}

View File

@@ -0,0 +1,196 @@
using System;
using System.IO;
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib.Data;
namespace MPF.CLI.Features
{
internal sealed class InteractiveFeature : BaseFeature
{
#region Feature Definition
public const string DisplayName = "interactive";
private static readonly string[] _flags = ["i", "interactive"];
private const string _description = "Enable interactive mode";
#endregion
public InteractiveFeature()
: base(DisplayName, _flags, _description)
{
}
/// <inheritdoc/>
public override bool ProcessArgs(string[] args, int index)
{
// Cache all args as inputs
for (int i = 1; i < args.Length; i++)
{
Inputs.Add(args[i]);
}
// Read the options from config, if possible
Options = OptionsLoader.LoadFromConfig();
// Create return values
MediaType = SabreTools.RedumpLib.Data.MediaType.NONE;
string defaultFileName = $"track_{DateTime.Now:yyyyMMdd-HHmm}";
#if NET20 || NET35
FilePath = Path.Combine(Options.DefaultOutputPath ?? "ISO", Path.Combine(defaultFileName, $"{defaultFileName}.bin"));
#else
FilePath = Path.Combine(Options.DefaultOutputPath ?? "ISO", defaultFileName, $"{defaultFileName}.bin");
#endif
System = Options.DefaultSystem;
// Create state values
string? result;
root:
Console.Clear();
Console.WriteLine("MPF.CLI 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 media type (Currently '{MediaType}')");
Console.WriteLine($"4) Set device path (Currently '{DevicePath}')");
Console.WriteLine($"5) Set mounted path (Currently '{MountedPath}')");
Console.WriteLine($"6) Set file path (Currently '{FilePath}')");
Console.WriteLine($"7) Set override speed (Currently '{DriveSpeed}')");
Console.WriteLine($"8) Set custom parameters (Currently '{CustomParams}')");
Console.WriteLine();
Console.WriteLine($"Q) Exit the program");
Console.WriteLine($"X) Start dumping");
Console.Write("> ");
result = Console.ReadLine();
switch (result)
{
case "1":
goto system;
case "2":
goto dumpingProgram;
case "3":
goto mediaType;
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;
}
system:
Console.WriteLine();
Console.WriteLine("For possible inputs, use the List Systems commandline option");
Console.WriteLine();
Console.WriteLine("Input the system and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
System = result.ToRedumpSystem();
goto root;
dumpingProgram:
Console.WriteLine();
Console.WriteLine("Options:");
Console.WriteLine($"{InternalProgram.Redumper.ToString().ToLowerInvariant(),-15} => {InternalProgram.Redumper.LongName()}");
Console.WriteLine($"{InternalProgram.DiscImageCreator.ToString().ToLowerInvariant(),-15} => {InternalProgram.DiscImageCreator.LongName()}");
Console.WriteLine($"{InternalProgram.Aaru.ToString().ToLowerInvariant(),-15} => {InternalProgram.Aaru.LongName()}");
// Console.WriteLine($"{InternalProgram.Dreamdump.ToString().ToLowerInvariant(),-15} => {InternalProgram.Dreamdump.LongName()}");
Console.WriteLine();
Console.WriteLine("Input the dumping program and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
Options.InternalProgram = result.ToInternalProgram();
goto root;
mediaType:
Console.WriteLine();
Console.WriteLine("For possible inputs, use the List Media commandline option");
Console.WriteLine();
Console.WriteLine("Input the media type and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
MediaType = OptionsLoader.ToMediaType(result);
goto root;
devicePath:
Console.WriteLine();
Console.WriteLine("Input the device path and press Enter:");
Console.Write("> ");
DevicePath = Console.ReadLine();
goto root;
mountedPath:
Console.WriteLine();
Console.WriteLine("Input the mounted path and press Enter:");
Console.Write("> ");
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!);
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;
DriveSpeed = speed;
goto root;
customParams:
Console.WriteLine();
Console.WriteLine("Input the custom parameters and press Enter:");
Console.Write("> ");
CustomParams = Console.ReadLine();
goto root;
exit:
return true;
}
/// <inheritdoc/>
public override bool VerifyInputs() => true;
}
}

View File

@@ -0,0 +1,115 @@
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.CommandLine.Inputs;
using SabreTools.RedumpLib.Data;
namespace MPF.CLI.Features
{
internal sealed class MainFeature : BaseFeature
{
#region Feature Definition
public const string DisplayName = "main";
/// <remarks>Flags are unused</remarks>
private static readonly string[] _flags = [];
/// <remarks>Description is unused</remarks>
private const string _description = "";
#endregion
#region Inputs
private const string _customName = "custom";
internal readonly StringInput CustomInput = new(_customName, ["-c", "--custom"], "Custom parameters to use");
private const string _deviceName = "device";
internal readonly StringInput DeviceInput = new(_deviceName, ["-d", "--device"], "Physical drive path (Required if no custom parameters set)");
private const string _fileName = "file";
internal readonly StringInput FileInput = new(_fileName, ["-f", "--file"], "Output file path (Required if no custom parameters set)");
private const string _mediaTypeName = "media-type";
internal readonly StringInput MediaTypeInput = new(_mediaTypeName, ["-t", "--mediatype"], "Set media type for dumping (Required for DIC)");
private const string _mountedName = "mounted";
internal readonly StringInput MountedInput = new(_mountedName, ["-m", "--mounted"], "Mounted filesystem path for additional checks");
private const string _speedName = "speed";
internal readonly Int32Input SpeedInput = new(_speedName, ["-s", "--speed"], "Override default dumping speed");
private const string _useName = "use";
internal readonly StringInput UseInput = new(_useName, ["-u", "--use"], "Override configured dumping program name");
#endregion
public MainFeature()
: base(DisplayName, _flags, _description)
{
Add(UseInput);
Add(MediaTypeInput);
Add(DeviceInput);
Add(MountedInput);
Add(FileInput);
Add(SpeedInput);
Add(CustomInput);
}
/// <inheritdoc/>
public override bool ProcessArgs(string[] args, int index)
{
// If we have no arguments, just return
if (args is null || args.Length == 0)
return true;
// Read the options from config, if possible
Options = OptionsLoader.LoadFromConfig();
// The first argument is the system type
System = args[0].Trim('"').ToRedumpSystem();
// Loop through the arguments and parse out values
for (index = 1; index < args.Length; index++)
{
// Use specific program
if (UseInput.ProcessInput(args, ref index))
Options.InternalProgram = UseInput.Value.ToInternalProgram();
// Set a media type
else if (MediaTypeInput.ProcessInput(args, ref index))
MediaType = OptionsLoader.ToMediaType(MediaTypeInput.Value?.Trim('"'));
// Use a device path
else if (DeviceInput.ProcessInput(args, ref index))
DevicePath = DeviceInput.Value;
// Use a mounted path for physical checks
else if (MountedInput.ProcessInput(args, ref index))
MountedPath = MountedInput.Value;
// Use a file path
else if (FileInput.ProcessInput(args, ref index))
FilePath = FileInput.Value;
// Set an override speed
else if (SpeedInput.ProcessInput(args, ref index))
DriveSpeed = SpeedInput.Value;
// Use a custom parameters
else if (CustomInput.ProcessInput(args, ref index))
CustomParams = CustomInput.Value;
// Default, add to inputs
else
Inputs.Add(args[index]);
}
return true;
}
/// <inheritdoc/>
public override bool VerifyInputs() => true;
}
}

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;net10.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.6.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,20 @@
<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`)) OR $(TargetFramework.StartsWith(`net10`))">
<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;net10.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.CommandLine" Version="[1.4.0]" />
<PackageReference Include="SabreTools.RedumpLib" Version="[1.9.1]" />
</ItemGroup>
</Project>

View File

@@ -1,13 +1,13 @@
using System;
using System.IO;
using System.Collections.Generic;
#if NET40
using System.Threading.Tasks;
#endif
using BinaryObjectScanner;
using MPF.Frontend;
using MPF.CLI.Features;
using MPF.Frontend.Features;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
using SabreTools.CommandLine;
using SabreTools.CommandLine.Features;
namespace MPF.CLI
{
@@ -19,332 +19,125 @@ namespace MPF.CLI
var options = OptionsLoader.LoadFromConfig();
if (options.FirstRun)
{
// Application paths
options.AaruPath = "FILL ME IN";
options.DiscImageCreatorPath = "FILL ME IN";
options.RedumperPath = "FILL ME IN";
options.InternalProgram = InternalProgram.NONE;
// Reset first run
options.FirstRun = false;
OptionsLoader.SaveToConfig(options, saveDefault: true);
OptionsLoader.SaveToConfig(options);
// Display non-error message
Console.WriteLine("First-run detected! Please verify the generated config.json and run again.");
return;
}
// Create the command set
var mainFeature = new MainFeature();
var commandSet = CreateCommands(mainFeature);
// If we have no args, show the help and quit
if (args is null || args.Length == 0)
{
BaseFeature.DisplayHelp();
return;
}
// Get the first argument as a feature flag
string featureName = args[0];
// Try processing the standalone arguments
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
if (standaloneProcessed != false)
var topLevel = commandSet.GetTopLevel(featureName);
switch (topLevel)
{
if (standaloneProcessed == null)
DisplayHelp();
return;
}
// Standalone Options
case Help: BaseFeature.DisplayHelp(); return;
case VersionFeature version: version.Execute(); return;
case ListCodesFeature lc: lc.Execute(); return;
case ListConfigFeature lc: lc.Execute(); return;
case ListMediaTypesFeature lm: lm.Execute(); return;
case ListProgramsFeature lp: lp.Execute(); return;
case ListSystemsFeature ls: ls.Execute(); return;
// Try processing the common arguments
bool success = OptionsLoader.ProcessCommonArguments(args, out MediaType mediaType, out RedumpSystem? knownSystem, out var error);
if (!success)
{
DisplayHelp(error);
return;
}
// Validate the supplied credentials
bool? validated = RedumpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).GetAwaiter().GetResult();
string message = validated switch
{
true => "Redump username and password accepted!",
false => "Redump username and password denied!",
null => "An error occurred validating your credentials!",
};
if (!string.IsNullOrEmpty(message))
Console.WriteLine(message);
// Process any custom parameters
int startIndex = 2;
CommandOptions opts = LoadFromArguments(args, options, ref startIndex);
// Validate the internal program
switch (options.InternalProgram)
{
case InternalProgram.Aaru:
if (!File.Exists(options.AaruPath))
// Interactive Mode
case InteractiveFeature interactive:
if (!interactive.ProcessArgs(args, 0))
{
DisplayHelp("A path needs to be supplied for Aaru, exiting...");
BaseFeature.DisplayHelp();
return;
}
break;
case InternalProgram.DiscImageCreator:
if (!File.Exists(options.DiscImageCreatorPath))
if (!interactive.Execute())
{
DisplayHelp("A path needs to be supplied for DIC, exiting...");
return;
}
break;
case InternalProgram.Redumper:
if (!File.Exists(options.RedumperPath))
{
DisplayHelp("A path needs to be supplied for Redumper, exiting...");
BaseFeature.DisplayHelp();
return;
}
break;
// Default Behavior
default:
DisplayHelp($"{options.InternalProgram} is not a supported dumping program, exiting...");
if (!mainFeature.ProcessArgs(args, 0))
{
BaseFeature.DisplayHelp();
return;
}
if (!mainFeature.Execute())
{
BaseFeature.DisplayHelp();
return;
}
break;
}
// Ensure we have the values we need
if (opts.CustomParams == null && (opts.DevicePath == null || opts.FilePath == null))
{
DisplayHelp("Both a device path and file path need to be supplied, exiting...");
return;
}
// Get the speed from the options
int speed = opts.DriveSpeed ?? FrontendTool.GetDefaultSpeedForMediaType(mediaType, options);
// Populate an environment
var drive = Drive.Create(null, opts.DevicePath ?? string.Empty);
var env = new DumpEnvironment(options,
opts.FilePath,
drive,
knownSystem,
mediaType,
options.InternalProgram,
parameters: null);
// Process the parameters
string? paramStr = opts.CustomParams ?? env.GetFullParameters(speed);
if (string.IsNullOrEmpty(paramStr))
{
DisplayHelp("No valid environment could be created, exiting...");
return;
}
env.SetExecutionContext(paramStr);
// Make new Progress objects
var resultProgress = new Progress<ResultEventArgs>();
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
// Invoke the dumping program
Console.WriteLine($"Invoking {options.InternalProgram} using '{paramStr}'");
var dumpResult = env.Run(resultProgress).GetAwaiter().GetResult();
Console.WriteLine(dumpResult.Message);
if (!dumpResult)
return;
// If it was not a dumping command
if (!env.IsDumpingCommand())
{
Console.WriteLine("Execution not recognized as dumping command, skipping processing...");
return;
}
// If we have a mounted path, replace the environment
if (opts.MountedPath != null && Directory.Exists(opts.MountedPath))
{
drive = Drive.Create(null, opts.MountedPath);
env = new DumpEnvironment(options,
opts.FilePath,
drive,
knownSystem,
mediaType,
internalProgram: null,
parameters: null);
}
// Finally, attempt to do the output dance
#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
Console.WriteLine(verifyResult.Message);
}
/// <summary>
/// Display help for MPF.CLI
/// Create the command set for the program
/// </summary>
/// <param name="error">Error string to prefix the help text with</param>
private static void DisplayHelp(string? error = null)
private static CommandSet CreateCommands(MainFeature mainFeature)
{
if (error != null)
Console.WriteLine(error);
List<string> header = [
"MPF.CLI [standalone|system] [options] <path> ...",
string.Empty,
];
Console.WriteLine("Usage:");
Console.WriteLine("MPF.CLI <mediatype> <system> [options]");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
Console.WriteLine("-lc, --listcodes List supported comment/content site codes");
Console.WriteLine("-lm, --listmedia List supported media types");
Console.WriteLine("-ls, --listsystems List supported system types");
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");
Console.WriteLine();
List<string> footer = [
string.Empty,
"Dumping program paths and other settings can be found in the config.json file",
"generated next to the program by default. Ensure that all settings are to user",
"preference before running MPF.CLI.",
string.Empty,
Console.WriteLine("CLI Options:");
Console.WriteLine("-u, --use <program> Override default dumping program");
Console.WriteLine("-d, --device <devicepath> Physical drive path (Required if no custom parameters set)");
Console.WriteLine("-m, --mounted <dirpath> Mounted filesystem path for additional checks");
Console.WriteLine("-f, --file \"<filepath>\" Output file path (Required if no custom parameters set)");
Console.WriteLine("-s, --speed <speed> Override default dumping speed");
Console.WriteLine("-c, --custom \"<params>\" Custom parameters to use");
Console.WriteLine();
"Custom dumping parameters, if used, will fully replace the default parameters.",
"All dumping parameters need to be supplied if doing this.",
"Otherwise, both a drive path and output file path are required.",
string.Empty,
Console.WriteLine("Custom dumping parameters, if used, will fully replace the default parameters.");
Console.WriteLine("All dumping parameters need to be supplied if doing this.");
Console.WriteLine("Otherwise, both a drive path and output file path are required.");
Console.WriteLine();
"Mounted filesystem path is only recommended on OSes that require block",
"device dumping, usually Linux and macOS.",
string.Empty,
];
Console.WriteLine("Mounted filesystem path is only recommended on OSes that require block");
Console.WriteLine("device dumping, usually Linux and macOS.");
Console.WriteLine();
}
var commandSet = new CommandSet(header, footer);
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
private static CommandOptions LoadFromArguments(string[] args, Frontend.Options options, ref int startIndex)
{
// Create return values
var opts = new CommandOptions();
// Standalone Options
commandSet.Add(new Help());
commandSet.Add(new VersionFeature());
commandSet.Add(new ListCodesFeature());
commandSet.Add(new ListConfigFeature());
commandSet.Add(new ListMediaTypesFeature());
commandSet.Add(new ListSystemsFeature());
commandSet.Add(new ListProgramsFeature());
commandSet.Add(new InteractiveFeature());
// If we have no arguments, just return
if (args == null || args.Length == 0)
{
startIndex = 0;
return opts;
}
// CLI Options
commandSet.Add(mainFeature.UseInput);
commandSet.Add(mainFeature.MediaTypeInput);
commandSet.Add(mainFeature.DeviceInput);
commandSet.Add(mainFeature.MountedInput);
commandSet.Add(mainFeature.FileInput);
commandSet.Add(mainFeature.SpeedInput);
commandSet.Add(mainFeature.CustomInput);
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
return opts;
// Loop through the arguments and parse out values
for (; startIndex < args.Length; startIndex++)
{
// Use specific program
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
{
string internalProgram = args[startIndex].Split('=')[1];
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
startIndex++;
}
// Use a device path
else if (args[startIndex].StartsWith("-d=") || args[startIndex].StartsWith("--device="))
{
opts.DevicePath = args[startIndex].Split('=')[1].Trim('"');
}
else if (args[startIndex] == "-d" || args[startIndex] == "--device")
{
opts.DevicePath = args[startIndex + 1].Trim('"');
startIndex++;
}
// Use a mounted path for physical checks
else if (args[startIndex].StartsWith("-m=") || args[startIndex].StartsWith("--mounted="))
{
opts.MountedPath = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-m" || args[startIndex] == "--mounted")
{
opts.MountedPath = args[startIndex + 1];
startIndex++;
}
// Use a file path
else if (args[startIndex].StartsWith("-f=") || args[startIndex].StartsWith("--file="))
{
opts.FilePath = args[startIndex].Split('=')[1].Trim('"');
}
else if (args[startIndex] == "-f" || args[startIndex] == "--file")
{
opts.FilePath = args[startIndex + 1].Trim('"');
startIndex++;
}
// Set an override speed
else if (args[startIndex].StartsWith("-s=") || args[startIndex].StartsWith("--speed="))
{
if (!int.TryParse(args[startIndex].Split('=')[1].Trim('"'), out int speed))
speed = -1;
opts.DriveSpeed = speed;
}
else if (args[startIndex] == "-s" || args[startIndex] == "--speed")
{
if (!int.TryParse(args[startIndex + 1].Trim('"'), out int speed))
speed = -1;
opts.DriveSpeed = speed;
startIndex++;
}
// Use a custom parameters
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--custom="))
{
opts.CustomParams = args[startIndex].Split('=')[1].Trim('"');
}
else if (args[startIndex] == "-c" || args[startIndex] == "--custom")
{
opts.CustomParams = args[startIndex + 1].Trim('"');
startIndex++;
}
// Default, we fall out
else
{
break;
}
}
return opts;
}
/// <summary>
/// Represents commandline options
/// </summary>
private class CommandOptions
{
/// <summary>
/// Path to the device to dump
/// </summary>
/// <remarks>Required if custom parameters are not set</remarks>
public string? DevicePath { get; set; } = null;
/// <summary>
/// Path to the mounted filesystem to check
/// </summary>
/// <remarks>Should only be used when the device path is not readable</remarks>
public string? MountedPath { get; set; } = null;
/// <summary>
/// Path to the output file
/// </summary>
/// <remarks>Required if custom parameters are not set</remarks>
public string? FilePath { get; set; } = null;
/// <summary>
/// Override drive speed
/// </summary>
public int? DriveSpeed { get; set; } = null;
/// <summary>
/// Custom parameters for dumping
/// </summary>
public string? CustomParams { get; set; } = null;
return commandSet;
}
}
}

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

@@ -0,0 +1,187 @@
using System;
using System.IO;
#if NET40
using System.Threading.Tasks;
#endif
using MPF.Frontend;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
using LogCompression = MPF.Processors.LogCompression;
namespace MPF.Check.Features
{
internal abstract class BaseFeature : SabreTools.CommandLine.Feature
{
#region Properties
/// <summary>
/// User-defined options
/// </summary>
public Options Options { get; protected set; }
/// <summary>
/// Currently-selected system
/// </summary>
public RedumpSystem? System { get; protected set; }
/// <summary>
/// Seed submission info from an input file
/// </summary>
public SubmissionInfo? Seed { get; protected set; }
/// <summary>
/// Path to the device to scan
/// </summary>
public string? DevicePath { get; protected set; }
#endregion
protected BaseFeature(string name, string[] flags, string description, string? detailed = null)
: base(name, flags, description, detailed)
{
Options = new Options()
{
// Internal Program
InternalProgram = InternalProgram.NONE,
// Extra Dumping Options
ScanForProtection = false,
AddPlaceholders = true,
PullAllInformation = false,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
IncludeArtifacts = false,
CompressLogFiles = false,
LogCompression = LogCompression.DeflateMaximum,
DeleteUnnecessaryFiles = false,
CreateIRDAfterDumping = false,
// Protection Scanning Options
ScanArchivesForProtection = true,
IncludeDebugProtectionInformation = false,
HideDriveLetters = false,
// Redump Login Information
RetrieveMatchInformation = true,
RedumpUsername = null,
RedumpPassword = null,
};
}
/// <inheritdoc/>
public override bool Execute()
{
// Validate a system type is provided
if (System == null)
{
Console.Error.WriteLine("A system name needs to be provided");
return false;
}
// Log the system being used, in case it came from config
Console.WriteLine($"Using system: {System.LongName()}");
// Validate a program is provided
if (Options.InternalProgram == InternalProgram.NONE)
{
Console.Error.WriteLine("A program name needs to be provided");
return false;
}
// 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);
}
// Loop through all the rest of the args
for (int i = 0; i < Inputs.Count; i++)
{
// Get the full file path
string filepath = Path.GetFullPath(Inputs[i].Trim('"'));
// Now populate an environment
Drive? drive = null;
if (!string.IsNullOrEmpty(DevicePath))
drive = Drive.Create(null, DevicePath!);
var env = new DumpEnvironment(Options,
filepath,
drive,
System,
internalProgram: null);
env.SetProcessor();
// Finally, attempt to do the output dance
var result = env.VerifyAndSaveDumpOutput(seedInfo: Seed)
.ConfigureAwait(false).GetAwaiter().GetResult();
Console.WriteLine(result.Message);
}
return true;
}
/// <summary>
/// Display help for MPF.Check
/// </summary>
/// <param name="error">Error string to prefix the help text with</param>
public static void DisplayHelp()
{
Console.WriteLine("Usage:");
Console.WriteLine("MPF.Check <system> [options] </path/to/output.cue|iso|_logs.zip> ...");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
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("lo, listconfig List current configuration values");
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:");
Console.WriteLine("-u, --use <program> Dumping program output type [REQUIRED]");
Console.WriteLine(" --load-seed <path> Load a seed submission JSON for user information");
Console.WriteLine(" --no-placeholders Disable placeholder values in submission info");
Console.WriteLine(" --create-ird Create IRD from output files (PS3 only)");
Console.WriteLine(" --no-retrieve Disable retrieving match information from Redump");
Console.WriteLine("-c, --credentials <user> <pw> Redump username and password (incompatible with --no-retrieve) [WILL BE REMOVED]");
Console.WriteLine("-U, --username <user> Redump username (incompatible with --no-retrieve)");
Console.WriteLine("-P, --password <pw> Redump password (incompatible with --no-retrieve)");
Console.WriteLine(" --pull-all Pull all information from Redump (requires --username and --password)");
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-debug Enable debug protection information (requires --scan)");
Console.WriteLine(" --hide-drive-letters Hide drive letters from scan output (requires --scan)");
Console.WriteLine("-x, --suffix Enable adding filename suffix");
Console.WriteLine("-j, --json Enable submission JSON output");
Console.WriteLine(" --include-artifacts Include artifacts in JSON (requires --json)");
Console.WriteLine("-z, --zip Enable log file compression");
Console.WriteLine(" --log-compression Set the log compression type (requires compression enabled)");
Console.WriteLine("-d, --delete Enable unnecessary file deletion");
Console.WriteLine();
Console.WriteLine("WARNING: If using a configuration file alongside any of the above options");
Console.WriteLine("then flag options will act as toggles instead of always enabling.");
Console.WriteLine("For example, if log compression is enabled in your configuration file, then");
Console.WriteLine("providing the --zip option would disable compression.");
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();
}
}
}

View File

@@ -0,0 +1,288 @@
using System;
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using LogCompression = MPF.Processors.LogCompression;
namespace MPF.Check.Features
{
internal sealed class InteractiveFeature : BaseFeature
{
#region Feature Definition
public const string DisplayName = "interactive";
private static readonly string[] _flags = ["i", "interactive"];
private const string _description = "Enable interactive mode";
#endregion
public InteractiveFeature()
: base(DisplayName, _flags, _description)
{
}
/// <inheritdoc/>
public override bool ProcessArgs(string[] args, int index)
{
// Cache all args as inputs
for (int i = 1; i < args.Length; i++)
{
Inputs.Add(args[i]);
}
// Read the options from config, if possible
Options = OptionsLoader.LoadFromConfig();
if (Options.FirstRun)
{
Options = new Options()
{
// Internal Program
InternalProgram = InternalProgram.NONE,
// Extra Dumping Options
ScanForProtection = false,
AddPlaceholders = true,
PullAllInformation = false,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
IncludeArtifacts = false,
CompressLogFiles = false,
LogCompression = LogCompression.DeflateMaximum,
DeleteUnnecessaryFiles = false,
CreateIRDAfterDumping = false,
// Protection Scanning Options
ScanArchivesForProtection = true,
IncludeDebugProtectionInformation = false,
HideDriveLetters = false,
// Redump Login Information
RetrieveMatchInformation = true,
RedumpUsername = null,
RedumpPassword = null,
};
}
// Create return values
System = null;
// These values require multiple parts to be active
bool scan = false,
enableArchives = true,
enableDebug = false,
hideDriveLetters = false;
// Create state values
string? result;
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 '{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 '{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) Log compression (Currently '{Options.LogCompression.LongName()}')");
Console.WriteLine($"J) 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":
goto logCompression;
case "j":
case "J":
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("For possible inputs, use the List Systems commandline option");
Console.WriteLine();
Console.WriteLine("Input the system and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
System = result.ToRedumpSystem();
goto root;
dumpingProgram:
Console.WriteLine();
Console.WriteLine("Options:");
foreach (var program in (InternalProgram[])Enum.GetValues(typeof(InternalProgram)))
{
// Skip the placeholder values
if (program == InternalProgram.NONE)
continue;
Console.WriteLine($"{program.ToString().ToLowerInvariant(),-15} => {program.LongName()}");
}
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();
Seed = Builder.CreateFromFile(result);
goto root;
redumpCredentials:
Console.WriteLine();
Console.WriteLine("Enter your Redump username and press Enter:");
Console.Write("> ");
Options.RedumpUsername = Console.ReadLine();
Console.WriteLine("Enter your Redump 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("> ");
DevicePath = Console.ReadLine();
goto root;
logCompression:
Console.WriteLine();
Console.WriteLine("Options:");
foreach (var compressionType in (LogCompression[])Enum.GetValues(typeof(LogCompression)))
{
Console.WriteLine($"{compressionType.ToString().ToLowerInvariant(),-15} => {compressionType.LongName()}");
}
Console.WriteLine();
Console.WriteLine("Input the log compression type and press Enter:");
Console.Write("> ");
result = Console.ReadLine();
Options.LogCompression = result.ToLogCompression();
goto root;
exit:
// Now deal with the complex options
Options.ScanForProtection = scan && !string.IsNullOrEmpty(DevicePath);
Options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(DevicePath);
Options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(DevicePath);
Options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(DevicePath);
return true;
}
/// <inheritdoc/>
public override bool VerifyInputs() => Inputs.Count > 0;
}
}

View File

@@ -0,0 +1,272 @@
using System;
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.CommandLine.Inputs;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using LogCompression = MPF.Processors.LogCompression;
namespace MPF.Check.Features
{
internal sealed class MainFeature : BaseFeature
{
#region Feature Definition
public const string DisplayName = "main";
/// <remarks>Flags are unused</remarks>
private static readonly string[] _flags = [];
/// <remarks>Description is unused</remarks>
private const string _description = "";
#endregion
#region Inputs
private const string _createIrdName = "create-ird";
internal readonly FlagInput CreateIrdInput = new(_createIrdName, "--create-ird", "Create IRD from output files (PS3 only)");
private const string _deleteName = "delete";
internal readonly FlagInput DeleteInput = new(_deleteName, ["-d", "--delete"], "Enable unnecessary file deletion");
private const string _disableArchivesName = "disable-archives";
internal readonly FlagInput DisableArchivesInput = new(_disableArchivesName, "--disable-archives", "Disable scanning archives (requires --scan)");
private const string _enableDebugName = "enable-debug";
internal readonly FlagInput EnableDebugInput = new(_enableDebugName, "--enable-debug", "Enable debug protection information (requires --scan)");
private const string _hideDriveLettersName = "hide-drive-letters";
internal readonly FlagInput HideDriveLettersInput = new(_hideDriveLettersName, "--hide-drive-letters", "Hide drive letters from scan output (requires --scan)");
private const string _includeArtifactsName = "include-artifacts";
internal readonly FlagInput IncludeArtifactsInput = new(_includeArtifactsName, "--include-artifacts", "Include artifacts in JSON (requires --json)");
private const string _jsonName = "json";
internal readonly FlagInput JsonInput = new(_jsonName, ["-j", "--json"], "Enable submission JSON output");
private const string _loadSeedName = "load-seed";
internal readonly StringInput LoadSeedInput = new(_loadSeedName, "--load-seed", "Load a seed submission JSON for user information");
private const string _logCompressionName = "log-compression";
internal readonly StringInput LogCompressionInput = new(_logCompressionName, "--log-compression", "Set the log compression type (requires compression enabled)");
private const string _noPlaceholdersName = "no-placeholders";
internal readonly FlagInput NoPlaceholdersInput = new(_noPlaceholdersName, "--no-placeholders", "Disable placeholder values in submission info");
private const string _noRetrieveName = "no-retrieve";
internal readonly FlagInput NoRetrieveInput = new(_noRetrieveName, "--no-retrieve", "Disable retrieving match information from Redump");
private const string _passwordName = "password";
internal readonly StringInput PasswordInput = new(_passwordName, ["-P", "--password"], "Redump password (incompatible with --no-retrieve)");
private const string _pathName = "path";
internal readonly StringInput PathInput = new(_pathName, ["-p", "--path"], "Physical drive path for additional checks");
private const string _pullAllName = "pull-all";
internal readonly FlagInput PullAllInput = new(_pullAllName, "--pull-all", "Pull all information from Redump (requires --username and --password)");
private const string _scanName = "scan";
internal readonly FlagInput ScanInput = new(_scanName, ["-s", "--scan"], "Enable copy protection scan (requires --path)");
private const string _suffixName = "suffix";
internal readonly FlagInput SuffixInput = new(_suffixName, ["-x", "--suffix"], "Enable adding filename suffix");
private const string _useName = "use";
internal readonly StringInput UseInput = new(_useName, ["-u", "--use"], "Override configured dumping program name");
private const string _usernameName = "username";
internal readonly StringInput UsernameInput = new(_usernameName, ["-U", "--username"], "Redump username (incompatible with --no-retrieve)");
private const string _zipName = "zip";
internal readonly FlagInput ZipInput = new(_zipName, ["-z", "--zip"], "Enable log file compression");
#endregion
public MainFeature()
: base(DisplayName, _flags, _description)
{
Add(UseInput);
Add(LoadSeedInput);
Add(NoPlaceholdersInput);
Add(CreateIrdInput);
Add(NoRetrieveInput);
// TODO: Figure out how to work with the credentials input
Add(PullAllInput);
Add(PathInput);
Add(ScanInput);
Add(DisableArchivesInput);
Add(EnableDebugInput);
Add(HideDriveLettersInput);
Add(SuffixInput);
Add(JsonInput);
Add(IncludeArtifactsInput);
Add(ZipInput);
Add(LogCompressionInput);
Add(DeleteInput);
}
/// <inheritdoc/>
public override bool ProcessArgs(string[] args, int index)
{
// These values require multiple parts to be active
bool scan = false,
enableArchives = true,
enableDebug = false,
hideDriveLetters = false;
// If we have no arguments, just return
if (args is null || args.Length == 0)
return true;
// Read the options from config, if possible
Options = OptionsLoader.LoadFromConfig();
if (Options.FirstRun)
{
Options = new Options()
{
// Internal Program
InternalProgram = InternalProgram.NONE,
// Extra Dumping Options
ScanForProtection = false,
AddPlaceholders = true,
PullAllInformation = false,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
IncludeArtifacts = false,
CompressLogFiles = false,
LogCompression = LogCompression.DeflateMaximum,
DeleteUnnecessaryFiles = false,
CreateIRDAfterDumping = false,
// Protection Scanning Options
ScanArchivesForProtection = true,
IncludeDebugProtectionInformation = false,
HideDriveLetters = false,
// Redump Login Information
RetrieveMatchInformation = true,
RedumpUsername = null,
RedumpPassword = null,
};
}
else
{
Console.WriteLine("Options will be loaded from found configuration file!");
}
// The first argument is the system type
System = args[0].Trim('"').ToRedumpSystem();
// Loop through the arguments and parse out values
for (index = 1; index < args.Length; index++)
{
// Use specific program
if (UseInput.ProcessInput(args, ref index))
Options.InternalProgram = UseInput.Value.ToInternalProgram();
// Include seed info file
else if (LoadSeedInput.ProcessInput(args, ref index))
Seed = Builder.CreateFromFile(LoadSeedInput.Value);
// Disable placeholder values in submission info
else if (NoPlaceholdersInput.ProcessInput(args, ref index))
Options.AddPlaceholders = !Options.AddPlaceholders;
// Create IRD from output files (PS3 only)
else if (CreateIrdInput.ProcessInput(args, ref index))
Options.CreateIRDAfterDumping = !Options.CreateIRDAfterDumping;
// Set the log compression type (requires compression enabled)
else if (LogCompressionInput.ProcessInput(args, ref index))
Options.LogCompression = LogCompressionInput.Value.ToLogCompression();
// Retrieve Redump match information
else if (NoRetrieveInput.ProcessInput(args, ref index))
Options.RetrieveMatchInformation = !Options.RetrieveMatchInformation;
// Redump login
else if (args[index].StartsWith("-c=") || args[index].StartsWith("--credentials="))
{
string[] credentials = args[index].Split('=')[1].Split(';');
Options.RedumpUsername = credentials[0];
Options.RedumpPassword = credentials[1];
}
else if (args[index] == "-c" || args[index] == "--credentials")
{
Options.RedumpUsername = args[index + 1];
Options.RedumpPassword = args[index + 2];
index += 2;
}
// Redump username
else if (UsernameInput.ProcessInput(args, ref index))
Options.RedumpUsername = UsernameInput.Value;
// Redump password
else if (PasswordInput.ProcessInput(args, ref index))
Options.RedumpPassword = PasswordInput.Value;
// Pull all information (requires Redump login)
else if (PullAllInput.ProcessInput(args, ref index))
Options.PullAllInformation = !Options.PullAllInformation;
// Use a device path for physical checks
else if (PathInput.ProcessInput(args, ref index))
DevicePath = PathInput.Value;
// Scan for protection (requires device path)
else if (ScanInput.ProcessInput(args, ref index))
scan = true;
// Disable scanning archives (requires --scan)
else if (ScanInput.ProcessInput(args, ref index))
enableArchives = false;
// Enable debug protection information (requires --scan)
else if (EnableDebugInput.ProcessInput(args, ref index))
enableDebug = true;
// Hide drive letters from scan output (requires --scan)
else if (HideDriveLettersInput.ProcessInput(args, ref index))
hideDriveLetters = true;
// Add filename suffix
else if (SuffixInput.ProcessInput(args, ref index))
Options.AddFilenameSuffix = !Options.AddFilenameSuffix;
// Output submission JSON
else if (JsonInput.ProcessInput(args, ref index))
Options.OutputSubmissionJSON = !Options.OutputSubmissionJSON;
// Include JSON artifacts
else if (IncludeArtifactsInput.ProcessInput(args, ref index))
Options.IncludeArtifacts = !Options.IncludeArtifacts;
// Compress log and extraneous files
else if (ZipInput.ProcessInput(args, ref index))
Options.CompressLogFiles = !Options.CompressLogFiles;
// Delete unnecessary files
else if (DeleteInput.ProcessInput(args, ref index))
Options.DeleteUnnecessaryFiles = !Options.DeleteUnnecessaryFiles;
// Default, add to inputs
else
Inputs.Add(args[index]);
}
// Now deal with the complex options
Options.ScanForProtection = scan && !string.IsNullOrEmpty(DevicePath);
Options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(DevicePath);
Options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(DevicePath);
Options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(DevicePath);
return true;
}
/// <inheritdoc/>
public override bool VerifyInputs() => Inputs.Count > 0;
}
}

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;net10.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.6.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,20 @@
<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`)) OR $(TargetFramework.StartsWith(`net10`))">
<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;net10.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.CommandLine" Version="[1.4.0]" />
<PackageReference Include="SabreTools.RedumpLib" Version="[1.9.1]" />
</ItemGroup>
</Project>

View File

@@ -1,15 +1,12 @@
using System;
using System.IO;
using System.Collections.Generic;
#if NET40
using System.Threading;
using System.Threading.Tasks;
#endif
using BinaryObjectScanner;
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
using MPF.Check.Features;
using MPF.Frontend.Features;
using SabreTools.CommandLine;
using SabreTools.CommandLine.Features;
namespace MPF.Check
{
@@ -17,334 +14,132 @@ namespace MPF.Check
{
public static void Main(string[] args)
{
// Create a default options object
var options = new Frontend.Options()
// Create the command set
var mainFeature = new MainFeature();
var commandSet = CreateCommands(mainFeature);
// If we have no args, show the help and quit
if (args is null || args.Length == 0)
{
// Internal Program
InternalProgram = InternalProgram.NONE,
BaseFeature.DisplayHelp();
return;
}
// Extra Dumping Options
ScanForProtection = false,
AddPlaceholders = true,
PullAllInformation = false,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
IncludeArtifacts = false,
CompressLogFiles = false,
DeleteUnnecessaryFiles = false,
CreateIRDAfterDumping = false,
// Protection Scanning Options
ScanArchivesForProtection = true,
ScanPackersForProtection = false,
IncludeDebugProtectionInformation = false,
HideDriveLetters = false,
// Redump Login Information
RedumpUsername = null,
RedumpPassword = null,
};
// Get the first argument as a feature flag
string featureName = args[0];
// Try processing the standalone arguments
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
if (standaloneProcessed != false)
var topLevel = commandSet.GetTopLevel(featureName);
switch (topLevel)
{
if (standaloneProcessed == null)
DisplayHelp();
return;
}
// Standalone Options
case Help: BaseFeature.DisplayHelp(); return;
case VersionFeature version: version.Execute(); return;
case ListCodesFeature lc: lc.Execute(); return;
case ListConfigFeature lc: lc.Execute(); return;
case ListMediaTypesFeature lm: lm.Execute(); return;
case ListProgramsFeature lp: lp.Execute(); return;
case ListSystemsFeature ls: ls.Execute(); return;
// Try processing the common arguments
bool success = OptionsLoader.ProcessCommonArguments(args, out MediaType mediaType, out RedumpSystem? knownSystem, out var error);
if (!success)
{
DisplayHelp(error);
return;
}
// Interactive Mode
case InteractiveFeature interactive:
if (!interactive.ProcessArgs(args, 0))
{
BaseFeature.DisplayHelp();
return;
}
// 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;
}
if (!interactive.VerifyInputs())
{
Console.Error.WriteLine("At least one input is required");
BaseFeature.DisplayHelp();
return;
}
// Make new Progress objects
var resultProgress = new Progress<ResultEventArgs>();
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
if (!interactive.Execute())
{
BaseFeature.DisplayHelp();
return;
}
break;
// Default Behavior
default:
if (!mainFeature.ProcessArgs(args, 0))
{
BaseFeature.DisplayHelp();
return;
}
if (!mainFeature.VerifyInputs())
{
Console.Error.WriteLine("At least one input is required");
BaseFeature.DisplayHelp();
return;
}
if (!mainFeature.Execute())
{
BaseFeature.DisplayHelp();
return;
}
// Validate the supplied credentials
bool? validated = RedumpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).GetAwaiter().GetResult();
string message = validated switch
{
true => "Redump username and password accepted!",
false => "Redump username and password denied!",
null => "An error occurred validating your credentials!",
};
if (!string.IsNullOrEmpty(message))
Console.WriteLine(message);
// Loop through all the rest of the args
for (int i = startIndex; i < args.Length; i++)
{
// Check for a file
if (!File.Exists(args[i].Trim('"')))
{
DisplayHelp($"{args[i].Trim('"')} does not exist");
return;
}
// Get the full file path
string filepath = Path.GetFullPath(args[i].Trim('"'));
// Now populate an environment
Drive? drive = null;
if (!string.IsNullOrEmpty(opts.DevicePath))
drive = Drive.Create(null, opts.DevicePath!);
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, internalProgram: null, parameters: null);
// Finally, attempt to do the output dance
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress, seedInfo: opts.Seed).GetAwaiter().GetResult();
Console.WriteLine(result.Message);
}
}
/// <summary>
/// Display help for MPF.Check
/// </summary>
/// <param name="error">Error string to prefix the help text with</param>
private static void DisplayHelp(string? error = null)
{
if (error != null)
Console.WriteLine(error);
Console.WriteLine("Usage:");
Console.WriteLine("MPF.Check <mediatype> <system> [options] </path/to/output.cue/iso> ...");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
Console.WriteLine("-lc, --listcodes List supported comment/content site codes");
Console.WriteLine("-lm, --listmedia List supported media types");
Console.WriteLine("-ls, --listsystems List supported system types");
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");
Console.WriteLine();
Console.WriteLine("Check Options:");
Console.WriteLine("-u, --use <program> Dumping program output type [REQUIRED]");
Console.WriteLine(" --load-seed <path> Load a seed submission JSON for user information");
Console.WriteLine(" --no-placeholders Disable placeholder values in submission info");
Console.WriteLine(" --create-ird Create IRD from output files (PS3 only)");
Console.WriteLine("-c, --credentials <user> <pw> Redump username and password");
Console.WriteLine(" --pull-all Pull all information from Redump (requires --credentials)");
Console.WriteLine("-p, --path <drivepath> Physical drive path for additional checks");
Console.WriteLine("-s, --scan Enable copy protection scan (requires --path)");
Console.WriteLine(" --disable-archives Disable scanning archives (requires --scan)");
Console.WriteLine(" --enable-packers Enable scanning for packers (requires --scan)");
Console.WriteLine(" --enable-debug Enable debug protection information (requires --scan)");
Console.WriteLine(" --hide-drive-letters Hide drive letters from scan output (requires --scan)");
Console.WriteLine("-x, --suffix Enable adding filename suffix");
Console.WriteLine("-j, --json Enable submission JSON output");
Console.WriteLine(" --include-artifacts Include artifacts in JSON (requires --json)");
Console.WriteLine("-z, --zip Enable log file compression");
Console.WriteLine("-d, --delete Enable unnecessary file deletion");
Console.WriteLine();
}
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
private static CommandOptions LoadFromArguments(string[] args, Frontend.Options options, ref int startIndex)
{
// Create return values
var opts = new CommandOptions();
// These values require multiple parts to be active
bool scan = false,
enableArchives = true,
enablePackers = false,
enableDebug = false,
hideDriveLetters = false;
// If we have no arguments, just return
if (args == null || args.Length == 0)
{
startIndex = 0;
return opts;
}
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
return opts;
// Loop through the arguments and parse out values
for (; startIndex < args.Length; startIndex++)
{
// Use specific program
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
{
string internalProgram = args[startIndex].Split('=')[1];
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
startIndex++;
}
// Include seed info file
else if (args[startIndex].StartsWith("--load-seed="))
{
string seedInfo = args[startIndex].Split('=')[1];
opts.Seed = Builder.CreateFromFile(seedInfo);
}
else if (args[startIndex] == "--load-seed")
{
string seedInfo = args[startIndex + 1];
opts.Seed = Builder.CreateFromFile(seedInfo);
startIndex++;
}
// Disable placeholder values in submission info
else if (args[startIndex].Equals("--no-placeholders"))
{
options.AddPlaceholders = false;
}
// Create IRD from output files (PS3 only)
else if (args[startIndex].Equals("--create-ird"))
{
options.CreateIRDAfterDumping = true;
}
// Redump login
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
{
string[] credentials = args[startIndex].Split('=')[1].Split(';');
options.RedumpUsername = credentials[0];
options.RedumpPassword = credentials[1];
}
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
{
options.RedumpUsername = args[startIndex + 1];
options.RedumpPassword = args[startIndex + 2];
startIndex += 2;
}
// Pull all information (requires Redump login)
else if (args[startIndex].Equals("--pull-all"))
{
options.PullAllInformation = true;
}
// Use a device path for physical checks
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
{
opts.DevicePath = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
{
opts.DevicePath = args[startIndex + 1];
startIndex++;
}
// Scan for protection (requires device path)
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
{
scan = true;
}
// Disable scanning archives (requires --scan)
else if (args[startIndex].Equals("--disable-archives"))
{
enableArchives = false;
}
// Enable scanning for packers (requires --scan)
else if (args[startIndex].Equals("--enable-packers"))
{
enablePackers = true;
}
// Enable debug protection information (requires --scan)
else if (args[startIndex].Equals("--enable-debug"))
{
enableDebug = true;
}
// Hide drive letters from scan output (requires --scan)
else if (args[startIndex].Equals("--hide-drive-letters"))
{
hideDriveLetters = true;
}
// Add filename suffix
else if (args[startIndex].Equals("-x") || args[startIndex].Equals("--suffix"))
{
options.AddFilenameSuffix = true;
}
// Output submission JSON
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
{
options.OutputSubmissionJSON = true;
}
// Output submission JSON
else if (args[startIndex].Equals("--include-artifacts"))
{
options.IncludeArtifacts = true;
}
// Compress log and extraneous files
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
{
options.CompressLogFiles = true;
}
// Delete unnecessary files files
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
{
options.DeleteUnnecessaryFiles = true;
}
// Default, we fall out
else
{
break;
}
}
// Now deal with the complex options
options.ScanForProtection = scan && !string.IsNullOrEmpty(opts.DevicePath);
options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(opts.DevicePath);
options.ScanPackersForProtection = enablePackers && scan && !string.IsNullOrEmpty(opts.DevicePath);
options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(opts.DevicePath);
options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(opts.DevicePath);
return opts;
}
/// <summary>
/// Represents commandline options
/// Create the command set for the program
/// </summary>
private class CommandOptions
private static CommandSet CreateCommands(MainFeature mainFeature)
{
/// <summary>
/// Seed submission info from an input file
/// </summary>
public SubmissionInfo? Seed { get; set; } = null;
List<string> header = [
"MPF.CLI [standalone|system] [options] <path> ...",
string.Empty,
];
/// <summary>
/// Path to the device to scan
/// </summary>
public string? DevicePath { get; set; } = null;
List<string> footer = [
string.Empty,
"WARNING: Check will overwrite both any existing submission information files as well",
"as any log archives. Please make backups of those if you need to before running Check.",
string.Empty,
];
var commandSet = new CommandSet(header, footer);
// Standalone Options
commandSet.Add(new Help());
commandSet.Add(new VersionFeature());
commandSet.Add(new ListCodesFeature());
commandSet.Add(new ListConfigFeature());
commandSet.Add(new ListMediaTypesFeature());
commandSet.Add(new ListSystemsFeature());
commandSet.Add(new ListProgramsFeature());
commandSet.Add(new InteractiveFeature());
// Check Options
commandSet.Add(mainFeature.UseInput);
commandSet.Add(mainFeature.LoadSeedInput);
commandSet.Add(mainFeature.NoPlaceholdersInput);
commandSet.Add(mainFeature.CreateIrdInput);
commandSet.Add(mainFeature.NoRetrieveInput);
commandSet.Add(mainFeature.UsernameInput);
commandSet.Add(mainFeature.PasswordInput);
commandSet.Add(mainFeature.PullAllInput);
commandSet.Add(mainFeature.PathInput);
commandSet.Add(mainFeature.ScanInput);
commandSet.Add(mainFeature.DisableArchivesInput);
commandSet.Add(mainFeature.EnableDebugInput);
commandSet.Add(mainFeature.HideDriveLettersInput);
commandSet.Add(mainFeature.SuffixInput);
commandSet.Add(mainFeature.JsonInput);
commandSet.Add(mainFeature.IncludeArtifactsInput);
commandSet.Add(mainFeature.ZipInput);
commandSet.Add(mainFeature.LogCompressionInput);
commandSet.Add(mainFeature.DeleteInput);
return commandSet;
}
}
}

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 readonly 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 --store-encrypted True --title-keys False --trim 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 --store-encrypted True --title-keys False --trim 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 --store-encrypted True --title-keys False --trim 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 --store-encrypted True --title-keys False --trim 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 --store-encrypted True --title-keys False --trim 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 --store-encrypted True --title-keys False --trim 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,480 @@
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 readonly 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 AuthPS3
[Theory]
[InlineData("authps3 f")]
public void AuthPS3Test(string parameters)
{
string? expected = "authps3 f";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.False(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,68 @@
using System.Collections.Generic;
using MPF.ExecutionContexts.Dreamdump;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.ExecutionContexts.Test
{
public class DreamdumpTests
{
#region Default Values
private static readonly Dictionary<string, string?> AllOptions = new()
{
[SettingConstants.RereadCount] = "1000",
[SettingConstants.SectorOrder] = "DATA_C2_SUB",
};
// None of these scenarios are actually supported as all are treated like GD-ROM
[Theory]
[InlineData(null, null, null, "filename.bin", null, "--retries=20 --image-name=\"filename\" --sector-order=DATA_C2_SUB")]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, "/dev/sr0", "path/filename.bin", 2, "--retries=20 --image-name=\"filename\" --image-path=\"path\" --speed=2 --sector-order=DATA_C2_SUB --drive=/dev/sr0")]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, "/dev/sr0", "path/filename.bin", 2, "--retries=20 --image-name=\"filename\" --image-path=\"path\" --speed=2 --sector-order=DATA_C2_SUB --drive=/dev/sr0")]
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, "/dev/sr0", "path/filename.bin", 2, "--retries=20 --image-name=\"filename\" --image-path=\"path\" --speed=2 --sector-order=DATA_C2_SUB --drive=/dev/sr0")]
[InlineData(RedumpSystem.NintendoWii, MediaType.NintendoWiiOpticalDisc, "/dev/sr0", "path/filename.bin", 2, "--retries=20 --image-name=\"filename\" --image-path=\"path\" --speed=2 --sector-order=DATA_C2_SUB --drive=/dev/sr0")]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, "/dev/sr0", "path/filename.bin", 2, "--retries=20 --image-name=\"filename\" --image-path=\"path\" --speed=2 --sector-order=DATA_C2_SUB --drive=/dev/sr0")]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, "/dev/sr0", "path/filename.bin", 2, "--retries=20 --image-name=\"filename\" --image-path=\"path\" --speed=2 --sector-order=DATA_C2_SUB --drive=/dev/sr0")]
[InlineData(RedumpSystem.NintendoWiiU, MediaType.NintendoWiiUOpticalDisc, "/dev/sr0", "path/filename.bin", 2, "--retries=20 --image-name=\"filename\" --image-path=\"path\" --speed=2 --sector-order=DATA_C2_SUB --drive=/dev/sr0")]
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 Default
[Theory]
[InlineData("--force-qtoc --train --retries=20 --image-name=image --image-path=path --read-offset=0 --read-at-once=0 --speed=8 --sector-order=so --drive=/dev/sr0")]
public void DiscTest(string parameters)
{
string? expected = "--force-qtoc --train --retries=20 --image-name=\"image\" --image-path=\"path\" --read-offset=0 --read-at-once=0 --speed=8 --sector-order=so --drive=/dev/sr0";
var context = new ExecutionContext(parameters);
string? actual = context.GenerateParameters();
Assert.Equal(expected, actual);
Assert.True(context.IsDumpingCommand());
}
[Theory]
[InlineData("--image-name=\"image name.bin\" --image-path=\"directory name\"")]
public void SpacesTest(string parameters)
{
string? expected = "--image-name=\"image name.bin\" --image-path=\"directory name\"";
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)
{
var 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)
{
var 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)
{
var 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)
{
var 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)
{
var 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)
{
var 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)
{
var 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)
{
var 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)
{
var 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)
{
var 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)
{
var 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;net10.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="18.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="SabreTools.RedumpLib" Version="[1.9.1]" />
<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.27.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.5">
<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 readonly 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, "disc --verbose --skeleton --retries=1000 --image-name=\"filename\" --drive-type=GENERIC --drive-read-method=BE --drive-sector-order=DATA_C2_SUB --plextor-leadin-retries=1000")]
[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 --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.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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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 --mediatek-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 --filesystem-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
}
}
}

View File

@@ -40,4 +40,4 @@ namespace MPF.ExecutionContexts.Aaru
public const string WesternEuropeanMac = "macintosh";
public const string WesternEuropeanRadix50 = "radix50";
}
}
}

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

@@ -166,4 +166,4 @@ namespace MPF.ExecutionContexts.Aaru
public const string XboxPartitioning = "Xbox partitioning";
public const string XENIX = "XENIX";
}
}
}

View File

@@ -24,4 +24,4 @@ namespace MPF.ExecutionContexts.Aaru
public const string WindowsNT83MixedCase = "nt";
public const string OS2Extended = "os2";
}
}
}

View File

@@ -40,4 +40,4 @@ namespace MPF.ExecutionContexts.Aaru
public const string VMwareDiskImageSparse = "sparse"; // boolean, default false
public const string VMwareDiskImageSplit = "split"; // boolean, default false
}
}
}

View File

@@ -17,4 +17,4 @@ namespace MPF.ExecutionContexts.Aaru
public const string StripPersonalData = "AaruStripPersonalData";
public const bool StripPersonalDataDefault = false;
}
}
}

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);
}
@@ -155,7 +160,7 @@ namespace MPF.ExecutionContexts
/// Returns if the current Parameter object is valid
/// </summary>
/// <returns></returns>
public bool IsValid() => GenerateParameters() != null;
public bool IsValid() => GenerateParameters() is not null;
/// <summary>
/// Reset all special variables to have default values
@@ -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
@@ -214,7 +222,7 @@ namespace MPF.ExecutionContexts
{
try
{
while (process != null && !process.HasExited)
while (process is not null && !process.HasExited)
{
process.Kill();
}
@@ -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];
@@ -286,18 +294,57 @@ namespace MPF.ExecutionContexts
return defaultValue;
}
/// <summary>
/// Get an UInt8 setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
internal static byte GetUInt8Setting(Dictionary<string, string?> settings, string key, byte defaultValue)
{
if (settings.ContainsKey(key))
{
if (byte.TryParse(settings[key], out byte value))
return value;
else
return defaultValue;
}
else
{
return defaultValue;
}
}
#endregion
#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
@@ -306,11 +353,11 @@ namespace MPF.ExecutionContexts
/// <returns>True if the flag value is supported, false otherwise</returns>
protected bool IsFlagSupported(string flag)
{
if (CommandSupport == null)
if (CommandSupport is null)
return false;
if (this.BaseCommand == null)
if (BaseCommand is 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 +367,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 +377,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 is not null && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
else if (upperBound is not null && temp > upperBound)
return false;
return true;
@@ -350,14 +397,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 is not null && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
else if (upperBound is not null && temp > upperBound)
return false;
return true;
@@ -370,14 +417,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 is not null && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
else if (upperBound is not null && temp > upperBound)
return false;
return true;
@@ -390,14 +437,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 is not null && temp < lowerBound)
return false;
else if (upperBound != -1 && temp > upperBound)
else if (upperBound is not null && temp > upperBound)
return false;
return true;
@@ -406,24 +453,24 @@ 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)
if (parts is null)
return false;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
@@ -440,26 +487,26 @@ 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)
if (parts is null)
return false;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
@@ -515,26 +562,26 @@ 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)
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
@@ -597,32 +644,32 @@ namespace MPF.ExecutionContexts
return null;
}
return SByte.MinValue;
return sbyte.MinValue;
}
/// <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)
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
@@ -684,32 +731,32 @@ namespace MPF.ExecutionContexts
return null;
}
return Int16.MinValue;
return short.MinValue;
}
/// <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)
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
@@ -771,32 +818,32 @@ 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)
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
@@ -831,10 +878,10 @@ namespace MPF.ExecutionContexts
i++;
string value = ExtractFactorFromValue(parts[i], out long factor);
if (long.TryParse(value, out long longValue))
return (long)(longValue * factor);
return longValue * factor;
string hexValue = RemoveHexIdentifier(value);
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long longHexValue))
return (long)(longHexValue * factor);
return longHexValue * factor;
return null;
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
@@ -851,39 +898,39 @@ namespace MPF.ExecutionContexts
this[longFlagString] = true;
string value = ExtractFactorFromValue(valuePart, out long factor);
if (long.TryParse(value, out long longValue))
return (long)(longValue * factor);
return longValue * factor;
string hexValue = RemoveHexIdentifier(value);
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long longHexValue))
return (long)(longHexValue * factor);
return longHexValue * factor;
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)
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
@@ -937,26 +984,26 @@ 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)
if (parts is null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
@@ -1019,7 +1066,7 @@ namespace MPF.ExecutionContexts
return null;
}
return Byte.MinValue;
return byte.MinValue;
}
/// <summary>
@@ -1027,7 +1074,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 +1136,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 is 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)
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,291 @@
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 NETCOREAPP || NETSTANDARD2_1_OR_GREATER
if (value.EndsWith('c'))
#else
if (value.EndsWith("c", System.StringComparison.Ordinal))
#endif
{
factor = 1;
value = value.TrimEnd('c');
}
// Words
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
else if (value.EndsWith('w'))
#else
else if (value.EndsWith("w", System.StringComparison.Ordinal))
#endif
{
factor = 2;
value = value.TrimEnd('w');
}
// Double Words
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
else if (value.EndsWith('d'))
#else
else if (value.EndsWith("d", System.StringComparison.Ordinal))
#endif
{
factor = 4;
value = value.TrimEnd('d');
}
// Quad Words
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
else if (value.EndsWith('q'))
#else
else if (value.EndsWith("q", System.StringComparison.Ordinal))
#endif
{
factor = 8;
value = value.TrimEnd('q');
}
// Kilobytes
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
else if (value.EndsWith('k'))
#else
else if (value.EndsWith("k", System.StringComparison.Ordinal))
#endif
{
factor = 1024;
value = value.TrimEnd('k');
}
// Megabytes
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
else if (value.EndsWith('M'))
#else
else if (value.EndsWith("M", System.StringComparison.Ordinal))
#endif
{
factor = 1024 * 1024;
value = value.TrimEnd('M');
}
// Gigabytes
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
else if (value.EndsWith('G'))
#else
else if (value.EndsWith("G", System.StringComparison.Ordinal))
#endif
{
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;
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
return value[2..];
#else
return value.Substring(2);
#endif
}
#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 is not 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 is 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out short? value) && value is not null)
{
index++;
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : short.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out short? value) && value is not null)
{
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : short.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is 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 is not null))
{
// Separator
if (useEquals)
builder.Append('=');
else
builder.Append(' ');
// Value
int?[] nonNull = Array.FindAll(Value, i => i is not 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 is not null)
{
index++;
Value[i] = value;
Value[i] = (MinValue is not null && Value[i] < MinValue) ? MinValue : Value[i];
Value[i] = (MaxValue is not 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 is 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out int? value) && value is not null)
{
index++;
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : int.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out int? value) && value is not null)
{
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : int.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out long? value) && value is not null)
{
index++;
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : long.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out long? value) && value is not null)
{
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : long.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 = 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 = 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 is 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out sbyte? value) && value is not null)
{
index++;
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : sbyte.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out sbyte? value) && value is not null)
{
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : sbyte.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is 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 is 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out ushort? value) && value is not null)
{
index++;
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : ushort.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out ushort? value) && value is not null)
{
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : ushort.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out uint? value) && value is not null)
{
index++;
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : uint.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out uint? value) && value is not null)
{
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : uint.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out ulong? value) && value is not null)
{
index++;
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : ulong.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out ulong? value) && value is not null)
{
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : ulong.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 = 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 = 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 is 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(parts[index + 1], out byte? value) && value is not null)
{
index++;
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : byte.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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 is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return !_required;
}
// If the next value is valid
if (ParseValue(val, out byte? value) && value is not null)
{
Value = value;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not null && Value > MaxValue) ? MaxValue : Value;
return true;
}
// Return value based on required flag
Value = _required ? null : byte.MinValue;
Value = (MinValue is not null && Value < MinValue) ? MinValue : Value;
Value = (MaxValue is not 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;
}
}
}

View File

@@ -7,6 +7,7 @@ namespace MPF.ExecutionContexts.DiscImageCreator
{
public const string NONE = "";
public const string Audio = "audio";
public const string AuthPS3 = "authps3";
public const string BluRay = "bd";
public const string Close = "close";
public const string CompactDisc = "cd";
@@ -32,4 +33,4 @@ namespace MPF.ExecutionContexts.DiscImageCreator
public const string XGD2Swap = "xgd2swap";
public const string XGD3Swap = "xgd3swap";
}
}
}

View File

@@ -72,6 +72,7 @@ namespace MPF.ExecutionContexts.DiscImageCreator
/// <returns>Valid extension (with leading '.'), null on error</returns>
public static string? Extension(MediaType? type)
{
#pragma warning disable IDE0072
return type switch
{
MediaType.CDROM
@@ -93,8 +94,9 @@ namespace MPF.ExecutionContexts.DiscImageCreator
MediaType.Cassette => ".wav",
_ => null,
};
#pragma warning restore IDE0072
}
#endregion
}
}
}

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,10 +40,11 @@ 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";
public const string VideoNowColor = "/vnc";
public const string VideoNowXP = "/vnx";
}
}
}

View File

@@ -23,4 +23,4 @@ namespace MPF.ExecutionContexts.DiscImageCreator
public const string UseCMIFlag = "DICUseCMIFlag";
public const bool UseCMIFlagDefault = false;
}
}
}

View File

@@ -0,0 +1,10 @@
namespace MPF.ExecutionContexts.Dreamdump
{
/// <summary>
/// Top-level commands for Dreamdump
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
}
}

View File

@@ -0,0 +1,15 @@
namespace MPF.ExecutionContexts.Dreamdump
{
/// <summary>
/// Drive sector order option
/// </summary>
public enum SectorOrder
{
NONE = 0,
DATA_C2,
DATA_SUB,
DATA_C2_SUB,
DATA_SUB_C2,
}
}

View File

@@ -0,0 +1,257 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using MPF.ExecutionContexts.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.ExecutionContexts.Dreamdump
{
/// <summary>
/// Represents a generic set of Dreamdump parameters
/// </summary>
public sealed class ExecutionContext : BaseExecutionContext
{
#region Generic Dumping Information
/// <inheritdoc/>
public override string? InputPath
=> (_inputs[FlagStrings.Drive] as StringInput)?.Value?.Trim('"');
/// <inheritdoc/>
public override string? OutputPath => Path.Combine(
(_inputs[FlagStrings.ImagePath] as StringInput)?.Value?.Trim('"') ?? string.Empty,
(_inputs[FlagStrings.ImageName] as StringInput)?.Value?.Trim('"') ?? string.Empty)
+ GetDefaultExtension(MediaType);
/// <inheritdoc/>
public override int? Speed
{
get
{
return (_inputs[FlagStrings.Speed] as Int32Input)?.Value;
}
set
{
if (value is not null && value > 0)
{
this[FlagStrings.Speed] = true;
(_inputs[FlagStrings.Speed] as Int32Input)?.SetValue(value);
}
else
{
this[FlagStrings.Speed] = false;
(_inputs[FlagStrings.Speed] as Int32Input)?.SetValue(null);
}
}
}
#endregion
#region Flag Values
/// <summary>
/// Set of all command flags
/// </summary>
private readonly Dictionary<string, Input> _inputs = new()
{
// Special
[FlagStrings.ForceQTOC] = new FlagInput(FlagStrings.ForceQTOC),
[FlagStrings.Train] = new FlagInput(FlagStrings.Train),
[FlagStrings.Retries] = new UInt8Input(FlagStrings.Retries),
// Paths
[FlagStrings.ImageName] = new StringInput(FlagStrings.ImageName) { Quotes = true },
[FlagStrings.ImagePath] = new StringInput(FlagStrings.ImagePath) { Quotes = true },
// Drive Part
[FlagStrings.ReadOffset] = new Int16Input(FlagStrings.ReadOffset),
[FlagStrings.ReadAtOnce] = new UInt8Input(FlagStrings.ReadAtOnce),
[FlagStrings.Speed] = new UInt16Input(FlagStrings.Speed),
[FlagStrings.SectorOrder] = new StringInput(FlagStrings.SectorOrder),
[FlagStrings.Drive] = new StringInput(FlagStrings.Drive),
};
#endregion
/// <inheritdoc/>
public ExecutionContext(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public ExecutionContext(RedumpSystem? system,
MediaType? type,
string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
#region BaseExecutionContext Implementations
/// <inheritdoc/>
/// <remarks>Command support is irrelevant for Dreamdump</remarks>
public override Dictionary<string, List<string>> GetCommandSupport()
{
return new Dictionary<string, List<string>>()
{
[CommandStrings.NONE] =
[
// Special
FlagStrings.ForceQTOC,
FlagStrings.Train,
FlagStrings.Retries,
// Paths
FlagStrings.ImageName,
FlagStrings.ImagePath,
// Drive Part
FlagStrings.ReadOffset,
FlagStrings.ReadAtOnce,
FlagStrings.Speed,
FlagStrings.SectorOrder,
FlagStrings.Drive,
],
};
}
/// <inheritdoc/>
public override string GenerateParameters()
{
var parameters = new StringBuilder();
// Loop though and append all existing
foreach (var kvp in _inputs)
{
// If the value doesn't exist
string formatted = kvp.Value.Format(useEquals: true);
if (formatted.Length == 0)
continue;
// Append the parameter
parameters.Append($"{formatted} ");
}
return parameters.ToString().TrimEnd();
}
/// <inheritdoc/>
public override string? GetDefaultExtension(MediaType? mediaType) => ".bin";
/// <inheritdoc/>
public override MediaType? GetMediaType() => SabreTools.RedumpLib.Data.MediaType.GDROM;
/// <inheritdoc/>
public override bool IsDumpingCommand() => true;
/// <inheritdoc/>
protected override void ResetValues()
{
BaseCommand = CommandStrings.NONE;
flags = [];
foreach (var kvp in _inputs)
kvp.Value.ClearValue();
}
/// <inheritdoc/>
protected override void SetDefaultParameters(string? drivePath,
string filename,
int? driveSpeed,
Dictionary<string, string?> options)
{
BaseCommand = CommandStrings.NONE;
if (drivePath is not null)
{
this[FlagStrings.Drive] = true;
(_inputs[FlagStrings.Drive] as StringInput)?.SetValue(drivePath);
}
if (driveSpeed is not null && driveSpeed > 0)
{
this[FlagStrings.Speed] = true;
(_inputs[FlagStrings.Speed] as UInt16Input)?.SetValue((ushort)driveSpeed);
}
else
{
this[FlagStrings.Speed] = false;
(_inputs[FlagStrings.Speed] as UInt16Input)?.SetValue(null);
}
// Set user-defined options
string? sectorOrder = GetStringSetting(options, SettingConstants.SectorOrder, SettingConstants.SectorOrderDefault);
if (!string.IsNullOrEmpty(sectorOrder) && sectorOrder != SectorOrder.NONE.ToString())
{
this[FlagStrings.SectorOrder] = true;
(_inputs[FlagStrings.SectorOrder] as StringInput)?.SetValue(sectorOrder!);
}
// Set the output paths
if (!string.IsNullOrEmpty(filename))
{
var imagePath = Path.GetDirectoryName(filename);
if (!string.IsNullOrEmpty(imagePath))
{
this[FlagStrings.ImagePath] = true;
(_inputs[FlagStrings.ImagePath] as StringInput)?.SetValue(imagePath!);
}
string imageName = Path.GetFileNameWithoutExtension(filename);
if (!string.IsNullOrEmpty(imageName))
{
this[FlagStrings.ImageName] = true;
(_inputs[FlagStrings.ImageName] as StringInput)?.SetValue(imageName!);
}
}
byte retries = GetUInt8Setting(options, SettingConstants.RereadCount, SettingConstants.RereadCountDefault);
if (retries > 0)
{
this[FlagStrings.Retries] = true;
(_inputs[FlagStrings.Retries] as UInt8Input)?.SetValue(retries);
}
}
/// <inheritdoc/>
protected override bool ValidateAndSetParameters(string? parameters)
{
// The string has to be valid by itself first
if (string.IsNullOrEmpty(parameters))
return false;
// Now split the string into parts for easier validation
string[] parts = SplitParameterString(parameters!);
// Setup the modes
BaseCommand = null;
// Loop through all auxiliary flags, if necessary
int index = 0;
for (int i = index; i < parts.Length; i++)
{
// Match all possible flags
foreach (var kvp in _inputs)
{
// If the value was not a match
if (!kvp.Value.Process(parts, ref i))
continue;
// Set the flag
this[kvp.Key] = true;
}
}
// If the image name was not set, set it with a default value
if (string.IsNullOrEmpty((_inputs[FlagStrings.ImageName] as StringInput)?.Value))
(_inputs[FlagStrings.ImageName] as StringInput)?.SetValue($"track_{DateTime.Now:yyyyMMdd-HHmm}");
return true;
}
#endregion
}
}

View File

@@ -0,0 +1,33 @@
namespace MPF.ExecutionContexts.Dreamdump
{
/// <summary>
/// Dumping flags for Dreamdump
/// </summary>
public static class FlagStrings
{
#region Special
public const string ForceQTOC = "--force-qtoc";
public const string Train = "--train";
public const string Retries = "--retries";
#endregion
#region Paths
public const string ImageName = "--image-name";
public const string ImagePath = "--image-path";
#endregion
#region Drive Part
public const string ReadOffset = "--read-offset";
public const string ReadAtOnce = "--read-at-once"; // [0,40] (Linux), [0,20] (Windows)
public const string Speed = "--speed";
public const string SectorOrder = "--sector-order";
public const string Drive = "--drive";
#endregion
}
}

View File

@@ -0,0 +1,11 @@
namespace MPF.ExecutionContexts.Dreamdump
{
public static class SettingConstants
{
public const string RereadCount = "DreamdumpRereadCount";
public const int RereadCountDefault = 20;
public const string SectorOrder = "DreamdumpSectorOrder";
public static readonly string SectorOrderDefault = Dreamdump.SectorOrder.NONE.ToString();
}
}

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;net10.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.6.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.9.1]" />
</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

@@ -13,16 +13,22 @@ namespace MPF.ExecutionContexts.Redumper
/// <returns>Valid extension (with leading '.'), null on error</returns>
public static string? Extension(MediaType? type)
{
#pragma warning disable IDE0072
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,
};
#pragma warning restore IDE0072
}
#endregion
}
}
}

View File

@@ -1,5 +1,21 @@
namespace MPF.ExecutionContexts.Redumper
{
/// <summary>
/// Drive type option
/// </summary>
public enum DriveType
{
NONE = 0,
GENERIC,
PLEXTOR,
MTK8A,
MTK8B,
MTK8C,
MTK3,
MTK2,
}
/// <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,73 @@ 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 AsusSkipLeadout = "--asus-skip-leadout";
public const string PlextorLeadinForceStore = "--plextor-leadin-force-store";
public const string KreonPartialSS = "--kreon-partial-ss";
public const string MediatekSkipLeadout = "--mediatek-skip-leadout";
public const string MediatekLeadoutRetries = "--mediatek-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";
public const string FilesystemTrim = "--filesystem-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 LBAEndBySubcode = "--lba-end-by-subcode";
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 ForceFlash = "--force-flash";
public const string SkipSubcodeDesync = "--skip-subcode-desync";
public const string CdrErrorThreshold = "--cdr-error-threshold";
public const string ScsiTimeout = "--scsi-timeout";
#endregion
#region Undocumented
public const string Debug = "--debug";
public const string LegacySubs = "--legacy-subs";
public const string DisableCDText = "--disable-cdtext";
#endregion
}
}
}

View File

@@ -2,28 +2,31 @@ 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;
public const string DrivePregapStart = "RedumperDrivePregapStart";
public const int DrivePregapStartDefault = 0;
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
@@ -45,9 +43,9 @@ namespace MPF.Test.Frontend
public static List<object?[]> GenerateDriveTypeMappingTestData()
{
var testData = new List<object?[]>() { new object?[] { null, true } };
foreach (DriveType driveType in Enum.GetValues(typeof(DriveType)))
foreach (DriveType driveType in Enum.GetValues<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,296 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SabreTools.RedumpLib.Data;
using Xunit;
using LogCompression = MPF.Processors.LogCompression;
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(InterfaceLanguage.AutoDetect, "Auto Detect")]
[InlineData(InterfaceLanguage.English, "English")]
[InlineData(InterfaceLanguage.French, "Français")]
[InlineData(InterfaceLanguage.German, "Deutsch")]
[InlineData(InterfaceLanguage.Italian, "Italiano")]
[InlineData(InterfaceLanguage.Japanese, "日本語")]
[InlineData(InterfaceLanguage.Korean, "한국어")]
[InlineData(InterfaceLanguage.Polish, "Polski")]
[InlineData(InterfaceLanguage.Russian, "Русский")]
[InlineData(InterfaceLanguage.Spanish, "Español")]
[InlineData(InterfaceLanguage.Swedish, "Svenska")]
[InlineData(InterfaceLanguage.Ukrainian, "Українська")]
[InlineData(InterfaceLanguage.L337, "L337")]
public void LongName_InterfaceLanguage(InterfaceLanguage? lang, string? expected)
{
string? actual = lang.LongName();
Assert.Equal(expected, actual);
if (lang is not null)
{
actual = EnumExtensions.GetLongName(lang);
Assert.Equal(expected, actual);
}
}
[Theory]
[InlineData(null, "Unknown")]
[InlineData(InternalProgram.NONE, "Unknown")]
[InlineData(InternalProgram.Aaru, "Aaru")]
[InlineData(InternalProgram.DiscImageCreator, "DiscImageCreator")]
// [InlineData(InternalProgram.Dreamdump, "Dreamdump")]
[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);
if (prog is not null)
{
actual = EnumExtensions.GetLongName(prog);
Assert.Equal(expected, actual);
}
}
[Theory]
[InlineData(null, "Unknown")]
[InlineData(LogCompression.DeflateDefault, "ZIP using Deflate (Level 5)")]
[InlineData(LogCompression.DeflateMaximum, "ZIP using Deflate (Level 9)")]
[InlineData(LogCompression.Zstd19, "ZIP using Zstd (Level 19)")]
public void LongName_LogCompression(LogCompression? comp, string? expected)
{
string? actual = comp.LongName();
Assert.Equal(expected, actual);
if (comp is not null)
{
actual = EnumExtensions.GetLongName(comp);
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);
if (method is not null)
{
actual = EnumExtensions.GetLongName(method);
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);
if (order is not null)
{
actual = EnumExtensions.GetLongName(order);
Assert.Equal(expected, actual);
}
}
[Theory]
[InlineData(null, "Unknown")]
[InlineData(RedumperDriveType.NONE, "Default")]
[InlineData(RedumperDriveType.GENERIC, "GENERIC")]
[InlineData(RedumperDriveType.PLEXTOR, "PLEXTOR")]
[InlineData(RedumperDriveType.MTK8A, "MTK8A")]
[InlineData(RedumperDriveType.MTK8B, "MTK8B")]
[InlineData(RedumperDriveType.MTK8C, "MTK8C")]
[InlineData(RedumperDriveType.MTK3, "MTK3")]
[InlineData(RedumperDriveType.MTK2, "MTK2")]
public void LongName_RedumperDriveType(RedumperDriveType? type, string? expected)
{
string? actual = type.LongName();
Assert.Equal(expected, actual);
if (type is not null)
{
actual = EnumExtensions.GetLongName(type);
Assert.Equal(expected, actual);
}
}
#endregion
#region Short Name
[Theory]
[InlineData(null, "Unknown")]
[InlineData(InterfaceLanguage.AutoDetect, "auto")]
[InlineData(InterfaceLanguage.English, "eng")]
[InlineData(InterfaceLanguage.French, "fra")]
[InlineData(InterfaceLanguage.German, "deu")]
[InlineData(InterfaceLanguage.Italian, "ita")]
[InlineData(InterfaceLanguage.Japanese, "jpn")]
[InlineData(InterfaceLanguage.Korean, "kor")]
[InlineData(InterfaceLanguage.Polish, "pol")]
[InlineData(InterfaceLanguage.Russian, "rus")]
[InlineData(InterfaceLanguage.Spanish, "spa")]
[InlineData(InterfaceLanguage.Swedish, "swe")]
[InlineData(InterfaceLanguage.Ukrainian, "ukr")]
[InlineData(InterfaceLanguage.L337, "l37")]
public void ShortName_InterfaceLanguage(InterfaceLanguage? lang, string? expected)
{
string? actual = lang.ShortName();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(null, "Unknown")]
[InlineData(InternalProgram.NONE, "Unknown")]
[InlineData(InternalProgram.Aaru, "aaru")]
[InlineData(InternalProgram.DiscImageCreator, "dic")]
// [InlineData(InternalProgram.Dreamdump, "dreamdump")]
[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, InterfaceLanguage.AutoDetect)]
[InlineData("", InterfaceLanguage.AutoDetect)]
[InlineData("auto", InterfaceLanguage.AutoDetect)]
[InlineData("eng", InterfaceLanguage.English)]
[InlineData("fra", InterfaceLanguage.French)]
[InlineData("deu", InterfaceLanguage.German)]
[InlineData("ita", InterfaceLanguage.Italian)]
[InlineData("jpn", InterfaceLanguage.Japanese)]
[InlineData("kor", InterfaceLanguage.Korean)]
[InlineData("pol", InterfaceLanguage.Polish)]
[InlineData("rus", InterfaceLanguage.Russian)]
[InlineData("spa", InterfaceLanguage.Spanish)]
[InlineData("swe", InterfaceLanguage.Swedish)]
[InlineData("ukr", InterfaceLanguage.Ukrainian)]
[InlineData("l37", InterfaceLanguage.L337)]
public void ToInterfaceLanguageTest(string? interfaceLanguage, InterfaceLanguage expected)
{
InterfaceLanguage actual = interfaceLanguage.ToInterfaceLanguage();
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(null, InternalProgram.NONE)]
[InlineData("", InternalProgram.NONE)]
[InlineData("aaru", InternalProgram.Aaru)]
[InlineData("dic", InternalProgram.DiscImageCreator)]
// [InlineData("dreamdump", InternalProgram.Dreamdump)]
[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.DVDVideo,
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<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<RedumpSystem>())
{
if (_copyProtectionSystems.Contains(redumpSystem))
testData.Add([redumpSystem, true]);
else
testData.Add([redumpSystem, false]);
}
return testData;
}
#endregion
}
}

View File

@@ -0,0 +1,24 @@
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Frontend.Test
{
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, 72)]
[InlineData(null, 72)]
public void GetAllowedDriveSpeedForMediaTypeTest(MediaType? mediaType, int maxExpected)
{
var actual = InterfaceConstants.GetSpeedsForMediaType(mediaType);
Assert.Equal(maxExpected, actual[^1]);
}
}
}

View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0;net10.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="18.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
<PackageReference Include="SabreTools.RedumpLib" Version="[1.9.1]" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="1.27.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.5">
<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,10 +1,26 @@
using MPF.Frontend;
using Xunit;
using Xunit;
namespace MPF.Test.Frontend
namespace MPF.Frontend.Test
{
public class ResultEventArgsTests
{
[Fact]
public void EmptyNeutralTest()
{
var actual = ResultEventArgs.Neutral();
Assert.Null((bool?)actual);
Assert.Empty(actual.Message);
}
[Fact]
public void CustomMessageNeutralTest()
{
string message = "Success!";
var actual = ResultEventArgs.Neutral(message);
Assert.Null((bool?)actual);
Assert.Equal(message, actual.Message);
}
[Fact]
public void EmptySuccessTest()
{

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)
{
var 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

@@ -1,34 +1,15 @@
using System.Collections.Generic;
using System.IO;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Data.Sections;
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()
public void ProcessSpecialFields_Complete()
{
// Create a new SubmissionInfo object
var info = new SubmissionInfo()
@@ -54,9 +35,9 @@ namespace MPF.Test.Frontend.Tools
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
Assert.Null(info.CommonDiscInfo.CommentsSpecialFields);
Assert.Empty(info.CommonDiscInfo.CommentsSpecialFields);
Assert.NotNull(info.CommonDiscInfo.Contents);
Assert.Null(info.CommonDiscInfo.ContentsSpecialFields);
Assert.Empty(info.CommonDiscInfo.ContentsSpecialFields);
// Split the values
string[] splitComments = info.CommonDiscInfo.Comments.Split('\n');
@@ -64,27 +45,11 @@ namespace MPF.Test.Frontend.Tools
// Validate the lines
Assert.Equal(3, splitComments.Length);
Assert.Equal(5, splitContents.Length);
Assert.Equal(4, splitContents.Length);
}
[Fact]
public void ProcessSpecialFieldsNullObjectTest()
{
// Create a new SubmissionInfo object
var info = new SubmissionInfo()
{
CommonDiscInfo = null,
};
// Process the special fields
Formatter.ProcessSpecialFields(info);
// Validate
Assert.Null(info.CommonDiscInfo);
}
[Fact]
public void ProcessSpecialFieldsNullCommentsContentsTest()
public void ProcessSpecialFields_NullStrings()
{
// Create a new SubmissionInfo object
var info = new SubmissionInfo()
@@ -110,9 +75,9 @@ namespace MPF.Test.Frontend.Tools
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
Assert.Null(info.CommonDiscInfo.CommentsSpecialFields);
Assert.Empty(info.CommonDiscInfo.CommentsSpecialFields);
Assert.NotNull(info.CommonDiscInfo.Contents);
Assert.Null(info.CommonDiscInfo.ContentsSpecialFields);
Assert.Empty(info.CommonDiscInfo.ContentsSpecialFields);
// Split the values
string[] splitComments = info.CommonDiscInfo.Comments.Split('\n');
@@ -124,7 +89,7 @@ namespace MPF.Test.Frontend.Tools
}
[Fact]
public void ProcessSpecialFieldsNullDictionariesTest()
public void ProcessSpecialFields_EmptyDictionaries()
{
// Create a new SubmissionInfo object
var info = new SubmissionInfo()
@@ -132,10 +97,10 @@ namespace MPF.Test.Frontend.Tools
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = "This is a comments line\n[T:ISBN] ISBN Value",
CommentsSpecialFields = null,
CommentsSpecialFields = [],
Contents = "This is a contents line\n[T:GF] Game Footage",
ContentsSpecialFields = null,
ContentsSpecialFields = [],
}
};
@@ -144,9 +109,9 @@ namespace MPF.Test.Frontend.Tools
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
Assert.Null(info.CommonDiscInfo.CommentsSpecialFields);
Assert.Empty(info.CommonDiscInfo.CommentsSpecialFields);
Assert.NotNull(info.CommonDiscInfo.Contents);
Assert.Null(info.CommonDiscInfo.ContentsSpecialFields);
Assert.Empty(info.CommonDiscInfo.ContentsSpecialFields);
// Split the values
string[] splitComments = info.CommonDiscInfo.Comments.Split('\n');

View File

@@ -0,0 +1,837 @@
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_MPRESS()
{
List<string> protections =
[
"MPRESS",
"MPRESS 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)];
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, "SafeDisc 0.00.000, SafeDisc 0.00.000-1.11.111, SafeDisc 3+ (DVD), SafeDisc Lite")]
[InlineData(3, "SafeDisc 0.00.000, SafeDisc 3+ (DVD), SafeDisc Lite")]
[InlineData(4, "SafeDisc 3+ (DVD), 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 1.11.111 / 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)];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal(expected, sanitized);
}
[Fact]
public void SanitizeFoundProtectsion_SafeDisc_MacrovisionSecurityDriver()
{
List<string> protections =
[
"Macrovision Protection File [Likely indicates either SafeDisc 1.45.011+ (CD) or CDS-300]",
"Macrovision Security Driver 4.00.060 / SafeDisc 4.00.000-4.70.000",
"SafeDisc 4.00.000-4.00.003",
"SafeDisc 4.00.002, Macrovision Protected Application"
];
string expected = "SafeDisc 4.00.002";
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)];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal(protections[0], sanitized);
}
[Fact]
public void SanitizeFoundProtections_StarForce_Keyless()
{
List<string> protections =
[
"StarForce Keyless",
"StarForce Keyless - ",
"StarForce Keyless - ESPY97LS8FHNYR52JDWL8D6FJ",
"StarForce Keyless - UAYA-RPMVHJ-ZRY45G-ETUJ36-WL922B",
];
string sanitized = ProtectionTool.SanitizeFoundProtections(protections);
Assert.Equal("StarForce Keyless", 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,24 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace MPF.Frontend.ComboBoxItems
{
/// <summary>
/// A generic combo box element
/// </summary>
/// <typeparam name="T">Enum type representing the possible values</typeparam>
public class Element<T> : IEquatable<Element<T>>, IElement where T : struct, Enum
/// <typeparam name="TEnum">Enum type representing the possible values</typeparam>
public class Element<TEnum> : IEquatable<Element<TEnum>>, IElement where TEnum : struct, Enum
{
private readonly T Data;
private readonly TEnum Data;
public Element(T data) => Data = data;
public Element(TEnum data) => Data = data;
/// <summary>
/// Allow elements to be used as their internal enum type
/// </summary>
/// <param name="item"></param>
public static implicit operator T? (Element<T> item) => item?.Data;
public static implicit operator TEnum?(Element<TEnum> item) => item?.Data;
/// <inheritdoc/>
public string Name => EnumExtensions.GetLongName(Data);
@@ -28,7 +27,7 @@ namespace MPF.Frontend.ComboBoxItems
/// <summary>
/// Internal enum value
/// </summary>
public T Value => Data;
public TEnum Value => Data;
/// <summary>
/// Determine if the item is selected or not
@@ -40,23 +39,22 @@ 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<TEnum>> GenerateElements()
{
return Enum.GetValues(typeof(T))
.OfType<T>()
.Select(e => new Element<T>(e));
var enumArr = (TEnum[])Enum.GetValues(typeof(TEnum));
return [.. Array.ConvertAll(enumArr, e => new Element<TEnum>(e))];
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return Equals(obj as Element<T>);
return Equals(obj as Element<TEnum>);
}
/// <inheritdoc/>
public bool Equals(Element<T>? other)
public bool Equals(Element<TEnum>? other)
{
if (other == null)
if (other is null)
return false;
return Name == other.Name;

View File

@@ -5,6 +5,6 @@
/// <summary>
/// Display name for the combo box element
/// </summary>
string Name { get; }
public string Name { get; }
}
}

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,10 +80,11 @@ namespace MPF.Frontend.ComboBoxItems
.OrderBy(s => s.LongName())
.ToList()
);
#endif
var systemsValues = new List<RedumpSystemComboBoxItem>
{
new RedumpSystemComboBoxItem((RedumpSystem?)null),
new((RedumpSystem?)null),
};
foreach (var group in mapping)
@@ -89,7 +105,7 @@ namespace MPF.Frontend.ComboBoxItems
/// <inheritdoc/>
public bool Equals(RedumpSystemComboBoxItem? other)
{
if (other == null)
if (other is null)
return false;
return Value == other.Value;

View File

@@ -1,8 +1,7 @@
using System;
using BinaryObjectScanner;
using MPF.Frontend;
namespace MPF.CLI
namespace MPF.Frontend
{
public static class ConsoleLogger
{
@@ -11,7 +10,14 @@ namespace MPF.CLI
/// </summary>
public static void ProgressUpdated(object? sender, ResultEventArgs value)
{
Console.WriteLine(value.Message);
string prefix = (bool?)value switch
{
true => "SUCCESS: ",
false => "FAILURE: ",
_ => "",
};
Console.WriteLine($"{prefix}{value.Message}");
}
/// <summary>

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;
@@ -86,11 +86,15 @@ namespace MPF.Frontend
// Sanitize a Windows-formatted long device path
if (devicePath.StartsWith("\\\\.\\"))
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
devicePath = devicePath["\\\\.\\".Length..];
#else
devicePath = devicePath.Substring("\\\\.\\".Length);
#endif
// Create and validate the drive info object
var driveInfo = new DriveInfo(devicePath);
if (driveInfo == null || driveInfo == default)
if (driveInfo is null || driveInfo == default)
return null;
// Fill in the rest of the data
@@ -106,7 +110,7 @@ namespace MPF.Frontend
private void PopulateFromDriveInfo(DriveInfo? driveInfo)
{
// If we have an invalid DriveInfo, just return
if (driveInfo == null || driveInfo == default)
if (driveInfo is null || driveInfo == default)
return;
// Populate the data fields
@@ -136,8 +140,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 is null ? "\0" : d1.Name;
string d2Name = d2?.Name is null ? "\0" : d2.Name;
return d1Name.CompareTo(d2Name);
});
return [.. drives];
}
/// <summary>
@@ -147,6 +156,7 @@ namespace MPF.Frontend
/// <returns>The detected media type, if possible</returns>
public MediaType? GetMediaType(RedumpSystem? system)
{
#pragma warning disable IDE0010
// Take care of the non-optical stuff first
switch (InternalDriveType)
{
@@ -206,6 +216,7 @@ namespace MPF.Frontend
case RedumpSystem.SonyPlayStationPortable:
return MediaType.UMD;
}
#pragma warning restore IDE0010
// Handle optical media by size and filesystem
if (TotalSize >= 0 && TotalSize <= 800_000_000 && (DriveFormat == "CDFS" || DriveFormat == "UDF"))
@@ -223,7 +234,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 +263,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
@@ -279,10 +288,14 @@ namespace MPF.Frontend
{
CimKeyedCollection<CimProperty> properties = instance.CimInstanceProperties;
uint? mediaType = properties["MediaType"]?.Value as uint?;
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
if (mediaType is not 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 is not null && d.Name[0] == devId)
d.InternalDriveType = Frontend.InternalDriveType.Floppy;
});
}
}
}
@@ -292,7 +305,7 @@ namespace MPF.Frontend
}
#endif
return drives;
return [.. drives];
}
/// <summary>
@@ -302,6 +315,7 @@ namespace MPF.Frontend
/// <returns>InternalDriveType, if possible, null on error</returns>
internal static InternalDriveType? ToInternalDriveType(DriveType driveType)
{
#pragma warning disable IDE0072
return driveType switch
{
DriveType.CDRom => (InternalDriveType?)Frontend.InternalDriveType.Optical,
@@ -309,6 +323,7 @@ namespace MPF.Frontend
DriveType.Removable => (InternalDriveType?)Frontend.InternalDriveType.Removable,
_ => null,
};
#pragma warning restore IDE0072
}
#endregion

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
@@ -90,11 +81,7 @@ namespace MPF.Frontend
public int? Speed
{
get => _executionContext?.Speed;
set
{
if (_executionContext != null)
_executionContext.Speed = value;
}
set => _executionContext?.Speed = value;
}
/// <inheritdoc cref="Extensions.LongName(RedumpSystem?)/>
@@ -118,16 +105,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 +121,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,64 +129,116 @@ 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 is null && _internalProgram != InternalProgram.Redumper)
{
var processor = new Processors.Aaru(_system, _type);
var missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.Aaru;
}
if (programFound == null && _internalProgram != InternalProgram.DiscImageCreator)
{
var processor = new Processors.DiscImageCreator(_system, _type);
var missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.DiscImageCreator;
}
if (programFound == null && _internalProgram != InternalProgram.Redumper)
{
var processor = new Processors.Redumper(_system, _type);
var missingFiles = processor.FoundAllFiles(outputDirectory, outputFilename);
var processor = new Redumper(_system);
var missingFiles = processor.FoundAllFiles(mediaType, outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.Redumper;
}
if (programFound is null && _internalProgram != InternalProgram.DiscImageCreator)
{
var processor = new DiscImageCreator(_system);
var missingFiles = processor.FoundAllFiles(mediaType, outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.DiscImageCreator;
}
if (programFound is null && _internalProgram != InternalProgram.Aaru)
{
var processor = new Aaru(_system);
var missingFiles = processor.FoundAllFiles(mediaType, outputDirectory, outputFilename);
if (missingFiles.Count == 0)
programFound = InternalProgram.Aaru;
}
// if (programFound is null && _internalProgram != InternalProgram.Dreamdump)
// {
// var processor = new Dreamdump(_system);
// var missingFiles = processor.FoundAllFiles(mediaType, outputDirectory, outputFilename);
// if (missingFiles.Count == 0)
// programFound = InternalProgram.Dreamdump;
// }
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 is null && _internalProgram != InternalProgram.Redumper)
{
var processor = new Redumper(_system);
if (processor.FoundAnyFiles(mediaType, outputDirectory, outputFilename))
programFound = InternalProgram.Redumper;
}
if (programFound is null && _internalProgram != InternalProgram.DiscImageCreator)
{
var processor = new DiscImageCreator(_system);
if (processor.FoundAnyFiles(mediaType, outputDirectory, outputFilename))
programFound = InternalProgram.DiscImageCreator;
}
if (programFound is null && _internalProgram != InternalProgram.Aaru)
{
var processor = new Aaru(_system);
if (processor.FoundAnyFiles(mediaType, outputDirectory, outputFilename))
programFound = InternalProgram.Aaru;
}
// if (programFound is null && _internalProgram != InternalProgram.Dreamdump)
// {
// var processor = new Dreamdump(_system);
// if (processor.FoundAnyFiles(mediaType, outputDirectory, outputFilename))
// programFound = InternalProgram.Dreamdump;
// }
return programFound;
}
/// <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)
{
#pragma warning disable IDE0072
_executionContext = _internalProgram switch
{
InternalProgram.Aaru => new ExecutionContexts.Aaru.ExecutionContext(parameters) { ExecutablePath = _options.AaruPath },
InternalProgram.DiscImageCreator => new ExecutionContexts.DiscImageCreator.ExecutionContext(parameters) { ExecutablePath = _options.DiscImageCreatorPath },
// InternalProgram.Dreamdump => new ExecutionContexts.Dreamdump.ExecutionContext(parameters) { ExecutablePath = _options.DreamdumpPath },
InternalProgram.Redumper => new ExecutionContexts.Redumper.ExecutionContext(parameters) { ExecutablePath = _options.RedumperPath },
// If no dumping program found, set to null
InternalProgram.NONE => null,
_ => null,
};
#pragma warning restore IDE0072
// Set system, type, and drive
if (_executionContext != null)
if (_executionContext is not null)
{
_executionContext.System = _system;
_executionContext.Type = _type;
_executionContext.RedumpSystem = _system;
_executionContext.MediaType = mediaType;
// Set some parameters, if not already set
OutputPath ??= _executionContext.OutputPath!;
_drive ??= Drive.Create(InternalDriveType.Optical, _executionContext.InputPath!);
}
return _executionContext != null;
return _executionContext is not null;
}
/// <summary>
@@ -218,47 +248,52 @@ 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 Aaru(_system),
InternalProgram.CleanRip => new CleanRip(_system),
InternalProgram.DiscImageCreator => new DiscImageCreator(_system),
// InternalProgram.Dreamdump => new Dreamdump(_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,
_ => null,
};
return _processor != null;
return _processor is not null;
}
/// <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 is not null && mediaType != MediaType.NONE)
{
// If drive letter is invalid, skip this
if (_drive == null)
if (_drive is null)
return null;
#pragma warning disable IDE0072
// 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.Dreamdump => new ExecutionContexts.Dreamdump.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,
_ => null,
};
#pragma warning restore IDE0072
// Generate and return the param string
return _executionContext?.GenerateParameters();
@@ -279,9 +314,10 @@ 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 static bool DoesSupportDriveSpeed(MediaType? mediaType)
{
return _type switch
#pragma warning disable IDE0072
return mediaType switch
{
MediaType.CDROM
or MediaType.DVD
@@ -289,24 +325,35 @@ namespace MPF.Frontend
or MediaType.HDDVD
or MediaType.BluRay
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => true,
or MediaType.NintendoWiiOpticalDisc
or MediaType.NintendoWiiUOpticalDisc => true,
_ => false,
};
#pragma warning restore IDE0072
}
/// <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)
if (_processor is 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 is null)
return false;
return _processor.FoundAnyFiles(mediaType, outputDirectory, outputFilename);
}
/// <inheritdoc cref="BaseExecutionContext.GetDefaultExtension(MediaType?)"/>
public string? GetDefaultExtension(MediaType? mediaType)
{
if (_executionContext == null)
if (_executionContext is null)
return null;
return _executionContext.GetDefaultExtension(mediaType);
@@ -315,7 +362,7 @@ namespace MPF.Frontend
/// <inheritdoc cref="BaseExecutionContext.GetMediaType()"/>
public MediaType? GetMediaType()
{
if (_executionContext == null)
if (_executionContext is null)
return null;
return _executionContext.GetMediaType();
@@ -324,15 +371,19 @@ 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)
if (_system is null)
return ResultEventArgs.Failure("Please select a valid system");
#pragma warning disable IDE0072
// If we're on an unsupported type, update the status accordingly
return _type switch
return mediaType switch
{
// Null means it will be handled by the program
null => ResultEventArgs.Success("Ready to dump"),
// Fully supported types
MediaType.BluRay
or MediaType.CDROM
@@ -342,28 +393,30 @@ 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"),
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"),
};
#pragma warning restore IDE0072
}
/// <inheritdoc cref="BaseExecutionContext.IsDumpingCommand()"/>
public bool IsDumpingCommand()
{
if (_executionContext == null)
if (_executionContext is null)
return false;
return _executionContext.IsDumpingCommand();
@@ -384,20 +437,29 @@ 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)
if (_executionContext is null)
return ResultEventArgs.Failure("Error! Current configuration is not supported!");
// Build default console progress indicators if none exist
if (progress is null)
{
var temp = new Progress<ResultEventArgs>();
temp.ProgressChanged += ConsoleLogger.ProgressUpdated;
progress = temp;
}
// Check that we have the basics for dumping
ResultEventArgs result = IsValidForDump();
if (!result)
ResultEventArgs result = IsValidForDump(mediaType);
if (result == false)
return result;
// Execute internal tool
progress?.Report(ResultEventArgs.Success($"Executing {_internalProgram}... please wait!"));
progress?.Report(ResultEventArgs.Neutral($"Executing {_internalProgram}... please wait!"));
var directoryName = Path.GetDirectoryName(OutputPath);
if (!string.IsNullOrEmpty(directoryName))
@@ -427,154 +489,173 @@ namespace MPF.Frontend
ProcessUserInfoDelegate? processUserInfo = null,
SubmissionInfo? seedInfo = null)
{
if (_processor == null)
if (_processor is 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 is null)
{
var temp = new Progress<ResultEventArgs>();
temp.ProgressChanged += ConsoleLogger.ProgressUpdated;
resultProgress = temp;
}
if (protectionProgress is null)
{
var temp = new Progress<ProtectionProgress>();
temp.ProgressChanged += ConsoleLogger.ProgressUpdated;
protectionProgress = temp;
}
resultProgress.Report(ResultEventArgs.Neutral("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)
// If a standard log zip was provided, replace the suffix with ".tmp" for easier processing
if (outputFilename.EndsWith("_logs.zip", StringComparison.OrdinalIgnoreCase))
{
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!");
int zipSuffixIndex = outputFilename.LastIndexOf("_logs.zip", StringComparison.OrdinalIgnoreCase);
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
outputFilename = outputFilename[..zipSuffixIndex] + ".tmp";
#else
outputFilename = outputFilename.Substring(0, zipSuffixIndex) + ".tmp";
#endif
}
// Determine the media type from the processor
MediaType? mediaType = _processor.DetermineMediaType(outputDirectory, outputFilename);
if (mediaType is null)
return ResultEventArgs.Failure("Could not determine the media type from output files...");
// Extract the information from the output files
resultProgress?.Report(ResultEventArgs.Success("Extracting output information from output files..."));
resultProgress.Report(ResultEventArgs.Neutral("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 is null)
return ResultEventArgs.Failure("There was an issue extracting information!");
else
resultProgress.Report(ResultEventArgs.Success("Extracting information complete!"));
// Inject seed submission info data, if necessary
if (seedInfo != null)
if (seedInfo is not null)
{
resultProgress?.Report(ResultEventArgs.Success("Injecting user-supplied information..."));
Builder.InjectSubmissionInformation(submissionInfo, seedInfo);
resultProgress?.Report(ResultEventArgs.Success("Information injection complete!"));
resultProgress.Report(ResultEventArgs.Neutral("Injecting user-supplied information..."));
submissionInfo = Builder.InjectSubmissionInformation(submissionInfo, seedInfo);
resultProgress.Report(ResultEventArgs.Success("Information injection complete!"));
}
// Get user-modifiable information if confugured to
if (_options.PromptForDiscInformation && processUserInfo != null)
// Get user-modifiable information if configured to
if (_options.PromptForDiscInformation && processUserInfo is not null)
{
resultProgress?.Report(ResultEventArgs.Success("Waiting for additional disc information..."));
bool? filledInfo = processUserInfo(ref submissionInfo);
resultProgress.Report(ResultEventArgs.Neutral("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.Neutral("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.Neutral("Formatting information..."));
var formattedValues = Formatter.FormatOutputData(submissionInfo, _options.EnableRedumpCompatibility, out string? formatResult);
if (formattedValues == null)
resultProgress?.Report(ResultEventArgs.Failure(formatResult));
if (formattedValues is null)
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.Neutral("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 is not null && submissionInfo.CopyProtection.FullProtections.Count > 0)
{
if (_options.ScanForProtection)
{
resultProgress?.Report(ResultEventArgs.Success("Writing protection information file..."));
resultProgress.Report(ResultEventArgs.Neutral("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.Neutral($"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.Neutral("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, _options.LogCompression, 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.Neutral("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.Neutral("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 +663,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)
if (_drive is 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,17 +683,17 @@ 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 is null || !ParametersValid(mediaType))
return ResultEventArgs.Failure("Error! Current configuration is not supported!");
// Fix the output paths, just in case
OutputPath = FrontendTool.NormalizeOutputPaths(OutputPath, false);
// Validate that the output path isn't on the dumping drive
if (_drive?.Name != null && OutputPath.StartsWith(_drive.Name))
if (_drive?.Name is not null && OutputPath.StartsWith(_drive.Name))
return ResultEventArgs.Failure("Error! Cannot output to same drive that is being dumped!");
// Validate that the required program exists
@@ -621,11 +702,11 @@ namespace MPF.Frontend
// Validate that the dumping drive doesn't contain the executable
string fullExecutablePath = Path.GetFullPath(_executionContext.ExecutablePath!);
if (_drive?.Name != null && fullExecutablePath.StartsWith(_drive.Name))
if (_drive?.Name is not null && fullExecutablePath.StartsWith(_drive.Name))
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,14 +716,14 @@ 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)
if (lines is null)
{
status = "No formatted data found to write!";
return false;
@@ -663,10 +744,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 +760,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>
@@ -690,53 +768,48 @@ namespace MPF.Frontend
private static bool WriteOutputData(string? outputDirectory, string? filenameSuffix, SubmissionInfo? info, bool includedArtifacts)
{
// Check to see if the input is valid
if (info == null)
if (info is null)
return false;
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
// Absorb the exception
return false;
}
@@ -747,7 +820,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 +828,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 is null || info.CopyProtection.FullProtections.Count == 0)
return true;
// Now write out to a generic file
@@ -773,16 +846,22 @@ 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;
if (hideDriveLetters)
scanPath = Path.DirectorySeparatorChar + key.Substring((Path.GetPathRoot(key) ?? String.Empty).Length);
#if NETCOREAPP || NETSTANDARD2_1_OR_GREATER
scanPath = Path.DirectorySeparatorChar + key[(Path.GetPathRoot(key) ?? string.Empty).Length..];
#else
scanPath = Path.DirectorySeparatorChar + key.Substring((Path.GetPathRoot(key) ?? string.Empty).Length);
#endif
List<string>? scanResult = info.CopyProtection.FullProtections[key];
if (scanResult == null)
if (scanResult is null)
sw.WriteLine($"{scanPath}: None");
else
sw.WriteLine($"{scanPath}: {string.Join(", ", [.. scanResult])}");
@@ -790,73 +869,13 @@ namespace MPF.Frontend
}
catch
{
// We don't care what the error is right now
// Absorb the exception
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,9 @@ using System.Collections.Concurrent;
#endif
using System.Reflection;
using SabreTools.RedumpLib.Data;
using DreamdumpSectorOrder = MPF.ExecutionContexts.Dreamdump.SectorOrder;
using LogCompression = MPF.Processors.LogCompression;
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
@@ -48,8 +51,8 @@ namespace MPF.Frontend
#endif
}
if (method != null)
return method.Invoke(null, new[] { value }) as string ?? string.Empty;
if (method is not null)
return method.Invoke(null, [value]) as string ?? string.Empty;
else
return string.Empty;
}
@@ -60,6 +63,49 @@ namespace MPF.Frontend
}
}
/// <summary>
/// Get the string representation of the InterfaceLanguage enum values
/// </summary>
/// <param name="lang">InterfaceLanguage value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this InterfaceLanguage lang)
=> ((InterfaceLanguage?)lang).LongName();
/// <summary>
/// Get the string representation of the InterfaceLanguage enum values
/// </summary>
/// <param name="lang">InterfaceLanguage value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this InterfaceLanguage? lang)
{
return lang switch
{
InterfaceLanguage.AutoDetect => "Auto Detect",
InterfaceLanguage.English => "English",
InterfaceLanguage.French => "Français",
InterfaceLanguage.German => "Deutsch",
InterfaceLanguage.Italian => "Italiano",
InterfaceLanguage.Japanese => "日本語",
InterfaceLanguage.Korean => "한국어",
InterfaceLanguage.Polish => "Polski",
InterfaceLanguage.Russian => "Русский",
InterfaceLanguage.Spanish => "Español",
InterfaceLanguage.Swedish => "Svenska",
InterfaceLanguage.Ukrainian => "Українська",
InterfaceLanguage.L337 => "L337",
_ => "Unknown",
};
}
/// <summary>
/// Get the string representation of the InternalProgram enum values
/// </summary>
/// <param name="prog">InternalProgram value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this InternalProgram prog)
=> ((InternalProgram?)prog).LongName();
/// <summary>
/// Get the string representation of the InternalProgram enum values
/// </summary>
@@ -67,12 +113,13 @@ 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
InternalProgram.Aaru => "Aaru",
InternalProgram.DiscImageCreator => "DiscImageCreator",
// InternalProgram.Dreamdump => "Dreamdump",
InternalProgram.Redumper => "Redumper",
#endregion
@@ -91,6 +138,39 @@ namespace MPF.Frontend
};
}
/// <summary>
/// Get the string representation of the LogCompression enum values
/// </summary>
/// <param name="comp">LogCompression value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this LogCompression comp)
=> ((LogCompression?)comp).LongName();
/// <summary>
/// Get the string representation of the LogCompression enum values
/// </summary>
/// <param name="comp">LogCompression value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this LogCompression? comp)
{
return comp switch
{
LogCompression.DeflateDefault => "ZIP using Deflate (Level 5)",
LogCompression.DeflateMaximum => "ZIP using Deflate (Level 9)",
LogCompression.Zstd19 => "ZIP using Zstd (Level 19)",
_ => "Unknown",
};
}
/// <summary>
/// Get the string representation of the RedumperReadMethod enum values
/// </summary>
/// <param name="method">RedumperReadMethod value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this RedumperReadMethod method)
=> ((RedumperReadMethod?)method).LongName();
/// <summary>
/// Get the string representation of the RedumperReadMethod enum values
/// </summary>
@@ -98,17 +178,24 @@ 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",
};
}
/// <summary>
/// Get the string representation of the RedumperSectorOrder enum values
/// </summary>
/// <param name="order">RedumperSectorOrder value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this RedumperSectorOrder order)
=> ((RedumperSectorOrder?)order).LongName();
/// <summary>
/// Get the string representation of the RedumperSectorOrder enum values
/// </summary>
@@ -116,7 +203,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 +215,234 @@ namespace MPF.Frontend
};
}
/// <summary>
/// Get the string representation of the RedumperDriveType enum values
/// </summary>
/// <param name="type">RedumperDriveType value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this RedumperDriveType type)
=> ((RedumperDriveType?)type).LongName();
/// <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.MTK8A => "MTK8A",
RedumperDriveType.MTK8B => "MTK8B",
RedumperDriveType.MTK8C => "MTK8C",
RedumperDriveType.MTK3 => "MTK3",
RedumperDriveType.MTK2 => "MTK2",
RedumperDriveType.NONE => "Default",
_ => "Unknown",
};
}
#endregion
#region Convert to Short Name
/// <summary>
/// Get the short string representation of the InterfaceLanguage enum values
/// </summary>
/// <param name="lang">InterfaceLanguage value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string ShortName(this InterfaceLanguage lang)
=> ((InterfaceLanguage?)lang).ShortName();
/// <summary>
/// Get the short string representation of the InterfaceLanguage enum values
/// </summary>
/// <param name="lang">InterfaceLanguage value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string ShortName(this InterfaceLanguage? lang)
{
return lang switch
{
InterfaceLanguage.AutoDetect => "auto",
InterfaceLanguage.English => "eng",
InterfaceLanguage.French => "fra",
InterfaceLanguage.German => "deu",
InterfaceLanguage.Italian => "ita",
InterfaceLanguage.Japanese => "jpn",
InterfaceLanguage.Korean => "kor",
InterfaceLanguage.Polish => "pol",
InterfaceLanguage.Russian => "rus",
InterfaceLanguage.Spanish => "spa",
InterfaceLanguage.Swedish => "swe",
InterfaceLanguage.Ukrainian => "ukr",
InterfaceLanguage.L337 => "l37",
_ => "Unknown",
};
}
/// <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.Dreamdump => "dreamdump",
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 DreamdumpSectorOrder enum value for a given string
/// </summary>
/// <param name="order">String value to convert</param>
/// <returns>DreamdumpSectorOrder represented by the string, if possible</returns>
public static DreamdumpSectorOrder ToDreamdumpSectorOrder(this string? order)
{
return (order?.ToLowerInvariant()) switch
{
"data_c2_sub"
or "data c2 sub"
or "data-c2-sub"
or "datac2sub" => DreamdumpSectorOrder.DATA_C2_SUB,
"data_sub_c2"
or "data sub c2"
or "data-sub-c2"
or "datasubc2" => DreamdumpSectorOrder.DATA_SUB_C2,
"data_sub"
or "data sub"
or "data-sub"
or "datasub" => DreamdumpSectorOrder.DATA_SUB,
"data_c2"
or "data c2"
or "data-c2"
or "datac2" => DreamdumpSectorOrder.DATA_C2,
_ => DreamdumpSectorOrder.NONE,
};
}
/// <summary>
/// Get the InterfaceLanguage enum value for a given string
/// </summary>
/// <param name="internalLanguage">String value to convert</param>
/// <returns>InterfaceLanguage represented by the string, if possible</returns>
public static InterfaceLanguage ToInterfaceLanguage(this string? internalLanguage)
{
return (internalLanguage?.ToLowerInvariant()) switch
{
"auto" or "autodetect" or "auto detect" => InterfaceLanguage.AutoDetect,
"eng" or "english" => InterfaceLanguage.English,
"fra" or "french" or "français" => InterfaceLanguage.French,
"deu" or "german" or "deutsch" => InterfaceLanguage.German,
"ita" or "italian" or "italiano" => InterfaceLanguage.Italian,
"jpn" or "japanese" or "日本語" => InterfaceLanguage.Japanese,
"kor" or "korean" or "한국어" => InterfaceLanguage.Korean,
"pol" or "polish" or "polski" => InterfaceLanguage.Polish,
"rus" or "russian" or "русский" => InterfaceLanguage.Russian,
"spa" or "spanish" or "español" => InterfaceLanguage.Spanish,
"swe" or "swedish" or "svenska" => InterfaceLanguage.Swedish,
"ukr" or "ukranian" or "українська" => InterfaceLanguage.Ukrainian,
"l37" or "l337" => InterfaceLanguage.L337,
_ => InterfaceLanguage.AutoDetect,
};
}
/// <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,
// "dreamdump" => InternalProgram.Dreamdump,
"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 LogCompression enum value for a given string
/// </summary>
/// <param name="logCompression">String value to convert</param>
/// <returns>LogCompression represented by the string, if possible</returns>
public static LogCompression ToLogCompression(this string? logCompression)
{
return (logCompression?.ToLowerInvariant()) switch
{
"deflate"
or "deflatedefault"
or "zip" => LogCompression.DeflateDefault,
"deflatemaximum"
or "max"
or "maximum" => LogCompression.DeflateMaximum,
"zstd"
or "zstd19" => LogCompression.Zstd19,
_ => LogCompression.DeflateDefault,
};
}
/// <summary>
/// Get the RedumperReadMethod enum value for a given string
/// </summary>
@@ -143,10 +454,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 +489,82 @@ 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,
"mtk8a"
or "mtk_8a"
or "lg_asus8a"
or "lg-asus8a"
or "lgasus8a"
or "lg_asus_8a"
or "lg-asus-8a"
or "lg_asu8a"
or "lg-asu8a"
or "lgasu8a"
or "lg_asu_8a"
or "lg-asu-8a" => RedumperDriveType.MTK8A,
"mtk8b"
or "mtk_8b"
or "lg_asus8b"
or "lg-asus8b"
or "lgasus8b"
or "lg_asus_8b"
or "lg-asus-8b"
or "lg_asu8b"
or "lg-asu8b"
or "lgasu8b"
or "lg_asu_8b"
or "lg-asu-8b" => RedumperDriveType.MTK8B,
"mtk8c"
or "mtk_8c"
or "lg_asus8c"
or "lg-asus8c"
or "lgasus8c"
or "lg_asus_8c"
or "lg-asus-8c"
or "lg_asu8c"
or "lg-asu8c"
or "lgasu8c"
or "lg_asu_8c"
or "lg-asu-8c" => RedumperDriveType.MTK8C,
"mtk3"
or "mtk3"
or "lg_asus3"
or "lg-asus3"
or "lgasus3"
or "lg_asus_3"
or "lg-asus-3"
or "lg_asu3"
or "lg-asu3"
or "lgasu3"
or "lg_asu_3"
or "lg-asu-3" => RedumperDriveType.MTK3,
"mtk2"
or "mtk2"
or "lg_asus2"
or "lg-asus2"
or "lgasus2"
or "lg_asus_2"
or "lg-asus-2"
or "lg_asu2"
or "lg-asu2"
or "lgasu2"
or "lg_asu_2"
or "lg-asu-2" => RedumperDriveType.MTK2,
_ => RedumperDriveType.NONE,
};
}
#endregion
#region Functionality Support
@@ -191,11 +574,13 @@ namespace MPF.Frontend
/// </summary>
public static bool SupportsAntiModchipScans(this RedumpSystem? system)
{
#pragma warning disable IDE0072
return system switch
{
RedumpSystem.SonyPlayStation => true,
_ => false,
};
#pragma warning restore IDE0072
}
/// <summary>
@@ -203,9 +588,11 @@ namespace MPF.Frontend
/// </summary>
public static bool SupportsCopyProtectionScans(this RedumpSystem? system)
{
#pragma warning disable IDE0072
return system switch
{
RedumpSystem.AppleMacintosh => true,
RedumpSystem.DVDVideo => true,
RedumpSystem.EnhancedCD => true,
RedumpSystem.IBMPCcompatible => true,
RedumpSystem.PalmOS => true,
@@ -214,6 +601,7 @@ namespace MPF.Frontend
RedumpSystem.SonyElectronicBook => true,
_ => false,
};
#pragma warning restore IDE0072
}
#endregion

View File

@@ -1,5 +1,32 @@
namespace MPF.Frontend
{
/// <summary>
/// Interface language
/// </summary>
public enum InterfaceLanguage
{
/// <summary>
/// Default to auto-detecting language
/// </summary>
AutoDetect = 0,
// Selectable languages (in Manual Window dropdown)
English,
French,
German,
Italian,
Japanese,
Korean,
Polish,
Russian,
Spanish,
Swedish,
Ukrainian,
// Hidden languages (not in Main Window dropdown)
L337,
}
/// <summary>
/// Drive type for dumping
/// </summary>
@@ -21,6 +48,7 @@ namespace MPF.Frontend
// Dumping support
Aaru,
DiscImageCreator,
// Dreamdump,
Redumper,
// Verification support only
@@ -35,9 +63,10 @@ namespace MPF.Frontend
/// </summary>
public enum LogLevel
{
USER,
USER_GENERIC,
USER_SUCCESS,
VERBOSE,
ERROR,
SECRET,
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using SabreTools.CommandLine;
namespace MPF.Frontend.Features
{
public class ListCodesFeature : Feature
{
#region Feature Definition
public const string DisplayName = "listcodes";
private static readonly string[] _flags = ["lc", "listcodes"];
private const string _description = "List supported comment/content site codes";
#endregion
public ListCodesFeature()
: base(DisplayName, _flags, _description)
{
}
/// <inheritdoc/>
public override bool Execute()
{
Console.WriteLine("Supported Site Codes:");
foreach (string siteCode in SabreTools.RedumpLib.Data.Extensions.ListSiteCodes())
{
Console.WriteLine(siteCode);
}
return true;
}
/// <inheritdoc/>
public override bool VerifyInputs() => true;
}
}

View File

@@ -0,0 +1,150 @@
using System;
using MPF.Frontend;
using MPF.Frontend.Tools;
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend.Features
{
public class ListConfigFeature : SabreTools.CommandLine.Feature
{
#region Feature Definition
public const string DisplayName = "listconfig";
private static readonly string[] _flags = ["lo", "listconfig"];
private const string _description = "List current configuration values";
#endregion
public ListConfigFeature()
: base(DisplayName, _flags, _description)
{
}
/// <inheritdoc/>
public override bool Execute()
{
// Try to load the current config
var options = OptionsLoader.LoadFromConfig();
if (options.FirstRun)
{
Console.WriteLine("No valid configuration found!");
return true;
}
// Paths
Console.WriteLine("Paths:");
Console.WriteLine($" Aaru Path = {options.AaruPath}");
Console.WriteLine($" DiscImageCreator Path = {options.DiscImageCreatorPath}");
Console.WriteLine($" Redumper Path = {options.RedumperPath}");
Console.WriteLine($" Default Program = {options.InternalProgram.LongName()}");
Console.WriteLine();
// UI Defaults
Console.WriteLine("UI Defaults:");
Console.WriteLine($" Dark Mode = {options.EnableDarkMode}");
Console.WriteLine($" Purp Mode = {options.EnablePurpMode}");
Console.WriteLine($" Custom Background Color = {options.CustomBackgroundColor}");
Console.WriteLine($" Custom Text Color = {options.CustomTextColor}");
Console.WriteLine($" Check for Updates on Startup = {options.CheckForUpdatesOnStartup}");
Console.WriteLine($" Copy Update URL to Clipboard = {options.CopyUpdateUrlToClipboard}");
Console.WriteLine($" Fast Label Update = {options.FastUpdateLabel}");
Console.WriteLine($" Default Interface Language = {options.DefaultInterfaceLanguage.LongName()}");
Console.WriteLine($" Default Output Path = {options.DefaultOutputPath}");
Console.WriteLine($" Default System = {options.DefaultSystem.LongName()}");
Console.WriteLine($" Show Debug Menu Item = {options.ShowDebugViewMenuItem}");
Console.WriteLine();
// Dumping Speeds
Console.WriteLine("Dumping Speeds:");
Console.WriteLine($" Default CD Speed = {options.PreferredDumpSpeedCD}");
Console.WriteLine($" Default DVD Speed = {options.PreferredDumpSpeedDVD}");
Console.WriteLine($" Default HD-DVD Speed = {options.PreferredDumpSpeedHDDVD}");
Console.WriteLine($" Default Blu-ray Speed = {options.PreferredDumpSpeedBD}");
Console.WriteLine();
// Aaru
Console.WriteLine("Aaru-Specific Options:");
Console.WriteLine($" Enable Debug = {options.AaruEnableDebug}");
Console.WriteLine($" Enable Verbose = {options.AaruEnableVerbose}");
Console.WriteLine($" Force Dumping = {options.AaruForceDumping}");
Console.WriteLine($" Reread Count = {options.AaruRereadCount}");
Console.WriteLine($" Strip Personal Data = {options.AaruStripPersonalData}");
Console.WriteLine();
// DiscImageCreator
Console.WriteLine("DiscImageCreator-Specific Options:");
Console.WriteLine($" Multi-Sector Read Flag = {options.DICMultiSectorRead}");
Console.WriteLine($" Multi-Sector Read Value = {options.DICMultiSectorReadValue}");
Console.WriteLine($" Overly-Secure Flags = {options.DICParanoidMode}");
Console.WriteLine($" Quiet Flag = {options.DICQuietMode}");
Console.WriteLine($" CD Reread Count = {options.DICRereadCount}");
Console.WriteLine($" DVD Reread Count = {options.DICDVDRereadCount}");
Console.WriteLine($" Use CMI Flag = {options.DICUseCMIFlag}");
Console.WriteLine();
// Redumper
Console.WriteLine("Redumper-Specific Options:");
Console.WriteLine($" Enable Skeleton = {options.RedumperEnableSkeleton}");
Console.WriteLine($" Enable Verbose = {options.RedumperEnableVerbose}");
Console.WriteLine($" Lead-in Retry Count = {options.RedumperLeadinRetryCount}");
Console.WriteLine($" Non-Redump Mode = {options.RedumperNonRedumpMode}");
Console.WriteLine($" Drive Type = {options.RedumperDriveType.LongName()}");
Console.WriteLine($" Read Method = {options.RedumperReadMethod.LongName()}");
Console.WriteLine($" Sector Order = {options.RedumperSectorOrder.LongName()}");
Console.WriteLine($" Reread Count = {options.RedumperRereadCount}");
Console.WriteLine($" Refine Sector Mode = {options.RedumperRefineSectorMode}");
Console.WriteLine();
// Extra Dumping Options
Console.WriteLine("Extra Dumping Options:");
Console.WriteLine($" Scan for Protection = {options.ScanForProtection}");
Console.WriteLine($" Add Placeholders = {options.AddPlaceholders}");
Console.WriteLine($" Prompt for Media Information = {options.PromptForDiscInformation}");
Console.WriteLine($" Pull All Information = {options.PullAllInformation}");
Console.WriteLine($" Enable Tabs in Input Fields = {options.EnableTabsInInputFields}");
Console.WriteLine($" Enable Redump Compatibility = {options.EnableRedumpCompatibility}");
Console.WriteLine($" Show Disc Eject Reminder = {options.ShowDiscEjectReminder}");
Console.WriteLine($" Ignore Fixed Drives = {options.IgnoreFixedDrives}");
Console.WriteLine($" Add Filename Suffix = {options.AddFilenameSuffix}");
Console.WriteLine($" Output Submission JSON = {options.OutputSubmissionJSON}");
Console.WriteLine($" Include Artifacts = {options.IncludeArtifacts}");
Console.WriteLine($" Compress Log Files = {options.CompressLogFiles}");
Console.WriteLine($" Log Compression = {options.LogCompression.LongName()}");
Console.WriteLine($" Delete Unnecessary Files = {options.DeleteUnnecessaryFiles}");
Console.WriteLine($" Create IRD After Dumping = {options.CreateIRDAfterDumping}");
Console.WriteLine();
// Skip Options
Console.WriteLine("Skip Options:");
Console.WriteLine($" Skip System Detection = {options.SkipSystemDetection}");
Console.WriteLine();
// Protection Scanning Options
Console.WriteLine("Protection Scanning Options:");
Console.WriteLine($" Scan Archives for Protection = {options.ScanArchivesForProtection}");
Console.WriteLine($" Include Debug Protection Information = {options.IncludeDebugProtectionInformation}");
Console.WriteLine($" Hide Drive Letters = {options.HideDriveLetters}");
Console.WriteLine();
// Logging Options
Console.WriteLine("Logging Options:");
Console.WriteLine($" Verbose Logging = {options.VerboseLogging}");
Console.WriteLine($" Open Log Window at Startup = {options.OpenLogWindowAtStartup}");
Console.WriteLine();
// Redump Login Information
Console.WriteLine("Redump Login Information:");
Console.WriteLine($" Retrieve Match Information = {options.RetrieveMatchInformation}");
Console.WriteLine($" Redump Username = {options.RedumpUsername}");
Console.WriteLine($" Redump Password = {(string.IsNullOrEmpty(options.RedumpPassword) ? "[UNSET]" : "[SET]")}");
Console.WriteLine();
return true;
}
/// <inheritdoc/>
public override bool VerifyInputs() => true;
}
}

View File

@@ -0,0 +1,38 @@
using System;
using SabreTools.CommandLine;
namespace MPF.Frontend.Features
{
public class ListMediaTypesFeature : Feature
{
#region Feature Definition
public const string DisplayName = "listmedia";
private static readonly string[] _flags = ["lm", "listmedia"];
private const string _description = "List supported media types";
#endregion
public ListMediaTypesFeature()
: base(DisplayName, _flags, _description)
{
}
/// <inheritdoc/>
public override bool Execute()
{
Console.WriteLine("Supported Media Types:");
foreach (string mediaType in SabreTools.RedumpLib.Data.Extensions.ListMediaTypes())
{
Console.WriteLine(mediaType);
}
return true;
}
/// <inheritdoc/>
public override bool VerifyInputs() => true;
}
}

View File

@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using SabreTools.CommandLine;
namespace MPF.Frontend.Features
{
public class ListProgramsFeature : Feature
{
#region Feature Definition
public const string DisplayName = "listprograms";
private static readonly string[] _flags = ["lp", "listprograms"];
private const string _description = "List supported dumping program outputs";
#endregion
public ListProgramsFeature()
: base(DisplayName, _flags, _description)
{
}
/// <inheritdoc/>
public override bool Execute()
{
Console.WriteLine("Supported Programs:");
foreach (string program in ListPrograms())
{
Console.WriteLine(program);
}
return true;
}
/// <inheritdoc/>
public override bool VerifyInputs() => 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).ShortName()} - {((InternalProgram?)val).LongName()}");
}
return programs;
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using SabreTools.CommandLine;
namespace MPF.Frontend.Features
{
public class ListSystemsFeature : Feature
{
#region Feature Definition
public const string DisplayName = "listsystems";
private static readonly string[] _flags = ["ls", "listsystems"];
private const string _description = "List supported system types";
#endregion
public ListSystemsFeature()
: base(DisplayName, _flags, _description)
{
}
/// <inheritdoc/>
public override bool Execute()
{
Console.WriteLine("Supported Systems:");
foreach (string system in SabreTools.RedumpLib.Data.Extensions.ListSystems())
{
Console.WriteLine(system);
}
return true;
}
/// <inheritdoc/>
public override bool VerifyInputs() => true;
}
}

View File

@@ -0,0 +1,34 @@
using System;
using MPF.Frontend.Tools;
using SabreTools.CommandLine;
namespace MPF.Frontend.Features
{
public class VersionFeature : Feature
{
#region Feature Definition
public const string DisplayName = "version";
private static readonly string[] _flags = ["version"];
private const string _description = "Display the program version";
#endregion
public VersionFeature()
: base(DisplayName, _flags, _description)
{
}
/// <inheritdoc/>
public override bool Execute()
{
Console.WriteLine(FrontendTool.GetCurrentVersion() ?? "Unknown version");
return true;
}
/// <inheritdoc/>
public override bool VerifyInputs() => true;
}
}

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using SabreTools.RedumpLib.Data;
namespace MPF.Frontend
@@ -12,40 +11,36 @@ 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();
/// <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> BD => _speedValues.FindAll(s => s <= 16);
/// <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)
{
#pragma warning disable IDE0072
return type switch
{
MediaType.CDROM
@@ -54,9 +49,13 @@ namespace MPF.Frontend
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => DVD,
MediaType.HDDVD => HDDVD,
MediaType.BluRay => BD,
_ => Unknown,
MediaType.BluRay
or MediaType.NintendoWiiUOpticalDisc => BD,
// Default to CD speed range
_ => CD,
};
#pragma warning restore IDE0072
}
}
}

View File

@@ -2,68 +2,44 @@
<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;net10.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.6.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.5.0]" />
<PackageReference Include="LibIRD" Version="[1.0.1]" />
<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.CommandLine" Version="[1.4.0]" />
<PackageReference Include="SabreTools.RedumpLib" Version="[1.9.1]" />
<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

@@ -1,7 +1,13 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using SabreTools.RedumpLib.Data;
using AaruSettings = MPF.ExecutionContexts.Aaru.SettingConstants;
using DICSettings = MPF.ExecutionContexts.DiscImageCreator.SettingConstants;
// using DreamdumpSectorOrder = MPF.ExecutionContexts.Dreamdump.SectorOrder;
// using DreamdumpSettings = MPF.ExecutionContexts.Dreamdump.SettingConstants;
using LogCompression = MPF.Processors.LogCompression;
using RedumperDriveType = MPF.ExecutionContexts.Redumper.DriveType;
using RedumperReadMethod = MPF.ExecutionContexts.Redumper.ReadMethod;
using RedumperSectorOrder = MPF.ExecutionContexts.Redumper.SectorOrder;
using RedumperSettings = MPF.ExecutionContexts.Redumper.SettingConstants;
@@ -10,6 +16,108 @@ namespace MPF.Frontend
{
public class Options
{
#region Default Paths
/// <summary>
/// Default Aaru path
/// </summary>
private static string DefaultAaruPath
{
get
{
#pragma warning disable IDE0072
string executableName = Environment.OSVersion.Platform switch
{
PlatformID.Unix => "aaru",
PlatformID.MacOSX => "aaru",
_ => "aaru.exe"
};
#pragma warning restore IDE0072
#if NET20 || NET35
return Path.Combine("Programs", Path.Combine("Aaru", executableName));
#else
return Path.Combine("Programs", "Aaru", executableName);
#endif
}
}
/// <summary>
/// Default DiscImageCreator path
/// </summary>
private static string DefaultDiscImageCreatorPath
{
get
{
#pragma warning disable IDE0072
string executableName = Environment.OSVersion.Platform switch
{
PlatformID.Unix => "DiscImageCreator.out",
PlatformID.MacOSX => "DiscImageCreator",
_ => "DiscImageCreator.exe"
};
#pragma warning restore IDE0072
#if NET20 || NET35
return Path.Combine("Programs", Path.Combine("Creator", executableName));
#else
return Path.Combine("Programs", "Creator", executableName);
#endif
}
}
#pragma warning disable IDE0051
/// <summary>
/// Default Dreamdump path
/// </summary>
private static string DefaultDreamdumpPath
{
get
{
#pragma warning disable IDE0072
string executableName = Environment.OSVersion.Platform switch
{
PlatformID.Unix => "dreamdump",
PlatformID.MacOSX => "dreamdump",
_ => "dreamdump.exe"
};
#pragma warning restore IDE0072
#if NET20 || NET35
return Path.Combine("Programs", Path.Combine("Dreamdump", executableName));
#else
return Path.Combine("Programs", "Dreamdump", executableName);
#endif
}
}
#pragma warning restore IDE0051
/// <summary>
/// Default Redumper path
/// </summary>
private static string DefaultRedumperPath
{
get
{
#pragma warning disable IDE0072
string executableName = Environment.OSVersion.Platform switch
{
PlatformID.Unix => "redumper",
PlatformID.MacOSX => "redumper",
_ => "redumper.exe"
};
#pragma warning restore IDE0072
#if NET20 || NET35
return Path.Combine("Programs", Path.Combine("Redumper", executableName));
#else
return Path.Combine("Programs", "Redumper", executableName);
#endif
}
}
#endregion
/// <summary>
/// All settings in the form of a dictionary
/// </summary>
@@ -31,7 +139,7 @@ namespace MPF.Frontend
/// </summary>
public string? AaruPath
{
get { return GetStringSetting(Settings, "AaruPath", "Programs\\Aaru\\Aaru.exe"); }
get { return GetStringSetting(Settings, "AaruPath", DefaultAaruPath); }
set { Settings["AaruPath"] = value; }
}
@@ -40,16 +148,25 @@ namespace MPF.Frontend
/// </summary>
public string? DiscImageCreatorPath
{
get { return GetStringSetting(Settings, "DiscImageCreatorPath", "Programs\\Creator\\DiscImageCreator.exe"); }
get { return GetStringSetting(Settings, "DiscImageCreatorPath", DefaultDiscImageCreatorPath); }
set { Settings["DiscImageCreatorPath"] = value; }
}
// /// <summary>
// /// Path to Dreamdump
// /// </summary>
// public string? DreamdumpPath
// {
// get { return GetStringSetting(Settings, "DreamdumpPath", DefaultDreamdumpPath); }
// set { Settings["DreamdumpPath"] = value; }
// }
/// <summary>
/// Path to Redumper
/// </summary>
public string? RedumperPath
{
get { return GetStringSetting(Settings, "RedumperPath", "Programs\\Redumper\\redumper.exe"); }
get { return GetStringSetting(Settings, "RedumperPath", DefaultRedumperPath); }
set { Settings["RedumperPath"] = value; }
}
@@ -61,7 +178,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 +203,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 +213,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 +223,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 +239,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>
@@ -128,6 +257,22 @@ namespace MPF.Frontend
set { Settings["FastUpdateLabel"] = value.ToString(); }
}
/// <summary>
/// Default interface language to launch MPF into
/// </summary>
public InterfaceLanguage DefaultInterfaceLanguage
{
get
{
var valueString = GetStringSetting(Settings, "DefaultInterfaceLanguage", InterfaceLanguage.AutoDetect.ShortName());
return valueString.ToInterfaceLanguage();
}
set
{
Settings["DefaultInterfaceLanguage"] = value.ShortName();
}
}
/// <summary>
/// Default output path for dumps
/// </summary>
@@ -144,18 +289,18 @@ namespace MPF.Frontend
{
get
{
var valueString = GetStringSetting(Settings, "DefaultSystem", null);
var valueEnum = Extensions.ToRedumpSystem(valueString ?? string.Empty);
var valueString = GetStringSetting(Settings, "DefaultSystem", RedumpSystem.IBMPCcompatible.ToString());
var valueEnum = (valueString ?? string.Empty).ToRedumpSystem();
return valueEnum;
}
set
{
Settings["DefaultSystem"] = value.LongName();
Settings["DefaultSystem"] = value.ToString();
}
}
/// <summary>
/// Default output path for dumps
/// Show the debug menu item
/// </summary>
/// <remarks>This is a hidden setting</remarks>
public bool ShowDebugViewMenuItem
@@ -327,24 +472,54 @@ namespace MPF.Frontend
#endregion
#region Dreamdump
// /// <summary>
// /// Enable options incompatible with redump submissions
// /// </summary>
// public bool DreamdumpNonRedumpMode
// {
// get { return GetBooleanSetting(Settings, "DreamdumpNonRedumpMode", false); }
// set { Settings["DreamdumpNonRedumpMode"] = value.ToString(); }
// }
// /// <summary>
// /// Currently selected default Dreamdump sector order
// /// </summary>
// public DreamdumpSectorOrder DreamdumpSectorOrder
// {
// get
// {
// var valueString = GetStringSetting(Settings, DreamdumpSettings.SectorOrder, DreamdumpSettings.SectorOrderDefault);
// return valueString.ToDreamdumpSectorOrder();
// }
// set
// {
// Settings[DreamdumpSettings.SectorOrder] = value.ToString();
// }
// }
// /// <summary>
// /// Default number of rereads
// /// </summary>
// public int DreamdumpRereadCount
// {
// get { return GetInt32Setting(Settings, DreamdumpSettings.RereadCount, DreamdumpSettings.RereadCountDefault); }
// set { Settings[DreamdumpSettings.RereadCount] = value.ToString(); }
// }
#endregion
#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 +550,28 @@ 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>
/// Currently selected default redumper drive pregap start sector
/// </summary>
public int RedumperDrivePregapStart
{
get { return GetInt32Setting(Settings, RedumperSettings.DrivePregapStart, RedumperSettings.DrivePregapStartDefault); }
set { Settings[RedumperSettings.DrivePregapStart] = value.ToString(); }
}
/// <summary>
@@ -424,6 +615,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 +647,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 +669,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 +683,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
{
@@ -536,6 +736,22 @@ namespace MPF.Frontend
set { Settings["CompressLogFiles"] = value.ToString(); }
}
/// <summary>
/// Compression type used during log compression
/// </summary>
public LogCompression LogCompression
{
get
{
var valueString = GetStringSetting(Settings, "LogCompression", LogCompression.DeflateMaximum.ToString());
return valueString.ToLogCompression();
}
set
{
Settings["LogCompression"] = value.ToString();
}
}
/// <summary>
/// Delete unnecessary files to reduce space
/// </summary>
@@ -558,15 +774,6 @@ namespace MPF.Frontend
#region Skip Options
/// <summary>
/// Skip detecting media type on disc scan
/// </summary>
public bool SkipMediaTypeDetection
{
get { return GetBooleanSetting(Settings, "SkipMediaTypeDetection", false); }
set { Settings["SkipMediaTypeDetection"] = value.ToString(); }
}
/// <summary>
/// Skip detecting known system on disc scan
/// </summary>
@@ -589,15 +796,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 +840,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 +858,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 +870,7 @@ namespace MPF.Frontend
/// <param name="settings"></param>
public Options(Dictionary<string, string?>? settings = null)
{
this.Settings = settings ?? [];
Settings = settings ?? [];
}
/// <summary>
@@ -688,55 +887,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 +900,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 +922,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 +944,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);
}

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