Compare commits

..

194 Commits
2.3 ... 2.4

Author SHA1 Message Date
Matt Nadareski
c4ef14ea3c Bump version to 2.4 2022-10-26 13:08:40 -07:00
Matt Nadareski
03668bd6af Update changelog 2022-10-25 21:11:16 -07:00
fuzzball
2d90a63ca7 Add + to positive offsets in a better way (#422) 2022-10-25 21:07:51 -07:00
Matt Nadareski
11e6e37331 Update changelog 2022-10-24 22:49:09 -07:00
Terry Janas
b229a2d59e Populate internal serial from PS5 disc (#423) 2022-10-24 21:06:57 -07:00
Matt Nadareski
c61af9316f Move to unused Chime class 2022-10-21 23:06:07 -07:00
Matt Nadareski
02e3040e1b Add unused notification method 2022-10-21 22:41:23 -07:00
Matt Nadareski
60bbe29435 Update changelog 2022-10-21 11:42:15 -07:00
Matt Nadareski
3274ea08aa Add PS4 version finding (tjanas) 2022-10-21 11:34:14 -07:00
Terry Janas
97a61dea32 Populate internal serial from PS4 disc (#421)
* Populate internal serial from PS4 disc

* Move GetPlayStation4Serial to that it precedes GetPlayStation4Version
2022-10-21 11:32:35 -07:00
Matt Nadareski
6cccf20b03 Fix layerbreak-based checks (fixes #399) 2022-10-21 11:06:27 -07:00
Matt Nadareski
0a7e17ed00 Fix readonly Filename info display 2022-10-20 14:21:09 -07:00
Matt Nadareski
706f75c0eb Fix readonly Filename info display 2022-10-20 14:21:03 -07:00
Matt Nadareski
022e87c4bb Fix NRE with offsets 2022-10-20 13:03:07 -07:00
Matt Nadareski
8b29ac7e47 Update issue templates to be more accurate 2022-10-20 13:00:07 -07:00
Matt Nadareski
4d5b8baf6f Add logging to !submissionInfo formatting failure 2022-10-20 12:45:01 -07:00
Matt Nadareski
e199e5a08c Add logging to !submissionInfo writing failure 2022-10-20 12:23:35 -07:00
Matt Nadareski
6cc2351bf7 Fix multiple DiscType* for DIC 2022-10-20 12:17:05 -07:00
Matt Nadareski
c391dbd3c8 Fix multiple DiscType for DIC 2022-10-19 23:23:04 -07:00
Matt Nadareski
709d980b67 Add disc type parsing for Aaru and DIC 2022-10-19 12:57:46 -07:00
Matt Nadareski
0903855d5c Add framework for reported disc type 2022-10-19 12:34:40 -07:00
Matt Nadareski
6ad2505bf8 Put Redump limitations behind existing flag 2022-10-18 17:03:21 -07:00
Matt Nadareski
5db8756639 Disable XGD1 PVD reporting 2022-10-18 16:18:49 -07:00
Matt Nadareski
5d176408a2 Disable XGD layerbreak reporting 2022-10-18 16:16:40 -07:00
Matt Nadareski
ab0b569798 Disable XGD version reporting 2022-10-18 16:12:40 -07:00
Matt Nadareski
ee76d49e56 Disable layerbreak generation for BD 2022-10-18 16:08:38 -07:00
Matt Nadareski
c75d2dcae2 Add + to positive offsets 2022-10-18 11:49:57 -07:00
Matt Nadareski
7c411d36db Add MS ZipFile package to MPF.Library 2022-10-17 21:04:58 -07:00
Matt Nadareski
ca767cf576 Change location of dumping info (fixes #415) 2022-10-17 20:56:28 -07:00
Matt Nadareski
b57e0bb97e Remove extraneous packages 2022-10-17 16:58:27 -07:00
Matt Nadareski
5f059253a4 Add CodePages package to MPF.Library 2022-10-17 16:42:04 -07:00
Matt Nadareski
e0f8443653 Add System.Memory package to MPF.Library 2022-10-17 16:20:41 -07:00
Matt Nadareski
4c23a4bbf3 Add compression result reason to log 2022-10-17 15:41:00 -07:00
Matt Nadareski
0c1486bbce Update BurnOutSharp to 2.3.4 2022-10-17 13:41:21 -07:00
Matt Nadareski
6f41c9a331 Remove deprecated protection setting 2022-10-17 11:14:41 -07:00
Matt Nadareski
2879dd29d6 Update Nuget packages 2022-10-17 11:00:51 -07:00
Matt Nadareski
42e1ef45b4 Add multi-language helper for filter 2022-10-16 23:15:09 -07:00
Matt Nadareski
19493fdf0c Add language filtering to formatter 2022-10-16 22:59:54 -07:00
Matt Nadareski
5870ad0673 Add unused article formatter 2022-10-16 21:21:56 -07:00
Matt Nadareski
cbd2850d1b Add specialized CDS/SafeDisc filter 2022-10-16 14:27:26 -07:00
Matt Nadareski
e7c36c104a Fix hardware info 2022-10-13 00:04:28 -07:00
Matt Nadareski
960840d9ba Add hardware info to DIC and Aaru 2022-10-12 21:40:38 -07:00
Matt Nadareski
b8ac1bc9d4 Fix missing info in Aaru 2022-10-12 21:27:58 -07:00
Matt Nadareski
89edf9a8f6 Create MPF log helper and filter for deletion 2022-10-11 13:19:06 -07:00
Matt Nadareski
eb8db0b311 Try adding MPF logs to zip 2022-10-11 12:54:11 -07:00
Matt Nadareski
b10cf8b78a Enable separated protection info output by default 2022-10-11 10:11:48 -07:00
Matt Nadareski
1dd5c0d6d0 Fix missing info reference change 2022-10-06 14:24:47 -07:00
Matt Nadareski
a1e00d23a4 Update Aaru submodules 2022-10-06 11:57:05 -07:00
Matt Nadareski
1a9d38dd0e Add dumping info section skeleton 2022-10-06 11:11:23 -07:00
Matt Nadareski
2f3e7d105d Add dumping info section skeleton 2022-10-06 10:25:27 -07:00
Matt Nadareski
a72b3c32b1 Report specific DIC version, if possible 2022-10-06 10:04:56 -07:00
Matt Nadareski
a479b16ae2 Add initial framework for reporting dumping program 2022-10-05 15:43:02 -07:00
Matt Nadareski
a9e7b6f5b3 Add unused command filename parser 2022-10-05 12:08:11 -07:00
Matt Nadareski
60605d7d00 Merge branch 'master' of https://github.com/SabreTools/MPF 2022-10-04 20:50:38 -07:00
Matt Nadareski
9dc7f726e9 Use default directory for folder browsing, if possible (fixes #412) 2022-10-04 20:45:28 -07:00
Whovian9369
46134032d6 Add a check for the PS4 Update file, and add some file info to the comments. (#411) 2022-10-04 20:14:50 -07:00
Markus Persson
9847c8f351 Update Drive.cs (#410)
Fixed console return typo.
2022-10-04 13:50:37 -07:00
Matt Nadareski
7b506586cd Add PS3 folder/file checks for detection (fixes #409) 2022-10-04 13:36:50 -07:00
Matt Nadareski
40bbd422b7 Possibly fix PIC parsing 2022-09-28 13:38:50 -07:00
Matt Nadareski
285fd69ab4 Update to DIC 20220909 2022-09-28 13:15:05 -07:00
Matt Nadareski
b436e19bbb Add important Redumper note 2022-09-28 13:04:17 -07:00
Matt Nadareski
bf6e079289 Minor Redumper cleanup 2022-09-28 12:49:17 -07:00
Matt Nadareski
b623866b6e Create Redumper extensions class 2022-09-28 12:43:40 -07:00
Matt Nadareski
df7046723c Add Redumper command generation 2022-09-28 12:38:23 -07:00
Matt Nadareski
3cd3047790 Add Redumper command validation 2022-09-28 12:22:14 -07:00
Matt Nadareski
abd60612c5 Add Redumper dumping command 2022-09-28 11:52:56 -07:00
Matt Nadareski
02292acee1 Add Redumper command support and reset 2022-09-28 11:48:25 -07:00
Matt Nadareski
c923899898 Add Redumper parameter values 2022-09-28 11:33:59 -07:00
Matt Nadareski
fcdd2ad036 Add Redumper constants 2022-09-28 11:23:04 -07:00
Matt Nadareski
02d3af0ac1 Download Redumper with AppVeyor 2022-09-28 11:12:03 -07:00
Matt Nadareski
0516bccece Add skeleton for Redumper 2022-09-27 23:08:12 -07:00
Matt Nadareski
2d6389d54d Fix ringcode guide images (fixes #407) 2022-09-27 22:54:23 -07:00
Matt Nadareski
60d5a117b5 Electronic not Electric (fixes #402) 2022-08-30 10:42:04 -07:00
Matt Nadareski
9b7e2e35f5 Verify GD-ROM outputs, finally (fixes #401) 2022-08-29 19:49:31 -07:00
Matt Nadareski
ddfb383f23 Add Sony Electronic Book system (fixes #400) 2022-08-29 10:42:20 -07:00
Matt Nadareski
a1707486f4 Update Aaru submodule to latest devel 2022-08-29 10:36:27 -07:00
Matt Nadareski
451101ec67 Update PSX content check 2022-08-25 14:38:28 -07:00
Matt Nadareski
83c583b7e6 Update to new constructor 2022-08-25 11:54:55 -07:00
Matt Nadareski
33cb3d3c97 Fix missing assignment 2022-08-25 11:36:42 -07:00
Matt Nadareski
3d98f345c1 Fix psxt001z namespace 2022-08-25 10:45:22 -07:00
Matt Nadareski
de8ce5c110 Add internal name for Cleanrip outputs (fixes #398) 2022-08-25 10:38:20 -07:00
Matt Nadareski
fd9ed77316 Fix missing parenthesis 2022-08-25 10:23:28 -07:00
Matt Nadareski
d974b73cce Fix serial parsing for Dreamcast (fixes #397) 2022-08-25 10:18:35 -07:00
Matt Nadareski
da8a67fcad Update to BurnOutSharp 2.3.1 (nw) 2022-08-25 10:11:28 -07:00
Matt Nadareski
471bc60ed7 Add Mattel Fisher-Price iXL detection 2022-08-25 10:07:15 -07:00
Matt Nadareski
fef46be34c Update to BurnOutSharp 2.3.0 (nw) 2022-08-25 09:48:37 -07:00
Matt Nadareski
9a92dbdedb Give .NET 6 priority for web calls 2022-07-26 13:47:19 -07:00
Matt Nadareski
d6be0a4154 Trim leading file paths for XBONE (fixes #394) 2022-07-25 09:38:02 -07:00
Matt Nadareski
d3ae372903 Disable nonstandard BD-ROM sizes 2022-07-08 22:05:24 -07:00
Matt Nadareski
7621ef1a13 Fix .bin file paths; update internal filename generation 2022-07-08 14:03:44 -07:00
Matt Nadareski
148fdd0590 Update to DIC 20220707 (possibly nw) 2022-07-08 13:37:51 -07:00
Matt Nadareski
232256310a Update changelog 2022-07-05 22:51:21 -07:00
Matt Nadareski
5e938791ef Add files for XBONE (fixes #390) 2022-07-05 22:43:28 -07:00
Matt Nadareski
08405dd9b4 Framework for XBONE filenames 2022-07-05 22:11:21 -07:00
Matt Nadareski
512c637ea3 Add Bandai Playdia QIS detection (fixes #393) 2022-07-05 22:06:04 -07:00
Matt Nadareski
909ebd72a1 Add Sharp X68k detection (fixes #387) 2022-07-05 22:02:31 -07:00
Matt Nadareski
123136e90e Fix incomplete system name detection 2022-05-29 14:07:38 -07:00
Matt Nadareski
299d25af27 Implement Drive.Create for safety 2022-04-20 13:47:55 -07:00
Matt Nadareski
9eee2f6444 Update changelog 2022-04-20 12:36:11 -07:00
Matt Nadareski
2afb10b73b Organize projects in solution 2022-04-20 12:33:47 -07:00
Matt Nadareski
1b016e5915 Add PIC.bin to log zip 2022-04-19 22:50:43 -07:00
Matt Nadareski
1c403e1748 Possibly fix tab regex replacement 2022-04-19 22:40:08 -07:00
Matt Nadareski
a372a133ca Reorganize GetMediaType 2022-04-19 21:07:09 -07:00
Matt Nadareski
08a7df504b Simplify IsOptical (.NET 6) 2022-04-19 20:39:42 -07:00
Matt Nadareski
39f9d9a86d Add optical media support method (.NET 6) 2022-04-19 20:25:36 -07:00
Matt Nadareski
bdd5af65ce Better get drive list (.NET 6) 2022-04-19 20:19:22 -07:00
Matt Nadareski
92d7d2ab91 Fix CD-R multisession info (fixes #384) 2022-04-19 17:12:37 -07:00
Matt Nadareski
8d4d7ce449 Update changelog 2022-04-19 16:53:58 -07:00
Matt Nadareski
d2d650cace Use Aaru for media type retrival (.NET 6) 2022-04-19 16:53:13 -07:00
Matt Nadareski
772cefd700 Multisession is multi-line 2022-04-19 12:49:18 -07:00
Matt Nadareski
e20350160b Add Aaru as a submodule for .NET 6 2022-04-18 22:59:07 -07:00
Matt Nadareski
08f4e91b27 Update solution file for VS2022 2022-04-18 21:53:20 -07:00
Matt Nadareski
021237bc38 Sync to newest CICM 2022-04-18 21:34:48 -07:00
Matt Nadareski
78c36db2f9 Avoid whitespace changes for PVD, Header, and Cuesheet 2022-04-18 21:20:12 -07:00
Matt Nadareski
7fd2562cb5 Add filesystem logging for .NET 6 2022-04-18 12:42:18 -07:00
Matt Nadareski
15a3be2e66 Update changelist 2022-04-18 11:03:34 -07:00
Matt Nadareski
9b6a540ec6 Remove .NET Core 3.1 entirely 2022-04-18 11:03:21 -07:00
Matt Nadareski
2b51085bc2 Remove .NET Core 3.1 from test project for now 2022-04-17 22:53:07 -07:00
Matt Nadareski
822134070b .NET 6.0 and Cleanup 2022-04-17 22:39:39 -07:00
Matt Nadareski
26cd874779 Update to BurnOutSharp 2.1.0 2022-04-17 22:14:13 -07:00
Matt Nadareski
9f21b68541 Use built-in NETFRAMEWORK directive 2022-04-17 21:54:04 -07:00
Matt Nadareski
e49d95663b Gate ManagmentObject use further 2022-04-17 21:30:04 -07:00
Matt Nadareski
2c70392ada Add size-based media type detection for non-Framework 2022-04-17 21:00:02 -07:00
Matt Nadareski
1b2248b1e7 Revert AppVeyor to VS2019 for now 2022-04-17 16:34:33 -07:00
Matt Nadareski
a322dc6353 Update copyright date to 2022 2022-04-17 16:29:28 -07:00
Matt Nadareski
7c78ae47c6 Remove needless csproj constants 2022-04-17 16:28:09 -07:00
Matt Nadareski
a14dec1b7e Update Nuget packages 2022-04-17 16:22:11 -07:00
Matt Nadareski
53b5862697 Update AppVeyor to VS2022 2022-04-17 16:19:33 -07:00
Matt Nadareski
83d379c7b5 Convert internal libraries to .NET Standard 2.0 2022-04-17 16:18:40 -07:00
Matt Nadareski
be33db8339 Sanitize whitespace around tabs 2022-04-16 12:15:23 -07:00
Matt Nadareski
97e9924a0b Be more picky about multisession 2022-04-12 22:04:55 -07:00
Matt Nadareski
7daab55639 Fix clone issue with copy protection 2022-04-12 21:22:49 -07:00
Matt Nadareski
9d70b7469a Convert triple space to tab 2022-04-12 17:03:32 -07:00
Matt Nadareski
b5504902c4 Parse and format CD multisession data (fixes #370) 2022-04-12 16:09:18 -07:00
Matt Nadareski
f5e82ccd75 Separate common arguments to new helper 2022-04-12 12:06:45 -07:00
Matt Nadareski
f4c4c21a10 Consolidate Redump login testing 2022-04-12 12:01:26 -07:00
Matt Nadareski
661d2440f2 Move helper methods around 2022-04-12 11:51:05 -07:00
Matt Nadareski
f4af8097f6 Move and update options loader; clean up Check 2022-04-12 11:27:10 -07:00
Matt Nadareski
e1822905e7 Add multisession helper method skeleton 2022-04-12 10:18:52 -07:00
Matt Nadareski
e5154dad5b Add multisession pseudo-tag 2022-04-12 10:10:07 -07:00
Matt Nadareski
30f8932039 Rename MPF.GUI to MPF.UI 2022-04-11 12:01:16 -07:00
Matt Nadareski
c4f0792c77 Create core UI library 2022-04-11 10:32:03 -07:00
Matt Nadareski
79c7f13ff9 Add warning to tooltip 2022-04-10 22:27:05 -07:00
Matt Nadareski
dbeeb0c69b Make protection field user-editable (fixes #340) 2022-04-10 22:23:15 -07:00
Matt Nadareski
58c53ff5e2 Add option for copy protection file output (fixes #372) 2022-04-10 22:12:20 -07:00
Matt Nadareski
87c441887a Convert status label to TextBlock (fixes #382) 2022-04-10 21:11:50 -07:00
Matt Nadareski
8ee4dab239 Combine cases in protection scan 2022-04-10 16:14:13 -07:00
Matt Nadareski
e01ebf8d8e Explicitly sanitize '?' from path 2022-04-09 13:21:31 -07:00
Matt Nadareski
e92bcd378c Disable PVD creation for Aaru 2022-04-08 20:55:33 -07:00
Matt Nadareski
80156e73d1 Even even stricter copy protection output 2022-04-07 20:56:28 -07:00
Matt Nadareski
97803cd860 Report dictionary to InfoTool 2022-04-04 22:44:15 -07:00
Matt Nadareski
9a5feee095 Even stricter output for copy protection section 2022-04-04 22:25:07 -07:00
Matt Nadareski
84410056bd Compress JSON for artifacts alone (fixes #379) 2022-04-04 22:15:50 -07:00
Matt Nadareski
acd0e41703 Further separate out protection scan outputs 2022-04-04 22:06:56 -07:00
Matt Nadareski
c7dfb9dca7 Increase JSON accuracy for disc types (fixes #381) 2022-04-04 21:46:13 -07:00
Matt Nadareski
0efd82bd59 Normalize newlines in comments and contents (fixes #380) 2022-04-04 21:12:50 -07:00
John Veness
426ceff451 Fix RingCodeGuideWindow.xaml so text matches images (#378)
* Fix RingCodeGuideWindow.xaml so text matches image

* Fix RingCodeGuideWindow.xaml so text matches img 2
2022-04-01 09:57:07 -07:00
Matt Nadareski
f2be7ed34c Update Nuget packages 2022-03-27 20:57:42 -07:00
Matt Nadareski
5dcc783b95 Fix a couple Redump URL-related things 2022-03-25 09:28:11 -07:00
Matt Nadareski
d1c641e934 Fix ring serialization (fixes #376) 2022-03-25 09:00:16 -07:00
Wilson
59be63785d Windows 7 UI Fix (#375)
* Updated the Main Menu grid width setting so that the Help menu now appears proprely on Windows 7.

* Changed the Secret log level color from Blue to Teal so that the text is much more readable.

* Undid the Secret color change.
2022-03-15 16:25:43 -07:00
Matt Nadareski
0bf85ec729 Refine copy protection section showing 2022-03-13 13:14:37 -07:00
Matt Nadareski
b54c2dc254 Clear out fully matched IDs from the partial list 2022-03-12 22:19:03 -08:00
Matt Nadareski
021fcd0641 Fix submission info clone 2022-03-12 22:06:04 -08:00
Matt Nadareski
f2dadae7a3 Add both fully and partially matching to info file 2022-03-12 21:27:23 -08:00
Matt Nadareski
7ce7df2625 Explicitly clear list, just in case 2022-03-12 21:14:26 -08:00
Matt Nadareski
09fcd384ab Make fully and partially matching IDs more apparent
Add write offset as read-only field
2022-03-12 21:09:51 -08:00
Matt Nadareski
5f8625a384 Fix tabs in Games and Videos boxes (fixes #373) 2022-03-09 15:34:44 -08:00
Matt Nadareski
621011af7a Remove redundant check around volume label 2022-03-09 14:25:01 -08:00
Matt Nadareski
a0b81941d1 Return faster on empty protection sets 2022-03-08 22:37:12 -08:00
Matt Nadareski
e735335773 Update Aaru Nuget package 2022-03-08 09:13:37 -08:00
Matt Nadareski
7b62572a56 Handle sanitized protections edge case 2022-03-06 22:19:04 -08:00
Matt Nadareski
0f921c926b Reorder event handers 2022-03-06 00:04:23 -08:00
Matt Nadareski
4d8a4d23c0 Add upper case <TAB> processing 2022-03-05 23:20:13 -08:00
Matt Nadareski
9d7eaa46fd Update to Aaru v5.3.1 LTS 2022-03-05 21:53:52 -08:00
Matt Nadareski
cc9664f7d6 Track last used drive on refresh; Clean up pre-dump validation 2022-03-05 21:50:18 -08:00
Matt Nadareski
573b3e9d1c Fix "missing" option in window 2022-03-05 21:05:34 -08:00
Matt Nadareski
9808694d89 Update changelog 2022-03-01 23:04:43 -08:00
Wilson
d70d8f5b6e Added a button to quickly update the volume label and dump path. (#369)
* Added a button to quickly update the volume label and dump path.
Behaves identically to switching the selected drive combo box. (Calls the InitializeUIValues method with rescanDrives set to false.)

* Typo fixed the odd w.
2022-03-01 21:43:05 -08:00
Matt Nadareski
b75c2d80bf Force internal drive refresh 2022-03-01 16:41:54 -08:00
Matt Nadareski
aa747ff651 Update to DIC 20220301 2022-03-01 13:27:31 -08:00
Matt Nadareski
bcbf5daf0d Add Xbox One system detection (fixes #368) 2022-02-24 11:22:04 -08:00
Matt Nadareski
aee1c05a45 Update Nuget packages to newest stable 2022-02-24 10:58:36 -08:00
Matt Nadareski
0bb96a8dd3 Specifically include Unsafe Nuget package 2022-02-22 09:08:29 -08:00
Matt Nadareski
1e1d2c7b63 Move path normalization to better place 2022-02-13 22:49:33 -08:00
Matt Nadareski
f12375cddc Assign normalized path to parameters 2022-02-12 23:25:34 -08:00
Matt Nadareski
ed26e6611a Cap X360 directory check to 500MB (fixes #362) 2022-02-11 12:59:37 -08:00
Matt Nadareski
98f77eca07 Add option to limit region and language selections (fixes #361) 2022-02-11 09:55:00 -08:00
Matt Nadareski
1b2b560f8f Fix failing module tests 2022-02-10 13:40:07 -08:00
Matt Nadareski
b49cc0c9bd Fix small DVD layer finding corner case (fixes #363) 2022-02-10 12:02:54 -08:00
Matt Nadareski
4ba58ea861 Make FillFromRedump private again 2022-02-10 11:54:25 -08:00
Matt Nadareski
3f52a20c90 Add /mr default flag options (fixes #360) 2022-02-10 11:52:13 -08:00
Matt Nadareski
580089d06e Fix Redump disc title pulling (fixes #359) 2022-02-10 11:37:36 -08:00
Matt Nadareski
1397ab0fa6 Update to DIC 20211001
Note that there are some code changes that could affect how things like offsets are parsed. Testing is needed.
2022-02-10 11:21:38 -08:00
77 changed files with 6205 additions and 1095 deletions

View File

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

View File

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

View File

@@ -10,7 +10,7 @@ assignees: mnadareski
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the issue has already been addressed.
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
- .NET Core 3.1 and .NET 5.0 have known issues, please try using another build to reproduce the error
- .NET 6.0 has known issues, please try using another build to reproduce the error
- Check multiple discs to help narrow down the issue
- Check the Options to see if changing any of those affects your issue.
@@ -25,9 +25,8 @@ What version are you using?
**Build**
What runtime version are you using?
- [ ] .NET Framework 4.7.2 running on (Operating System)
- [ ] .NET Framework 4.8 running on (Operating System)
- [ ] .NET Core 3.1 running on (Operating System)
- [ ] .NET 6.0 running on (Operating System)
**Describe the issue**
A clear and concise description of what the bug is.

6
.gitmodules vendored
View File

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

1
Aaru Submodule

Submodule Aaru added at b1ac16c551

View File

@@ -1,3 +1,182 @@
### 2.4 (2022-10-26)
- Update to DIC 20211001
- Fix Redump disc title pulling
- Add /mr default flag options
- Make FillFromRedump private again
- Fix DVD layer finding corner case
- Fix failing module tests
- Add option to limit region and language selections
- Cap X360 directory check to 500MB
- Assign normalized path to parameters
- Move path normalization to better place
- Specifically include Unsafe Nuget package
- Update Nuget packages to newest stable
- Add Xbox One system detection
- Update to DIC 20220301
- Force internal drive refresh
- Add single drive refresh button (IcySon55)
- Fix "missing" option in window
- Track last used drive on refresh
- Clean up pre-dump validation
- Update to Aaru v5.3.1 LTS
- Add upper case `<TAB>` processing
- Reorder event handers
- Handle sanitized protections edge case
- Update Aaru Nuget package
- Return faster on empty protection sets
- Remove redundant check around volume label
- Fix tabs in Games and Videos boxes
- Make fully and partially matching IDs more apparent
- Add write offset as read-only field
- Explicitly clear list, just in case
- Add both fully and partially matching to info file
- Fix submission info clone
- Clear out fully matched IDs from the partial list
- Refine copy protection section showing
- Update Nuget packages
- Normalize newlines in comments and contents
- Increase JSON accuracy for disc types
- Further separate out protection scan outputs
- Compress JSON for artifacts alone
- Even stricter output for copy protection section
- Report dictionary to InfoTool
- Even even stricter copy protection output
- Disable PVD creation for Aaru
- Explicitly sanitize '?' from path
- Combine cases in protection scan
- Convert status label to TextBlock
- Add option for copy protection file output
- Make protection field user-editable
- Add warning to tooltip
- Create core UI library
- Rename MPF.GUI to MPF.UI
- Add multisession pseudo-tag
- Add multisession helper method skeleton
- Move and update options loader; clean up Check
- Move helper methods around
- Consolidate Redump login testing
- Separate common arguments to new helper
- Parse and format CD multisession data
- Convert triple space to tab
- Fix clone issue with copy protection
- Be more picky about multisession
- Sanitize whitespace around tabs
- Convert internal libraries to .NET Standard 2.0
- Update AppVeyor to VS2022
- Update Nuget packages
- Remove needless csproj constants
- Update copyright date to 2022
- Revert AppVeyor to VS2019 for now
- Add size-based media type detection for non-Framework
- Gate ManagmentObject use further
- Use built-in NETFRAMEWORK directive
- Update to BurnOutSharp 2.1.0
- .NET 6.0 and Cleanup
- Remove .NET Core 3.1 from test project for now
- Remove .NET Core 3.1 entirely
- Add filesystem logging for .NET 6
- Avoid whitespace changes for PVD, Header, and Cuesheet
- Sync to newest CICM
- Update solution file for VS2022
- Add Aaru as a submodule for .NET 6
- Multisession is multi-line
- Use Aaru for media type retrival (.NET 6)
- Fix CD-R multisession info
- Better get drive list (.NET 6)
- Add optical media support method (.NET 6)
- Simplify IsOptical (.NET 6)
- Reorganize GetMediaType
- Possibly fix tab regex replacement
- Add PIC.bin to log zip
- Organize projects in solution
- Implement Drive.Create for safety
- Fix incomplete system name detection
- Add Sharp X68k detection
- Add Bandai Playdia QIS detection
- Framework for XBONE filenames
- Add files for XBONE
- Update to DIC 20220707
- Fix .bin file paths; update internal filename generation
- Disable nonstandard BD-ROM sizes
- Trim leading file paths for XBONE
- Give .NET 6 priority for web calls
- Update to BurnOutSharp 2.3.0 (nw)
- Add Mattel Fisher-Price iXL detection
- Update to BurnOutSharp 2.3.1 (nw)
- Fix serial parsing for Dreamcast
- Fix missing parenthesis
- Add internal name for Cleanrip outputs
- Fix psxt001z namespace
- Fix missing assignment
- Update to new constructor
- Update PSX content check
- Update Aaru submodule to latest devel
- Add Sony Electronic Book system
- Verify GD-ROM outputs, finally
- Electronic not Electric
- Fix ringcode guide images
- Add skeleton for Redumper
- Download Redumper with AppVeyor
- Add Redumper constants
- Add Redumper parameter values
- Add Redumper command support and reset
- Add Redumper dumping command
- Add Redumper command validation
- Add Redumper command generation
- Create Redumper extensions class
- Minor Redumper cleanup
- Add important Redumper note
- Update to DIC 20220909
- Possibly fix PIC parsing
- Add PS3 folder/file checks for detection
- Use default directory for folder browsing, if possible
- Add unused command filename parser
- Add initial framework for reporting dumping program
- Report specific DIC version, if possible
- Add dumping info section skeleton
- Update Aaru submodules
- Fix missing info reference change
- Enable separated protection info output by default
- Try adding MPF logs to zip
- Create MPF log helper and filter for deletion
- Fix missing info in Aaru
- Add hardware info to DIC and Aaru
- Fix hardware info
- Add specialized CDS/SafeDisc filter
- Add unused article formatter
- Add language filtering to formatter
- Add multi-language helper for filter
- Update Nuget packages
- Remove deprecated protection setting
- Update BurnOutSharp to 2.3.4
- Add compression result reason to log
- Add System.Memory package to MPF.Library
- Add CodePages package to MPF.Library
- Remove extraneous packages
- Change location of dumping info
- Add MS ZipFile package to MPF.Library
- Add + to positive offsets
- Disable layerbreak generation for BD
- Disable XGD version reporting
- Disable XGD layerbreak reporting
- Disable XGD1 PVD reporting
- Put Redump limitations behind existing flag
- Add framework for reported disc type
- Add disc type parsing for Aaru and DIC
- Fix multiple DiscType for DIC
- Fix multiple DiscType* for DIC
- Add logging to !submissionInfo writing failure
- Add logging to !submissionInfo formatting failure
- Update issue templates to be more accurate
- Fix NRE with offsets
- Fix readonly Filename info display
- Fix layerbreak-based checks
- Add PS4 serial finding (tjanas)
- Add unused notification method
- Move to unused Chime class
- Add PS5 serial finding (tjanas)
- Fix offset formatting (fuzz6001)
### 2.3 (2022-02-05)
- Start overhauling Redump information pulling, again
- Add internal structure for special site codes

Submodule CICMMetadata deleted from 7944bca8e6

View File

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

View File

@@ -1,6 +1,5 @@
using System;
using System.IO;
using System.Linq;
using BurnOutSharp;
using MPF.Core.Converters;
using MPF.Core.Data;
@@ -15,126 +14,17 @@ namespace MPF.Check
{
public static void Main(string[] args)
{
// Help options
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
{
DisplayHelp();
// Try processing the standalone arguments
if (ProcessStandaloneArguments(args))
return;
}
// List options
if (args[0] == "-lm" || args[0] == "--listmedia")
{
ListMediaTypes();
Console.ReadLine();
// Try processing the common arguments
(bool success, MediaType mediaType, RedumpSystem? knownSystem) = ProcessCommonArguments(args);
if (!success)
return;
}
else if (args[0] == "-lp" || args[0] == "--listprograms")
{
ListPrograms();
Console.ReadLine();
return;
}
else if (args[0] == "-ls" || args[0] == "--listsystems")
{
ListSystems();
Console.ReadLine();
return;
}
// Normal operation check
if (args.Length < 3)
{
DisplayHelp("Invalid number of arguments");
return;
}
// Check the MediaType
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
if (mediaType == MediaType.NONE)
{
DisplayHelp($"{args[0]} is not a recognized media type");
return;
}
// Check the RedumpSystem
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
if (knownSystem == null)
{
DisplayHelp($"{args[1]} is not a recognized system");
return;
}
// Default values
string username = null, password = null;
string internalProgram = "DiscImageCreator";
string path = string.Empty;
bool scan = false, compress = false, json = false;
// Loop through and process options
int startIndex = 2;
for (; startIndex < args.Length; startIndex++)
{
// Redump login
if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
{
string[] credentials = args[startIndex].Split('=')[1].Split(';');
username = credentials[0];
password = credentials[1];
}
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
{
username = args[startIndex + 1];
password = args[startIndex + 2];
startIndex += 2;
}
// Use specific program
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
{
internalProgram = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
internalProgram = args[startIndex + 1];
startIndex++;
}
// Use a device path for physical checks
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
{
path = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
{
path = args[startIndex + 1];
startIndex++;
}
// Scan for protection (requires device path)
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
{
scan = true;
}
// Output submission JSON
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
{
json = true;
}
// Compress log and extraneous files
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
{
compress = true;
}
// Default, we fall out
else
{
break;
}
}
(Options options, string path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
// Make new Progress objects
var resultProgress = new Progress<Result>();
@@ -142,20 +32,14 @@ namespace MPF.Check
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ProgressUpdated;
// If credentials are invalid, alert the user
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
{
using (RedumpWebClient wc = new RedumpWebClient())
{
bool? loggedIn = wc.Login(username, password);
if (loggedIn == true)
Console.WriteLine("Redump username and password accepted!");
else if (loggedIn == false)
Console.WriteLine("Redump username and password denied!");
else
Console.WriteLine("An error occurred validating your crendentials!");
}
}
// Validate the supplied credentials
#if NET48 || NETSTANDARD2_1
(bool? _, string message) = RedumpWebClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword);
#else
(bool? _, string message) = RedumpHttpClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
if (!string.IsNullOrWhiteSpace(message))
Console.WriteLine(message);
// Loop through all the rest of the args
for (int i = startIndex; i < args.Length; i++)
@@ -171,22 +55,9 @@ namespace MPF.Check
string filepath = Path.GetFullPath(args[i].Trim('"'));
// Now populate an environment
var options = new Options
{
InternalProgram = EnumConverter.ToInternalProgram(internalProgram),
ScanForProtection = scan && !string.IsNullOrWhiteSpace(path),
PromptForDiscInformation = false,
ShowDiscEjectReminder = false,
OutputSubmissionJSON = json,
CompressLogFiles = compress,
RedumpUsername = username,
RedumpPassword = password,
};
Drive drive = null;
if (!string.IsNullOrWhiteSpace(path))
drive = new Drive(null, new DriveInfo(path));
drive = Drive.Create(null, path);
var env = new DumpEnvironment(options, "", filepath, drive, knownSystem, mediaType, null);
@@ -214,61 +85,94 @@ namespace MPF.Check
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("-c, --credentials <user> <pw> Redump username and password");
Console.WriteLine("-u, --use <program> Dumping program output type");
Console.WriteLine("-p, --path <drivepath> Physical drive path for additional checks");
Console.WriteLine("-s, --scan Enable copy protection scan (requires --path)");
Console.WriteLine("-j, --json Enable submission JSON output");
Console.WriteLine("-z, --zip Enable log file compression");
var supportedArguments = OptionsLoader.PrintSupportedArguments();
foreach (string argument in supportedArguments)
{
Console.WriteLine(argument);
}
Console.WriteLine();
}
/// <summary>
/// List all media types with their short usable names
/// Process common arguments for all functionality
/// </summary>
private static void ListMediaTypes()
/// <returns>True if all arguments pass, false otherwise</returns>
private static (bool, MediaType, RedumpSystem?) ProcessCommonArguments(string[] args)
{
Console.WriteLine("Supported Media Types:");
foreach (var val in Enum.GetValues(typeof(MediaType)))
// All other use requires at least 3 arguments
if (args.Length < 3)
{
if (((MediaType)val) == MediaType.NONE)
continue;
Console.WriteLine($"{((MediaType?)val).ShortName()} - {((MediaType?)val).LongName()}");
DisplayHelp("Invalid number of arguments");
return (false, MediaType.NONE, null);
}
// Check the MediaType
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
if (mediaType == MediaType.NONE)
{
DisplayHelp($"{args[0]} is not a recognized media type");
return (false, MediaType.NONE, null);
}
// Check the RedumpSystem
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
if (knownSystem == null)
{
DisplayHelp($"{args[1]} is not a recognized system");
return (false, MediaType.NONE, null);
}
return (true, mediaType, knownSystem);
}
/// <summary>
/// List all programs with their short usable names
/// Process any standalone arguments for the program
/// </summary>
private static void ListPrograms()
/// <returns>True if one of the arguments was processed, false otherwise</returns>
private static bool ProcessStandaloneArguments(string[] args)
{
Console.WriteLine("Supported Programs:");
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
// Help options
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
{
if (((InternalProgram)val) == InternalProgram.NONE)
continue;
Console.WriteLine($"{((InternalProgram?)val).LongName()}");
DisplayHelp();
return true;
}
}
/// <summary>
/// List all systems with their short usable names
/// </summary>
private static void ListSystems()
{
Console.WriteLine("Supported Known Systems:");
var knownSystems = Enum.GetValues(typeof(RedumpSystem))
.OfType<RedumpSystem?>()
.Where(s => s != null && !s.IsMarker() && s.GetCategory() != SystemCategory.NONE)
.OrderBy(s => s.LongName() ?? string.Empty);
foreach (var val in knownSystems)
// List options
if (args[0] == "-lm" || args[0] == "--listmedia")
{
Console.WriteLine($"{val.ShortName()} - {val.LongName()}");
Console.WriteLine("Supported Media Types:");
foreach (string mediaType in Extensions.ListMediaTypes())
{
Console.WriteLine(mediaType);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-lp" || args[0] == "--listprograms")
{
Console.WriteLine("Supported Programs:");
foreach (string program in EnumExtensions.ListPrograms())
{
Console.WriteLine(program);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-ls" || args[0] == "--listsystems")
{
Console.WriteLine("Supported Systems:");
foreach (string system in Extensions.ListSystems())
{
Console.WriteLine(system);
}
Console.ReadLine();
return true;
}
return false;
}
/// <summary>

View File

@@ -2,7 +2,7 @@
using System.Collections.Concurrent;
using System.IO;
using System.Reflection;
#if NET_FRAMEWORK
#if NETFRAMEWORK
using IMAPI2;
#endif
using MPF.Core.Data;
@@ -34,7 +34,7 @@ namespace MPF.Core.Converters
}
}
#if NET_FRAMEWORK
#if NETFRAMEWORK
/// <summary>
/// Convert IMAPI physical media type to a MediaType
/// </summary>
@@ -73,6 +73,56 @@ namespace MPF.Core.Converters
return null;
}
}
#else
/// <summary>
/// Convert Aaru media type to a MediaType
/// </summary>
/// <param name="type">Aaru.CommonTypes.MediaType value to check</param>
/// <returns>MediaType if possible, null on error</returns>
public static MediaType? MediaTypeToMediaType(this Aaru.CommonTypes.MediaType? type)
{
if (type == null)
return null;
return (int)type switch
{
// Generics
>= 10 and <= 39 => MediaType.CDROM,
>= 40 and <= 50 => MediaType.DVD,
>= 51 and <= 59 => MediaType.HDDVD,
>= 60 and <= 69 => MediaType.BluRay,
// Specialty
112 => MediaType.CDROM,
113 => MediaType.CDROM,
114 => MediaType.DVD,
115 => MediaType.DVD,
116 => MediaType.BluRay,
117 => MediaType.BluRay,
118 => MediaType.UMD,
120 => MediaType.BluRay,
150 => MediaType.CDROM,
151 => MediaType.CDROM,
152 => MediaType.GDROM,
153 => MediaType.GDROM,
155 => MediaType.CDROM, // MilCD... is this GD-ROM?
171 => MediaType.CDROM,
172 => MediaType.CDROM,
173 => MediaType.CDROM,
174 => MediaType.CDROM,
175 => MediaType.CDROM,
176 => MediaType.CDROM,
177 => MediaType.CDROM,
178 => MediaType.DVD,
179 => MediaType.CDROM,
453 => MediaType.NintendoGameCubeGameDisc,
463 => MediaType.NintendoWiiOpticalDisc,
464 => MediaType.NintendoWiiUOpticalDisc,
>= 770 and <= 799 => MediaType.BluRay,
_ => null,
};
}
#endif
#endregion
@@ -135,6 +185,8 @@ namespace MPF.Core.Converters
return "dd";
case InternalProgram.DiscImageCreator:
return "DiscImageCreator";
case InternalProgram.Redumper:
return "Redumper";
#endregion
@@ -183,6 +235,9 @@ namespace MPF.Core.Converters
return InternalProgram.DiscImageCreator;
case "dd":
return InternalProgram.DD;
case "rd:":
case "redumper":
return InternalProgram.Redumper;
// Verification support only
case "cleanrip":

View File

@@ -82,13 +82,19 @@ namespace MPF.Core.Data
// Automatic Information
public const string DumpingProgramField = "Dumping Program";
public const string DumpingDriveManufacturer = "Manufacturer";
public const string DumpingDriveModel = "Model";
public const string DumpingDriveFirmware = "Firmware";
public const string ReportedDiscType = "Reported Disc Type";
public const string PVDField = "Primary Volume Descriptor (PVD)";
public const string DATField = "DAT";
public const string SizeField = "Size";
public const string CRC32Field = "CRC32";
public const string MD5Field = "MD5";
public const string SHA1Field = "SHA1";
public const string MatchingIDsField = "Matching IDs";
public const string FullyMatchingIDField = "Fully Matching ID";
public const string PartiallyMatchingIDsField = "Partially Matching IDs";
public const string ErrorCountField = "Error Count";
public const string CuesheetField = "Cuesheet";
public const string SubIntentionField = "SubIntention Data (SecuROM/LibCrypt)";

View File

@@ -2,15 +2,17 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;
using MPF.Core.Converters;
using MPF.Core.Utilities;
using RedumpLib.Data;
#if NET_FRAMEWORK
#if NETFRAMEWORK
using System.Management;
using IMAPI2;
#else
using Aaru.CommonTypes.Enums;
using AaruDevices = Aaru.Devices;
using Aaru.Core.Media.Info;
using Aaru.Decoders.SCSI.MMC;
using Aaru.Devices;
#endif
namespace MPF.Core.Data
@@ -18,8 +20,14 @@ namespace MPF.Core.Data
/// <summary>
/// Represents information for a single drive
/// </summary>
/// <remarks>
/// TODO: This needs to be less Windows-centric. Devices do not always have a single letter that can be used.
/// TODO: Can the Aaru models be used instead of the ones I've created here?
/// </remarks>
public class Drive
{
#region Fields
/// <summary>
/// Represents drive type
/// </summary>
@@ -28,41 +36,32 @@ namespace MPF.Core.Data
/// <summary>
/// Drive partition format
/// </summary>
public string DriveFormat => driveInfo?.DriveFormat;
/// <summary>
/// Windows drive letter
/// </summary>
public char Letter => driveInfo?.Name[0] ?? '\0';
public string DriveFormat { get; private set; } = null;
/// <summary>
/// Windows drive path
/// </summary>
public string Name => driveInfo?.Name;
public string Name { get; private set; } = null;
/// <summary>
/// Represents if Windows has marked the drive as active
/// </summary>
public bool MarkedActive => driveInfo?.IsReady ?? false;
public bool MarkedActive { get; private set; } = false;
/// <summary>
/// Represents the total size of the drive
/// </summary>
public long TotalSize { get; private set; } = default;
/// <summary>
/// Media label as read by Windows
/// </summary>
/// <remarks>The try/catch is needed because Windows will throw an exception if the drive is not marked as active</remarks>
public string VolumeLabel
{
get
{
try
{
return driveInfo?.VolumeLabel;
}
catch
{
return null;
}
}
}
public string VolumeLabel { get; private set; } = null;
#endregion
#region Derived Fields
/// <summary>
/// Media label as read by Windows, formatted to avoid odd outputs
@@ -72,12 +71,12 @@ namespace MPF.Core.Data
get
{
string volumeLabel = Template.DiscNotDetected;
if (driveInfo.IsReady)
if (this.MarkedActive)
{
if (string.IsNullOrWhiteSpace(driveInfo.VolumeLabel))
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
volumeLabel = "track";
else
volumeLabel = driveInfo.VolumeLabel;
volumeLabel = this.VolumeLabel;
}
foreach (char c in Path.GetInvalidFileNameChars())
@@ -88,167 +87,90 @@ namespace MPF.Core.Data
}
/// <summary>
/// DriveInfo object representing the drive, if possible
/// Windows drive letter
/// </summary>
private readonly DriveInfo driveInfo;
public char Letter => this.Name == null || this.Name.Length == 0 ? '\0' : this.Name[0];
public Drive(InternalDriveType? driveType, DriveInfo driveInfo)
#endregion
/// <summary>
/// Protected constructor
/// </summary>
protected Drive() { }
/// <summary>
/// Create a new Drive object from a drive type and device path
/// </summary>
/// <param name="driveType">InternalDriveType value representing the drive type</param>
/// <param name="devicePath">Path to the device according to the local machine</param>
public static Drive Create(InternalDriveType? driveType, string devicePath)
{
this.InternalDriveType = driveType;
this.driveInfo = driveInfo;
// Create a new, empty drive object
var drive = new Drive()
{
InternalDriveType = driveType,
};
// If we have an invalid device path, return null
if (string.IsNullOrWhiteSpace(devicePath))
return null;
// Sanitize a Windows-formatted long device path
if (devicePath.StartsWith("\\\\.\\"))
devicePath = devicePath.Substring("\\\\.\\".Length);
// Create and validate the drive info object
var driveInfo = new DriveInfo(devicePath);
if (driveInfo == null || driveInfo == default)
return null;
// Fill in the rest of the data
drive.PopulateFromDriveInfo(driveInfo);
return drive;
}
/// <summary>
/// Populate all fields from a DriveInfo object
/// </summary>
/// <param name="driveInfo">DriveInfo object to populate from</param>
private void PopulateFromDriveInfo(DriveInfo driveInfo)
{
// If we have an invalid DriveInfo, just return
if (driveInfo == null || driveInfo == default)
return;
// Populate the data fields
this.Name = driveInfo.Name;
this.MarkedActive = driveInfo.IsReady;
if (this.MarkedActive)
{
this.DriveFormat = driveInfo.DriveFormat;
this.TotalSize = driveInfo.TotalSize;
this.VolumeLabel = driveInfo.VolumeLabel;
}
}
#region Public Functionality
/// <summary>
/// Create a list of active drives matched to their volume labels
/// </summary>
/// <param name="ignoreFixedDrives">Ture to ignore fixed drives from population, false otherwise</param>
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
/// <returns>Active drives, matched to labels, if possible</returns>
/// <remarks>
/// https://stackoverflow.com/questions/3060796/how-to-distinguish-between-usb-and-floppy-devices?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
/// https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx
/// </remarks>
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
{
var desiredDriveTypes = new List<DriveType>() { DriveType.CDRom };
if (!ignoreFixedDrives)
{
desiredDriveTypes.Add(DriveType.Fixed);
desiredDriveTypes.Add(DriveType.Removable);
}
// Get all supported drive types
var drives = DriveInfo.GetDrives()
.Where(d => desiredDriveTypes.Contains(d.DriveType))
.Select(d => new Drive(EnumConverter.ToInternalDriveType(d.DriveType), d))
.ToList();
// TODO: Management searcher stuff is not supported on other platforms
// Get the floppy drives and set the flag from removable
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_LogicalDisk");
var collection = searcher.Get();
foreach (ManagementObject queryObj in collection)
{
uint? mediaType = (uint?)queryObj["MediaType"];
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
{
char devId = queryObj["DeviceID"].ToString()[0];
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
}
}
}
catch
{
// No-op
}
// Order the drives by drive letter
drives = drives.OrderBy(i => i.Letter).ToList();
var drives = GetDriveList(ignoreFixedDrives);
drives = drives?.OrderBy(i => i.Letter)?.ToList();
return drives;
}
/// <summary>
/// Get the current media type from drive letter
/// </summary>
/// <param name="drive"></param>
/// <returns></returns>
/// <remarks>
/// This may eventually be replaced by Aaru.Devices being able to be about 10x more accurate.
/// This will also end up making it so that IMAPI2 is no longer necessary. Unfortunately, that
/// will only work for .NET Core 3.1 and beyond.
/// </remarks>
public (MediaType?, string) GetMediaType()
{
// Take care of the non-optical stuff first
// TODO: See if any of these can be more granular, like Optical is
if (this.InternalDriveType == Data.InternalDriveType.Floppy)
return (MediaType.FloppyDisk, null);
else if (this.InternalDriveType == Data.InternalDriveType.HardDisk)
return (MediaType.HardDisk, null);
else if (this.InternalDriveType == Data.InternalDriveType.Removable)
return (MediaType.FlashDrive, null);
#if NET_FRAMEWORK
// Get the current drive information
string deviceId = null;
bool loaded = false;
try
{
// Get the device ID first
var searcher = new ManagementObjectSearcher(
"root\\CIMV2",
$"SELECT * FROM Win32_CDROMDrive WHERE Id = '{this.Letter}:\'");
foreach (ManagementObject queryObj in searcher.Get())
{
deviceId = (string)queryObj["DeviceID"];
loaded = (bool)queryObj["MediaLoaded"];
}
// If we got no valid device, we don't care and just return
if (deviceId == null)
return (null, "Device could not be found");
else if (!loaded)
return (null, "Device is not reporting media loaded");
MsftDiscMaster2 discMaster = new MsftDiscMaster2();
deviceId = deviceId.ToLower().Replace('\\', '#').Replace('/', '#');
string id = null;
foreach (var disc in discMaster)
{
if (disc.ToString().Contains(deviceId))
id = disc.ToString();
}
// If we couldn't find the drive, we don't care and return
if (id == null)
return (null, "Device ID could not be found");
// Create the required objects for reading from the drive
MsftDiscRecorder2 recorder = new MsftDiscRecorder2();
recorder.InitializeDiscRecorder(id);
MsftDiscFormat2Data dataWriter = new MsftDiscFormat2Data();
// If the recorder is not supported, just return
if (!dataWriter.IsRecorderSupported(recorder))
return (null, "IMAPI2 recorder not supported");
// Otherwise, set the recorder to get information from
dataWriter.Recorder = recorder;
var media = dataWriter.CurrentPhysicalMediaType;
return (media.IMAPIToMediaType(), null);
}
catch (Exception ex)
{
return (null, ex.Message);
}
#else
try
{
var device = new AaruDevices.Device(this.Name);
if (device.Error)
return (null, "Could not open device");
else if (device.Type != DeviceType.ATAPI && device.Type != DeviceType.SCSI)
return (null, "Device does not support media type detection");
// TODO: In order to get the disc type, Aaru.Core will need to be included as a
// package. Unfortunately, it currently has a conflict with one of the required libraries:
// System.Text.Encoding.CodePages (BOS uses >= 5.0.0, DotNetZip uses >= 4.5.0 && < 5.0.0)
}
catch (Exception ex)
{
return (null, ex.Message);
}
return (null, "Media detection only supported on .NET Framework");
#endif
}
=> GetMediaType(this.Name, this.InternalDriveType);
/// <summary>
/// Get the current system from drive
@@ -273,19 +195,51 @@ namespace MPF.Core.Data
if (systemFromLabel != null)
return systemFromLabel;
// Get a list of files for quicker checking
#region Consoles
// Bandai Playdia Quick Interactive System
try
{
List<string> files = Directory.EnumerateFiles(drivePath, "*", SearchOption.TopDirectoryOnly).ToList();
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
{
return RedumpSystem.BandaiPlaydiaQuickInteractiveSystem;
}
}
catch { }
// Mattel Fisher-Price iXL
if (File.Exists(Path.Combine(drivePath, "iXL", "iXLUpdater.exe")))
{
return RedumpSystem.MattelFisherPriceiXL;
}
// Microsoft Xbox 360
try
{
if (Directory.Exists(Path.Combine(drivePath, "$SystemUpdate"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any())
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any()
&& this.TotalSize <= 500_000_000)
{
return RedumpSystem.MicrosoftXbox360;
}
}
catch { }
// Microsoft Xbox One
try
{
if (Directory.Exists(Path.Combine(drivePath, "MSXC"))
&& Directory.EnumerateFiles(Path.Combine(drivePath, "MSXC")).Any())
{
return RedumpSystem.MicrosoftXboxOne;
}
}
catch { }
// Sega Dreamcast
if (File.Exists(Path.Combine(drivePath, "IP.BIN")))
{
@@ -330,6 +284,35 @@ namespace MPF.Core.Data
return RedumpSystem.SonyPlayStation;
}
// Sony PlayStation 3
try
{
if (Directory.Exists(Path.Combine(drivePath, "PS3_GAME"))
|| Directory.Exists(Path.Combine(drivePath, "PS3_UPDATE"))
|| File.Exists(Path.Combine(drivePath, "PS3_DISC.SFB")))
{
return RedumpSystem.SonyPlayStation3;
}
}
catch { }
// Sony PlayStation 4
// There are more possible paths that could be checked.
// There are some entries that can be found on most PS4 discs:
// "/app/GAME_SERIAL/app.pkg"
// "/bd/param.sfo"
// "/license/rif"
// There are also extra files that can be found on some discs:
// "/patch/GAME_SERIAL/patch.pkg" can be found in Redump entry 66816.
// Originally on disc as "/patch/CUSA11302/patch.pkg".
// Is used as an on-disc update for the base game app without needing to get update from the internet.
// "/addcont/GAME_SERIAL/CONTENT_ID/ac.pkg" can be found in Redump entry 97619.
// Originally on disc as "/addcont/CUSA00288/FFXIVEXPS400001A/ac.pkg".
if (File.Exists(Path.Combine(drivePath, "PS4", "UPDATE", "PS4UPDATE.PUP")))
{
return RedumpSystem.SonyPlayStation4;
}
// V.Tech V.Flash / V.Smile Pro
if (File.Exists(Path.Combine(drivePath, "0SYSTEM")))
{
@@ -338,6 +321,16 @@ namespace MPF.Core.Data
#endregion
#region Computers
// Sharp X68000
if (File.Exists(Path.Combine(drivePath, "COMMAND.X")))
{
return RedumpSystem.SharpX68000;
}
#endregion
#region Video Formats
// BD-Video
@@ -386,7 +379,7 @@ namespace MPF.Core.Data
}
catch { }
#endregion
#endregion
// Default return
return defaultValue;
@@ -424,6 +417,10 @@ namespace MPF.Core.Data
//if (this.VolumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
// return RedumpSystem.MicrosoftXbox360;
// Sega Mega-CD / Sega-CD
if (this.VolumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SegaMegaCDSegaCD;
// Sony PlayStation 3
if (this.VolumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SonyPlayStation3;
@@ -448,7 +445,7 @@ namespace MPF.Core.Data
public byte[] ReadSector(long num, int size = 2048)
{
// Missing drive leter is not supported
if (string.IsNullOrEmpty(this.driveInfo?.Name))
if (string.IsNullOrEmpty(this.Name))
return null;
// We don't support negative sectors
@@ -480,5 +477,383 @@ namespace MPF.Core.Data
fs?.Dispose();
}
}
/// <summary>
/// Refresh the current drive information based on path
/// </summary>
public void RefreshDrive()
{
var driveInfo = DriveInfo.GetDrives().FirstOrDefault(d => d?.Name == this.Name);
this.PopulateFromDriveInfo(driveInfo);
}
#endregion
#region Helpers
#if NETFRAMEWORK
/// <summary>
/// Get all current attached Drives
/// </summary>
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
/// <returns>List of drives, null on error</returns>
/// <remarks>
/// https://stackoverflow.com/questions/3060796/how-to-distinguish-between-usb-and-floppy-devices?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
/// https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx
/// </remarks>
private static List<Drive> GetDriveList(bool ignoreFixedDrives)
{
var desiredDriveTypes = new List<DriveType>() { DriveType.CDRom };
if (!ignoreFixedDrives)
{
desiredDriveTypes.Add(DriveType.Fixed);
desiredDriveTypes.Add(DriveType.Removable);
}
// Get all supported drive types
var drives = DriveInfo.GetDrives()
.Where(d => desiredDriveTypes.Contains(d.DriveType))
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name))
.ToList();
// Get the floppy drives and set the flag from removable
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_LogicalDisk");
var collection = searcher.Get();
foreach (ManagementObject queryObj in collection)
{
uint? mediaType = (uint?)queryObj["MediaType"];
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
{
char devId = queryObj["DeviceID"].ToString()[0];
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
}
}
}
catch
{
// No-op
}
return drives;
}
/// <summary>
/// Get the media type for a device path using the Aaru libraries
/// </summary>
/// <param name="devicePath">Path to the device</param>
/// <param name="internalDriveType">Current internal drive type</param>
/// <returns>MediaType, null on error</returns>
private static (MediaType?, string) GetMediaType(string devicePath, InternalDriveType? internalDriveType)
{
char driveLetter = devicePath == null || !devicePath.Any() ? '\0' : devicePath[0];
// Take care of the non-optical stuff first
// TODO: See if any of these can be more granular, like Optical is
if (internalDriveType == Data.InternalDriveType.Floppy)
return (MediaType.FloppyDisk, null);
else if (internalDriveType == Data.InternalDriveType.HardDisk)
return (MediaType.HardDisk, null);
else if (internalDriveType == Data.InternalDriveType.Removable)
return (MediaType.FlashDrive, null);
// Get the current drive information
string deviceId = null;
bool loaded = false;
try
{
// Get the device ID first
var searcher = new ManagementObjectSearcher(
"root\\CIMV2",
$"SELECT * FROM Win32_CDROMDrive WHERE Id = '{driveLetter}:\'");
foreach (ManagementObject queryObj in searcher.Get())
{
deviceId = (string)queryObj["DeviceID"];
loaded = (bool)queryObj["MediaLoaded"];
}
// If we got no valid device, we don't care and just return
if (deviceId == null)
return (null, "Device could not be found");
else if (!loaded)
return (null, "Device is not reporting media loaded");
MsftDiscMaster2 discMaster = new MsftDiscMaster2();
deviceId = deviceId.ToLower().Replace('\\', '#').Replace('/', '#');
string id = null;
foreach (var disc in discMaster)
{
if (disc.ToString().Contains(deviceId))
id = disc.ToString();
}
// If we couldn't find the drive, we don't care and return
if (id == null)
return (null, "Device ID could not be found");
// Create the required objects for reading from the drive
MsftDiscRecorder2 recorder = new MsftDiscRecorder2();
recorder.InitializeDiscRecorder(id);
MsftDiscFormat2Data dataWriter = new MsftDiscFormat2Data();
// If the recorder is not supported, just return
if (!dataWriter.IsRecorderSupported(recorder))
return (null, "IMAPI2 recorder not supported");
// Otherwise, set the recorder to get information from
dataWriter.Recorder = recorder;
var media = dataWriter.CurrentPhysicalMediaType;
return (media.IMAPIToMediaType(), null);
}
catch (Exception ex)
{
return (null, ex.Message);
}
}
#else
/// <summary>
/// Get all devices attached converted to Drive objects
/// </summary>
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
/// <returns>List of drives, null on error</returns>
private static List<Drive> GetDriveList(bool ignoreFixedDrives)
{
DeviceInfo[] deviceInfos = Device.ListDevices();
if (deviceInfos == null)
return null;
var drives = new List<Drive>();
foreach (DeviceInfo deviceInfo in deviceInfos)
{
if (deviceInfo.Path == null)
continue;
if (!deviceInfo.Supported)
continue;
var drive = GetDriveFromDevice(deviceInfo.Path, ignoreFixedDrives);
if (drive == null)
continue;
drives.Add(drive);
}
return drives;
}
/// <summary>
/// Generate a Drive object from a single device
/// </summary>
/// <param name="devicePath">Path to the device</param>
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
/// <returns>Drive object for the device, null on error</returns>
private static Drive GetDriveFromDevice(string devicePath, bool ignoreFixedDrives)
{
if (devicePath.Length == 2 &&
devicePath[1] == ':' &&
devicePath[0] != '/' &&
char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
string windowsLocalDevicePath = devicePath;
if (windowsLocalDevicePath.StartsWith("\\\\.\\"))
windowsLocalDevicePath = windowsLocalDevicePath.Substring("\\\\.\\".Length);
var dev = Device.Create(devicePath, out _);
if (dev == null || dev.Error)
return null;
var devInfo = new Aaru.Core.Devices.Info.DeviceInfo(dev);
if (devInfo.MmcConfiguration != null)
{
Features.SeparatedFeatures ftr = Features.Separate(devInfo.MmcConfiguration);
if (ftr.Descriptors != null && ftr.Descriptors.Any(d => d.Code == 0x0000))
{
var desc = ftr.Descriptors.First(d => d.Code == 0x0000);
bool isOptical = IsOptical(desc.Data);
if (isOptical)
return Create(Data.InternalDriveType.Optical, windowsLocalDevicePath);
else if (!ignoreFixedDrives)
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
}
}
if (!ignoreFixedDrives)
{
switch (dev.Type)
{
case DeviceType.MMC:
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
case DeviceType.SecureDigital:
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
}
if (dev.IsUsb)
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
if (dev.IsFireWire)
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
if (dev.IsPcmcia)
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
}
dev.Close();
return null;
}
/// <summary>
/// Get the media type for a device path using the Aaru libraries
/// </summary>
/// <param name="devicePath">Path to the device</param>
/// <param name="internalDriveType">Current internal drive type</param>
/// <returns>MediaType, null on error</returns>
/// <remarks>
/// TODO: Get the device type for devices with set media types
/// </remarks>
private static (MediaType?, string) GetMediaType(string devicePath, InternalDriveType? internalDriveType)
{
if (devicePath.Length == 2 &&
devicePath[1] == ':' &&
devicePath[0] != '/' &&
char.IsLetter(devicePath[0]))
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
var dev = Device.Create(devicePath, out _);
if (dev == null || dev.Error)
return (null, "Device could not be accessed");
switch (dev.Type)
{
case DeviceType.ATAPI:
case DeviceType.SCSI:
ScsiInfo scsiInfo = new ScsiInfo(dev);
var mediaType = EnumConverter.MediaTypeToMediaType(scsiInfo?.MediaType);
if (mediaType == null)
return (mediaType, "Could not determine media type");
else
return (mediaType, null);
}
return (null, "Device does not support media type finding");
}
/// <summary>
/// Determine if a drive is optical or not
/// </summary>
/// <param name="featureBytes">Bytes representing the field to check</param>
/// <returns>True if the drive is optical, false otherwise</returns>
private static bool IsOptical(byte[] featureBytes)
{
var supportedMediaTypes = OpticalMediaSupport(featureBytes);
if (supportedMediaTypes == null || !supportedMediaTypes.Any())
return false;
if (supportedMediaTypes.Contains(MediaType.CDROM))
return true;
else if (supportedMediaTypes.Contains(MediaType.DVD))
return true;
else if (supportedMediaTypes.Contains(MediaType.BluRay))
return true;
else if (supportedMediaTypes.Contains(MediaType.HDDVD))
return true;
return false;
}
/// <summary>
/// Get supported media types for a drive
/// </summary>
/// <param name="featureBytes">Bytes representing the field to check</param>
/// <returns>List of supported media types, null on error</returns>
private static List<MediaType> OpticalMediaSupport(byte[] featureBytes)
{
Feature_0000? feature = Features.Decode_0000(featureBytes);
if (!feature.HasValue)
return null;
var supportedMediaTypes = new List<MediaType>();
foreach (Profile prof in feature.Value.Profiles)
{
switch (prof.Number)
{
// Values we don't care about for Optical
case ProfileNumber.Reserved:
case ProfileNumber.NonRemovable:
case ProfileNumber.Removable:
case ProfileNumber.MOErasable:
case ProfileNumber.OpticalWORM:
case ProfileNumber.ASMO:
case ProfileNumber.Unconforming:
break;
// Every supported optical profile
case ProfileNumber.CDROM:
case ProfileNumber.CDR:
case ProfileNumber.CDRW:
supportedMediaTypes.Add(MediaType.CDROM);
break;
case ProfileNumber.DVDROM:
case ProfileNumber.DVDRSeq:
case ProfileNumber.DVDRAM:
case ProfileNumber.DVDRWRes:
case ProfileNumber.DVDRWSeq:
case ProfileNumber.DVDRDLSeq:
case ProfileNumber.DVDRDLJump:
case ProfileNumber.DVDRWDL:
case ProfileNumber.DVDDownload:
case ProfileNumber.DVDRWPlus:
case ProfileNumber.DVDRPlus:
case ProfileNumber.DVDRWDLPlus:
case ProfileNumber.DVDRDLPlus:
supportedMediaTypes.Add(MediaType.DVD);
break;
// TODO: Add DDCD as media type
case ProfileNumber.DDCDROM:
case ProfileNumber.DDCDR:
case ProfileNumber.DDCDRW:
supportedMediaTypes.Add(MediaType.DVD);
break;
case ProfileNumber.BDROM:
case ProfileNumber.BDRSeq:
case ProfileNumber.BDRRdm:
case ProfileNumber.BDRE:
supportedMediaTypes.Add(MediaType.BluRay);
break;
case ProfileNumber.HDDVDROM:
case ProfileNumber.HDDVDR:
case ProfileNumber.HDDVDRAM:
case ProfileNumber.HDDVDRW:
case ProfileNumber.HDDVDRDL:
case ProfileNumber.HDDVDRWDL:
case ProfileNumber.HDBURNROM:
case ProfileNumber.HDBURNR:
case ProfileNumber.HDBURNRW:
supportedMediaTypes.Add(MediaType.HDDVD);
break;
}
}
return supportedMediaTypes.Distinct().ToList();
}
#endif
#endregion
}
}

View File

@@ -22,6 +22,7 @@
Aaru,
DD,
DiscImageCreator,
Redumper,
// Verification support only
CleanRip,

View File

@@ -39,6 +39,15 @@ namespace MPF.Core.Data
set { _settings["DDPath"] = value; }
}
/// <summary>
/// Path to Redumper
/// </summary>
public string RedumperPath
{
get { return GetStringSetting(_settings, "RedumperPath", "Programs\\RedumperPath\\redumper.exe"); }
set { _settings["RedumperPath"] = value; }
}
/// <summary>
/// Currently selected dumping program
/// </summary>
@@ -198,6 +207,24 @@ namespace MPF.Core.Data
#region DiscImageCreator
/// <summary>
/// Enable multi-sector read flag by default
/// </summary>
public bool DICMultiSectorRead
{
get { return GetBooleanSetting(_settings, "DICMultiSectorRead", false); }
set { _settings["DICMultiSectorRead"] = value.ToString(); }
}
/// <summary>
/// Include a default multi-sector read value
/// </summary>
public int DICMultiSectorReadValue
{
get { return GetInt32Setting(_settings, "DICMultiSectorReadValue", 0); }
set { _settings["DICMultiSectorRead"] = value.ToString(); }
}
/// <summary>
/// Enable overly-secure dumping flags by default
/// </summary>
@@ -230,6 +257,15 @@ namespace MPF.Core.Data
set { _settings["DICRereadCount"] = value.ToString(); }
}
/// <summary>
/// Default number of DVD/HD-DVD/BD rereads
/// </summary>
public int DICDVDRereadCount
{
get { return GetInt32Setting(_settings, "DICDVDRereadCount", 10); }
set { _settings["DICDVDRereadCount"] = value.ToString(); }
}
/// <summary>
/// Reset drive after dumping (useful for older drives)
/// </summary>
@@ -261,6 +297,15 @@ namespace MPF.Core.Data
set { _settings["ScanForProtection"] = value.ToString(); }
}
/// <summary>
/// Output all found protections to a separate file in the directory
/// </summary>
public bool OutputSeparateProtectionFile
{
get { return GetBooleanSetting(_settings, "OutputSeparateProtectionFile", true); }
set { _settings["OutputSeparateProtectionFile"] = value.ToString(); }
}
/// <summary>
/// Add placeholder values in the submission info
/// </summary>
@@ -288,6 +333,15 @@ namespace MPF.Core.Data
set { _settings["EnableTabsInInputFields"] = value.ToString(); }
}
/// <summary>
/// Limit outputs to Redump-supported values only
/// </summary>
public bool EnableRedumpCompatibility
{
get { return GetBooleanSetting(_settings, "EnableRedumpCompatibility", true); }
set { _settings["EnableRedumpCompatibility"] = value.ToString(); }
}
/// <summary>
/// Show disc eject reminder before the disc information window is shown
/// </summary>
@@ -395,15 +449,6 @@ namespace MPF.Core.Data
set { _settings["ScanPackersForProtection"] = value.ToString(); }
}
/// <summary>
/// Force scanning all files for protection
/// </summary>
public bool ForceScanningForProtection
{
get { return GetBooleanSetting(_settings, "ForceScanningForProtection", false); }
set { _settings["ForceScanningForProtection"] = value.ToString(); }
}
/// <summary>
/// Include debug information with scan results
/// </summary>

View File

@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
<TargetFrameworks>net48;net6.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<Copyright>Copyright (c)2019-2022</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.3</Version>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -20,10 +20,6 @@
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
<DefineConstants>NET_FRAMEWORK</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
<COMReference Include="IMAPI2">
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
@@ -49,17 +45,21 @@
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
<ProjectReference Include="..\Aaru\CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
<ProjectReference Include="..\Aaru\Aaru.CommonTypes\Aaru.CommonTypes.csproj" />
<ProjectReference Include="..\Aaru\Aaru.Core\Aaru.Core.csproj" />
<ProjectReference Include="..\Aaru\Aaru.Devices\Aaru.Devices.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.Management" Version="6.0.0-rc.1.21451.13" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'!='net48'">
<PackageReference Include="Aaru.Devices" Version="5.3.0-rc2" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Management" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" />
<PackageReference Include="System.Management" Version="6.0.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>
</Project>

396
MPF.Core/Utilities/Chime.cs Normal file
View File

@@ -0,0 +1,396 @@
using System;
namespace MPF.Core.Utilities
{
/// <summary>
/// Methods to deal with outputting tones to the PC speaker
/// </summary>
public class Chime
{
/// <summary>
/// Standard duration to play a single tone
/// </summary>
private const int standardDurationMs = 200;
#region Octave 0
/// <summary>
/// Frequency representing C(0)
/// </summary>
private const int noteC0 = 16; // 16.35
/// <summary>
/// Frequency representing D(0)
/// </summary>
private const int noteD0 = 18; // 18.35
/// <summary>
/// Frequency representing E(0)
/// </summary>
private const int noteE0 = 21; // 20.60
/// <summary>
/// Frequency representing F(0)
/// </summary>
private const int noteF0 = 22; // 21.83
/// <summary>
/// Frequency representing G(0)
/// </summary>
private const int noteG0 = 25; // 24.50
/// <summary>
/// Frequency representing A(0)
/// </summary>
private const int noteA0 = 28; // 27.50
/// <summary>
/// Frequency representing B(0)
/// </summary>
private const int noteB0 = 31; // 30.87
#endregion
#region Octave 1
/// <summary>
/// Frequency representing C(1)
/// </summary>
private const int noteC1 = 33; // 32.70
/// <summary>
/// Frequency representing D(1)
/// </summary>
private const int noteD1 = 37; // 36.71
/// <summary>
/// Frequency representing E(1)
/// </summary>
private const int noteE1 = 41; // 41.20
/// <summary>
/// Frequency representing F(1)
/// </summary>
private const int noteF1 = 44; // 43.65
/// <summary>
/// Frequency representing G(1)
/// </summary>
private const int noteG1 = 49; // 49.00
/// <summary>
/// Frequency representing A(1)
/// </summary>
private const int noteA1 = 55; // 55.00
/// <summary>
/// Frequency representing B(1)
/// </summary>
private const int noteB1 = 62; // 61.74
#endregion
#region Octave 2
/// <summary>
/// Frequency representing C(2)
/// </summary>
private const int noteC2 = 65; // 65.41
/// <summary>
/// Frequency representing D(2)
/// </summary>
private const int noteD2 = 73; // 73.42
/// <summary>
/// Frequency representing E(2)
/// </summary>
private const int noteE2 = 82; // 82.41
/// <summary>
/// Frequency representing F(2)
/// </summary>
private const int noteF2 = 87; // 87.31
/// <summary>
/// Frequency representing G(2)
/// </summary>
private const int noteG2 = 98; // 98.00
/// <summary>
/// Frequency representing A(2)
/// </summary>
private const int noteA2 = 110; // 110.00
/// <summary>
/// Frequency representing B(2)
/// </summary>
private const int noteB2 = 123; // 123.47
#endregion
#region Octave 3
/// <summary>
/// Frequency representing C(3)
/// </summary>
private const int noteC3 = 131; // 130.81
/// <summary>
/// Frequency representing D(3)
/// </summary>
private const int noteD3 = 147; // 146.83
/// <summary>
/// Frequency representing E(3)
/// </summary>
private const int noteE3 = 165; // 164.81
/// <summary>
/// Frequency representing F(3)
/// </summary>
private const int noteF3 = 175; // 174.61
/// <summary>
/// Frequency representing G(3)
/// </summary>
private const int noteG3 = 196; // 196.00
/// <summary>
/// Frequency representing A(3)
/// </summary>
private const int noteA3 = 220; // 220.00
/// <summary>
/// Frequency representing B(3)
/// </summary>
private const int noteB3 = 247; // 246.94
#endregion
#region Octave 4
/// <summary>
/// Frequency representing C(4)
/// </summary>
private const int noteC4 = 262; // 261.63
/// <summary>
/// Frequency representing D(4)
/// </summary>
private const int noteD4 = 294; // 293.66
/// <summary>
/// Frequency representing E(4)
/// </summary>
private const int noteE4 = 330; // 329.63
/// <summary>
/// Frequency representing F(4)
/// </summary>
private const int noteF4 = 349; // 349.23
/// <summary>
/// Frequency representing G(4)
/// </summary>
private const int noteG4 = 392; // 392.00
/// <summary>
/// Frequency representing A(4)
/// </summary>
private const int noteA4 = 440; // 440.00
/// <summary>
/// Frequency representing B(4)
/// </summary>
private const int noteB4 = 494; // 493.88
#endregion
#region Octave 5
/// <summary>
/// Frequency representing C(5)
/// </summary>
private const int noteC5 = 523; // 523.25
/// <summary>
/// Frequency representing D(5)
/// </summary>
private const int noteD5 = 587; // 587.33
/// <summary>
/// Frequency representing E(5)
/// </summary>
private const int noteE5 = 659; // 659.25
/// <summary>
/// Frequency representing F(5)
/// </summary>
private const int noteF5 = 698; // 698.46
/// <summary>
/// Frequency representing G(5)
/// </summary>
private const int noteG5 = 783; // 783.99
/// <summary>
/// Frequency representing A(5)
/// </summary>
private const int noteA5 = 880; // 880.00
/// <summary>
/// Frequency representing B(5)
/// </summary>
private const int noteB5 = 988; // 987.77
#endregion
#region Octave 6
/// <summary>
/// Frequency representing C(6)
/// </summary>
private const int noteC6 = 1047; // 1046.50
/// <summary>
/// Frequency representing D(6)
/// </summary>
private const int noteD6 = 1175; // 1174.66
/// <summary>
/// Frequency representing E(6)
/// </summary>
private const int noteE6 = 1319; // 1318.51
/// <summary>
/// Frequency representing F(6)
/// </summary>
private const int noteF6 = 1397; // 1396.91
/// <summary>
/// Frequency representing G(6)
/// </summary>
private const int noteG6 = 1568; // 1567.98
/// <summary>
/// Frequency representing A(6)
/// </summary>
private const int noteA6 = 1760; // 1760.00
/// <summary>
/// Frequency representing B(6)
/// </summary>
private const int noteB6 = 1976; // 1975.53
#endregion
#region Octave 7
/// <summary>
/// Frequency representing C(7)
/// </summary>
private const int noteC7 = 2093; // 2093.00
/// <summary>
/// Frequency representing D(7)
/// </summary>
private const int noteD7 = 2349; // 2349.32
/// <summary>
/// Frequency representing E(7)
/// </summary>
private const int noteE7 = 2637; // 2637.02
/// <summary>
/// Frequency representing F(7)
/// </summary>
private const int noteF7 = 2794; // 2793.83
/// <summary>
/// Frequency representing G(7)
/// </summary>
private const int noteG7 = 3136; // 3135.96
/// <summary>
/// Frequency representing A(7)
/// </summary>
private const int noteA7 = 3520; // 3520.00
/// <summary>
/// Frequency representing B(7)
/// </summary>
private const int noteB7 = 3951; // 3951.07
#endregion
#region Octave 8
/// <summary>
/// Frequency representing C(8)
/// </summary>
private const int noteC8 = 4186; // 4186.01
/// <summary>
/// Frequency representing D(8)
/// </summary>
private const int noteD8 = 4699; // 4698.63
/// <summary>
/// Frequency representing E(8)
/// </summary>
private const int noteE8 = 5274; // 5274.04
/// <summary>
/// Frequency representing F(8)
/// </summary>
private const int noteF8 = 5588; // 5587.65
/// <summary>
/// Frequency representing G(8)
/// </summary>
private const int noteG8 = 6272; // 6271.93
/// <summary>
/// Frequency representing A(8)
/// </summary>
private const int noteA8 = 7040; // 7040.00
/// <summary>
/// Frequency representing B(8)
/// </summary>
private const int noteB8 = 7902; // 7902.13
#endregion
/// <summary>
/// Output a series of beeps for completion, similar to DiscImageCreator
/// </summary>
/// <param name="success">True if the upward series should play, false otherwise</param>
public static void StandardCompletion(bool success)
{
if (success)
{
Console.Beep(noteC4, standardDurationMs);
Console.Beep(noteD4, standardDurationMs);
Console.Beep(noteE4, standardDurationMs);
Console.Beep(noteF4, standardDurationMs);
Console.Beep(noteG4, standardDurationMs);
Console.Beep(noteA4, standardDurationMs);
Console.Beep(noteB4, standardDurationMs);
Console.Beep(noteC5, standardDurationMs);
}
else
{
Console.Beep(noteC5, standardDurationMs);
Console.Beep(noteB4, standardDurationMs);
Console.Beep(noteA4, standardDurationMs);
Console.Beep(noteG4, standardDurationMs);
Console.Beep(noteF4, standardDurationMs);
Console.Beep(noteE4, standardDurationMs);
Console.Beep(noteD4, standardDurationMs);
Console.Beep(noteC4, standardDurationMs);
}
}
}
}

View File

@@ -1,4 +1,8 @@
using RedumpLib.Data;
using System;
using System.Collections.Generic;
using MPF.Core.Converters;
using MPF.Core.Data;
using RedumpLib.Data;
namespace MPF.Core.Utilities
{
@@ -102,26 +106,6 @@ namespace MPF.Core.Utilities
}
}
/// <summary>
/// Determine if a system is a marker value
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
/// <returns>True if the system is a marker value, false otherwise</returns>
public static bool IsMarker(this RedumpSystem? system)
{
switch (system)
{
case RedumpSystem.MarkerArcadeEnd:
case RedumpSystem.MarkerComputerEnd:
case RedumpSystem.MarkerDiscBasedConsoleEnd:
// case RedumpSystem.MarkerOtherConsoleEnd:
case RedumpSystem.MarkerOtherEnd:
return true;
default:
return false;
}
}
/// <summary>
/// Determine if a system is considered XGD
/// </summary>
@@ -140,5 +124,23 @@ namespace MPF.Core.Utilities
return false;
}
}
/// <summary>
/// List all programs with their short usable names
/// </summary>
public static List<string> ListPrograms()
{
var programs = new List<string>();
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
{
if (((InternalProgram)val) == InternalProgram.NONE)
continue;
programs.Add($"{((InternalProgram?)val).LongName()}");
}
return programs;
}
}
}

View File

@@ -0,0 +1,174 @@
using System.Collections.Generic;
using System.Configuration;
using MPF.Core.Converters;
using MPF.Core.Data;
namespace MPF.Core.Utilities
{
public static class OptionsLoader
{
#region Arguments
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
public static (Options, string, int) LoadFromArguments(string[] args, int startIndex = 0)
{
// Create the output values
var options = new Options();
string parsedPath = null;
// These values require multiple parts to be active
bool scan = false, protectFile = false;
// If we have no arguments, just return
if (args == null || args.Length == 0)
return (options, null, 0);
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
return (options, null, startIndex);
// Loop through the arguments and parse out values
for (; startIndex < args.Length; startIndex++)
{
// Redump login
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;
}
// Use specific program
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
{
string internalProgram = args[startIndex].Split('=')[1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
startIndex++;
}
// Use a device path for physical checks
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
{
parsedPath = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
{
parsedPath = args[startIndex + 1];
startIndex++;
}
// Scan for protection (requires device path)
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
{
scan = true;
}
// Output protection to separate file (requires scan for protection)
else if (args[startIndex].Equals("-f") || args[startIndex].Equals("--protect-file"))
{
protectFile = true;
}
// Output submission JSON
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
{
options.OutputSubmissionJSON = true;
}
// Compress log and extraneous files
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
{
options.CompressLogFiles = true;
}
// Default, we fall out
else
{
break;
}
}
// We default to DiscImageCreator currently
if (options.InternalProgram == InternalProgram.NONE)
options.InternalProgram = InternalProgram.DiscImageCreator;
// Now deal with the complex options
options.ScanForProtection = scan && !string.IsNullOrWhiteSpace(parsedPath);
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrWhiteSpace(parsedPath);
return (options, parsedPath, startIndex);
}
/// <summary>
/// Return a list of supported arguments and descriptions
/// </summary>
public static List<string> PrintSupportedArguments()
{
var supportedArguments = new List<string>();
supportedArguments.Add("-c, --credentials <user> <pw> Redump username and password");
supportedArguments.Add("-u, --use <program> Dumping program output type");
supportedArguments.Add("-p, --path <drivepath> Physical drive path for additional checks");
supportedArguments.Add("-s, --scan Enable copy protection scan (requires --path)");
supportedArguments.Add("-f, --protect-file Output protection to separate file (requires --scan)");
supportedArguments.Add("-j, --json Enable submission JSON output");
supportedArguments.Add("-z, --zip Enable log file compression");
return supportedArguments;
}
#endregion
#region Configuration
/// <summary>
/// Load the current set of options from the application configuration
/// </summary>
public static Options LoadFromConfig()
{
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
var settings = configFile.AppSettings.Settings;
var dict = new Dictionary<string, string>();
foreach (string key in settings.AllKeys)
{
dict[key] = settings[key]?.Value ?? string.Empty;
}
return new Options(dict);
}
/// <summary>
/// Save the current set of options to the application configuration
/// </summary>
public static void SaveToConfig(Options options)
{
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// Loop through all settings in Options and save them, overwriting existing settings
foreach (var kvp in options)
{
configFile.AppSettings.Settings.Remove(kvp.Key);
configFile.AppSettings.Settings.Add(kvp.Key, kvp.Value);
}
configFile.Save(ConfigurationSaveMode.Modified);
}
#endregion
}
}

View File

@@ -1,5 +1,4 @@
using System;
using System.Net;
using System.Reflection;
using MPF.Core.Data;
using Newtonsoft.Json.Linq;
@@ -171,7 +170,8 @@ namespace MPF.Core.Utilities
/// </summary>
private static (string tag, string url) GetRemoteVersionAndUrl()
{
using (WebClient wc = new WebClient())
#if NETFRAMEWORK
using (System.Net.WebClient wc = new System.Net.WebClient())
{
wc.Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0";
@@ -184,6 +184,21 @@ namespace MPF.Core.Utilities
return (latestTag, releaseUrl);
}
#else
using (System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient())
{
// TODO: Figure out a better way than having this hardcoded...
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
var message = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
string latestReleaseJsonString = hc.Send(message)?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
var latestReleaseJson = JObject.Parse(latestReleaseJsonString);
string latestTag = latestReleaseJson["tag_name"].ToString();
string releaseUrl = latestReleaseJson["html_url"].ToString();
return (latestTag, releaseUrl);
}
#endif
}
#endregion

View File

@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
<TargetFrameworks>net48;net6.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<Copyright>Copyright (c)2019-2022</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.3</Version>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -21,7 +21,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -138,6 +138,9 @@ namespace MPF.Library
// .Replace('.', '_');
//string tempExtension = Path.GetExtension(this.OutputFilename)?.TrimStart('.');
//this.OutputFilename = $"{tempFilename}.{tempExtension}";
// Assign the path to the filename as well for dumping
((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = Path.Combine(this.OutputDirectory, this.OutputFilename);
}
/// <summary>
@@ -161,6 +164,10 @@ namespace MPF.Library
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
break;
case InternalProgram.Redumper:
this.Parameters = new Modules.DD.Parameters(parameters) { ExecutablePath = Options.RedumperPath };
break;
// Verification support only
case InternalProgram.CleanRip:
this.Parameters = new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null };
@@ -215,6 +222,10 @@ namespace MPF.Library
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
break;
case InternalProgram.Redumper:
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
break;
// This should never happen, but it needs a fallback
default:
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
@@ -359,37 +370,51 @@ namespace MPF.Library
// Format the information for the text output
resultProgress?.Report(Result.Success("Formatting information..."));
List<string> formattedValues = InfoTool.FormatOutputData(submissionInfo);
resultProgress?.Report(Result.Success("Formatting complete!"));
(List<string> formattedValues, string formatResult) = InfoTool.FormatOutputData(submissionInfo, this.Options);
if (formattedValues == null)
resultProgress?.Report(Result.Success(formatResult));
else
resultProgress?.Report(Result.Failure(formatResult));
// Write the text output
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
bool success = InfoTool.WriteOutputData(this.OutputDirectory, formattedValues);
if (success)
resultProgress?.Report(Result.Success("Writing complete!"));
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(this.OutputDirectory, formattedValues);
if (txtSuccess)
resultProgress?.Report(Result.Success(txtResult));
else
resultProgress?.Report(Result.Failure("Writing could not complete!"));
resultProgress?.Report(Result.Failure(txtResult));
// Write the JSON output, if required
if (Options.OutputSubmissionJSON)
// Write the copy protection output
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
{
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.json.gz..."));
success = InfoTool.WriteOutputData(this.OutputDirectory, submissionInfo);
if (success)
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
bool scanSuccess = InfoTool.WriteProtectionData(this.OutputDirectory, submissionInfo);
if (scanSuccess)
resultProgress?.Report(Result.Success("Writing complete!"));
else
resultProgress?.Report(Result.Failure("Writing could not complete!"));
}
// Conpress the logs, if required
// Write the JSON output, if required
if (Options.OutputSubmissionJSON)
{
resultProgress?.Report(Result.Success($"Writing information to !submissionInfo.json{(Options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
bool jsonSuccess = InfoTool.WriteOutputData(this.OutputDirectory, submissionInfo, Options.IncludeArtifacts);
if (jsonSuccess)
resultProgress?.Report(Result.Success("Writing complete!"));
else
resultProgress?.Report(Result.Failure("Writing could not complete!"));
}
// Compress the logs, if required
if (Options.CompressLogFiles)
{
resultProgress?.Report(Result.Success("Compressing log files..."));
success = InfoTool.CompressLogFiles(this.OutputDirectory, this.OutputFilename, this.Parameters);
if (success)
resultProgress?.Report(Result.Success("Compression complete!"));
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(this.OutputDirectory, this.OutputFilename, this.Parameters);
if (compressSuccess)
resultProgress?.Report(Result.Success(compressResult));
else
resultProgress?.Report(Result.Failure("Compression could not complete!"));
resultProgress?.Report(Result.Failure(compressResult));
}
resultProgress?.Report(Result.Success("Submission information process complete!"));

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
<TargetFrameworks>net48;net6.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<AssemblyName>MPF.Library</AssemblyName>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<Copyright>Copyright (c)2019-2022</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.3</Version>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -21,10 +21,6 @@
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
<DefineConstants>NET_FRAMEWORK</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
@@ -32,12 +28,14 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.0" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.3.4" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -5,57 +5,81 @@ using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BurnOutSharp;
using BurnOutSharp.External.psxt001z;
using BurnOutSharp.ProtectionType;
using MPF.Core.Data;
using psxt001z;
namespace MPF.Library
{
public static class Protection
{
/// <summary>
/// Run protection scan on a given dump environment
/// Run protection scan on a given path
/// </summary>
/// <param name="path">Path to scan for protection</param>
/// <param name="options">Options object that determines what to scan</param>
/// <param name="progress">Optional progress callback</param>
/// <returns>TCopy protection detected in the envirionment, if any</returns>
public static async Task<(bool, string)> RunProtectionScanOnPath(string path, Options options, IProgress<ProtectionProgress> progress = null)
/// <returns>Set of all detected copy protections with an optional error string</returns>
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Options options, IProgress<ProtectionProgress> progress = null)
{
try
{
var found = await Task.Run(() =>
{
var scanner = new Scanner(progress)
{
IncludeDebug = options.IncludeDebugProtectionInformation,
ScanAllFiles = options.ForceScanningForProtection,
ScanArchives = options.ScanArchivesForProtection,
ScanPackers = options.ScanPackersForProtection,
};
var scanner = new Scanner(
options.ScanArchivesForProtection,
options.ScanPackersForProtection,
options.IncludeDebugProtectionInformation,
progress);
return scanner.GetProtections(path);
});
if (found == null || found.Count() == 0)
return (true, "None found");
// If nothing was returned, return
if (found == null || !found.Any())
return (null, null);
// Get an ordered list of distinct found protections
var orderedDistinctProtections = found
// Filter out any empty protections
var filteredProtections = found
.Where(kvp => kvp.Value != null && kvp.Value.Any())
.SelectMany(kvp => kvp.Value)
.Distinct()
.OrderBy(p => p);
.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.OrderBy(s => s).ToList());
// Sanitize and join protections for writing
string protections = SanitizeFoundProtections(orderedDistinctProtections);
return (true, protections);
// Return the filtered set of protections
return (filteredProtections, null);
}
catch (Exception ex)
{
return (false, ex.ToString());
return (null, ex.ToString());
}
}
/// <summary>
/// Format found protections to a deduplicated, ordered string
/// </summary>
/// <param name="protections">Dictionary of file to list of protection mappings</param>
/// <returns>Detected protections, if any</returns>
public static string FormatProtections(Dictionary<string, List<string>> protections)
{
// If the filtered list is empty in some way, return
if (protections == null || !protections.Any())
return "None found";
// Get an ordered list of distinct found protections
var orderedDistinctProtections = protections
.SelectMany(kvp => kvp.Value)
.Distinct()
.OrderBy(p => p);
// Sanitize and join protections for writing
string protectionString = SanitizeFoundProtections(orderedDistinctProtections);
if (string.IsNullOrWhiteSpace(protectionString))
return "None found";
return protectionString;
}
/// <summary>
/// Get the existence of an anti-modchip string from a PlayStation disc, if possible
/// </summary>
@@ -73,7 +97,7 @@ namespace MPF.Library
try
{
byte[] fileContent = File.ReadAllBytes(file);
string protection = antiModchip.CheckContents(file, fileContent, false, null, null);
string protection = antiModchip.CheckContents(file, fileContent, false);
if (!string.IsNullOrWhiteSpace(protection))
return true;
}
@@ -177,7 +201,18 @@ namespace MPF.Library
// SafeCast
// TODO: Figure this one out
// Cactus Data Shield / SafeDisc
if (foundProtections.Any(p => p == "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)"))
{
foundProtections = foundProtections
.Where(p => p != "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)");
if (foundProtections.Any(p => !p.StartsWith("SafeDisc")))
foundProtections = foundProtections.Append("Cactus Data Shield 300");
}
// SafeDisc
// TODO: Update based on new internal naming schemes
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}")))

View File

@@ -7,6 +7,7 @@ using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.CueSheets;
using RedumpLib.Data;
@@ -191,22 +192,50 @@ namespace MPF.Modules.Aaru
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
{
// TODO: Fill in submission info specifics for Aaru
string outputDirectory = Path.GetDirectoryName(basePath);
// TODO: Determine if there's an Aaru version anywhere
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
// Deserialize the sidecar, if possible
var sidecar = GenerateSidecar(basePath + ".cicm.xml");
// Fill in the hardware data
if (GetHardwareInfo(sidecar, out string manufacturer, out string model, out string firmware))
{
info.DumpingInfo.Manufacturer = manufacturer;
info.DumpingInfo.Model = model;
info.DumpingInfo.Firmware = firmware;
}
// Fill in the disc type data
if (GetDiscType(sidecar, out string discType, out string discSubType))
{
string fullDiscType = string.Empty;
if (!string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
fullDiscType = $"{discType} ({discSubType})";
else if (!string.IsNullOrWhiteSpace(discType) && string.IsNullOrWhiteSpace(discSubType))
fullDiscType = discType;
else if (string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
fullDiscType = discSubType;
info.DumpingInfo.ReportedDiscType = fullDiscType;
}
// Fill in the hash data
info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(sidecar, basePath);
switch (this.Type)
{
// TODO: Can this do GD-ROM?
case MediaType.CDROM:
// TODO: Can this do GD-ROM?
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
// TODO: Re-enable once PVD generation / finding is fixed
// Generate / obtain the PVD
//info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
long errorCount = -1;
if (File.Exists(basePath + ".resume.xml"))
@@ -233,6 +262,10 @@ namespace MPF.Modules.Aaru
info.SizeAndChecksums.SHA1 = sha1;
}
// TODO: Re-enable once PVD generation / finding is fixed
// Generate / obtain the PVD
//info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
// Deal with the layerbreak
string layerbreak = null;
if (this.Type == MediaType.DVD)
@@ -243,12 +276,11 @@ namespace MPF.Modules.Aaru
// If we have a single-layer disc
if (string.IsNullOrWhiteSpace(layerbreak))
{
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
// Currently no-op
}
// If we have a dual-layer disc
else
{
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
info.SizeAndChecksums.Layerbreak = Int64.Parse(layerbreak);
}
@@ -346,10 +378,12 @@ namespace MPF.Modules.Aaru
case RedumpSystem.SonyPlayStation4:
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? "";
break;
case RedumpSystem.SonyPlayStation5:
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? "";
break;
}
@@ -2858,6 +2892,37 @@ namespace MPF.Modules.Aaru
return obj;
}
/// <summary>
/// Get reported disc type information, if possible
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>True if disc type info was set, false otherwise</returns>
private static bool GetDiscType(CICMMetadataType cicmSidecar, out string discType, out string discSubType)
{
// Set the default values
discType = null; discSubType = null;
// If the object is null, we can't get information from it
if (cicmSidecar == null)
return false;
// Only care about OpticalDisc types
if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0)
return false;
// Find and return the hardware info, if possible
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
{
// Store the first instance of each value
if (string.IsNullOrEmpty(discType) && !string.IsNullOrEmpty(opticalDisc.DiscType))
discType = opticalDisc.DiscType;
if (string.IsNullOrEmpty(discSubType) && !string.IsNullOrEmpty(opticalDisc.DiscSubType))
discSubType = opticalDisc.DiscSubType;
}
return !string.IsNullOrEmpty(discType) || !string.IsNullOrEmpty(discSubType);
}
/// <summary>
/// Get the DVD protection information, if possible
/// </summary>
@@ -2951,6 +3016,50 @@ namespace MPF.Modules.Aaru
}
}
/// <summary>
/// Get hardware information, if possible
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <returns>True if hardware info was set, false otherwise</returns>
private static bool GetHardwareInfo(CICMMetadataType cicmSidecar, out string manufacturer, out string model, out string firmware)
{
// Set the default values
manufacturer = null; model = null; firmware = null;
// If the object is null, we can't get information from it
if (cicmSidecar == null)
return false;
// Only care about OpticalDisc types
if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0)
return false;
// Find and return the hardware info, if possible
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
{
// If there's no hardware information, skip
if (opticalDisc.DumpHardwareArray == null || !opticalDisc.DumpHardwareArray.Any())
continue;
foreach (DumpHardwareType hardware in opticalDisc.DumpHardwareArray)
{
// If the hardware information is invalid, skip
if (hardware == null)
continue;
// Store the first instance of each value
if (string.IsNullOrEmpty(manufacturer) && !string.IsNullOrEmpty(hardware.Manufacturer))
manufacturer = hardware.Manufacturer;
if (string.IsNullOrEmpty(model) && !string.IsNullOrEmpty(hardware.Model))
model = hardware.Model;
if (string.IsNullOrEmpty(firmware) && !string.IsNullOrEmpty(hardware.Firmware))
firmware = hardware.Firmware;
}
}
return !string.IsNullOrEmpty(manufacturer) || !string.IsNullOrEmpty(model) || !string.IsNullOrEmpty(firmware);
}
/// <summary>
/// Get the layerbreak from the input file, if possible
/// </summary>

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -153,10 +153,11 @@ namespace MPF.Modules
/// Generate a SubmissionInfo for the output files
/// </summary>
/// <param name="submissionInfo">Base submission info to fill in specifics for</param>
/// <param name="options">Options object representing user-defined options</param>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <param name="drive">Drive representing the disc to get information from</param>
/// <param name="includeArtifacts">True to include output files as encoded artifacts, false otherwise</param>
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, string basePath, Drive drive, bool includeArtifacts);
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, Options options, string basePath, Drive drive, bool includeArtifacts);
#endregion
@@ -931,6 +932,83 @@ namespace MPF.Modules
return string.Empty;
}
/// <summary>
/// Process a byte parameter
/// </summary>
/// <param name="parts">List of parts 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)
=> 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="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)
{
if (parts == null)
return null;
if (parts[i] == shortFlagString || parts[i] == longFlagString)
{
if (!IsFlagSupported(longFlagString))
{
return null;
}
else if (!DoesExist(parts, i + 1))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (IsFlagSupported(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
else if (!IsValidInt8(parts[i + 1]))
{
if (missingAllowed)
this[longFlagString] = true;
return null;
}
this[longFlagString] = true;
i++;
(string value, long factor) = ExtractFactorFromValue(parts[i]);
return (byte)(byte.Parse(value) * factor);
}
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
{
if (!IsFlagSupported(longFlagString))
return null;
string[] commandParts = parts[i].Split('=');
if (commandParts.Length != 2)
return null;
string valuePart = commandParts[1];
(string value, long factor) = ExtractFactorFromValue(valuePart);
return (byte)(byte.Parse(value) * factor);
}
return Byte.MinValue;
}
/// <summary>
/// Get yhe trimmed value and multiplication factor from a value
/// </summary>
@@ -1244,6 +1322,43 @@ namespace MPF.Modules
return null;
}
/// <summary>
/// Get the internal serial from a PlayStation 4 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Internal disc serial if possible, null on error</returns>
protected static string GetPlayStation4Serial(char? driveLetter)
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return null;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return null;
// If we can't find param.sfo, we don't have a PlayStation 4 disc
string paramSfoPath = Path.Combine(drivePath, "bd", "param.sfo");
if (!File.Exists(paramSfoPath))
return null;
// Let's try reading param.sfo to find the serial at the end of the file
try
{
using (BinaryReader br = new BinaryReader(File.OpenRead(paramSfoPath)))
{
br.BaseStream.Seek(-0x14, SeekOrigin.End);
return new string(br.ReadChars(9));
}
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the version from a PlayStation 4 disc, if possible
/// </summary>
@@ -1281,6 +1396,43 @@ namespace MPF.Modules
}
}
/// <summary>
/// Get the internal serial from a PlayStation 5 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Internal disc serial if possible, null on error</returns>
protected static string GetPlayStation5Serial(char? driveLetter)
{
// If there's no drive letter, we can't do this part
if (driveLetter == null)
return null;
// If the folder no longer exists, we can't do this part
string drivePath = driveLetter + ":\\";
if (!Directory.Exists(drivePath))
return null;
// If we can't find param.json, we don't have a PlayStation 5 disc
string paramJsonPath = Path.Combine(drivePath, "bd", "param.json");
if (!File.Exists(paramJsonPath))
return null;
// Let's try reading param.json to find the serial in the unencrypted JSON
try
{
using (BinaryReader br = new BinaryReader(File.OpenRead(paramJsonPath)))
{
br.BaseStream.Seek(0x82E, SeekOrigin.Begin);
return new string(br.ReadChars(9));
}
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the version from a PlayStation 5 disc, if possible
/// </summary>
@@ -1391,4 +1543,4 @@ namespace MPF.Modules
#endregion
}
}
}

View File

@@ -2,6 +2,7 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using RedumpLib.Data;
@@ -58,8 +59,10 @@ namespace MPF.Modules.CleanRip
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
{
// TODO: Determine if there's a CleanRip version anywhere
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.TracksAndWriteOffsets.ClrMameProData = GetCleanripDatfile(basePath + ".iso", basePath + "-dumpinfo.txt");
// Get the individual hash data, as per internal
@@ -84,10 +87,11 @@ namespace MPF.Modules.CleanRip
if (File.Exists(basePath + ".bca"))
info.Extras.BCA = GetBCA(basePath + ".bca");
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out string gcVersion))
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out string gcVersion, out string gcName))
{
info.CommonDiscInfo.Region = gcRegion ?? info.CommonDiscInfo.Region;
info.VersionAndEditions.Version = gcVersion ?? info.VersionAndEditions.Version;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = gcName ?? string.Empty;
}
break;
@@ -204,10 +208,11 @@ namespace MPF.Modules.CleanRip
/// <param name="dumpinfo">Path to discinfo file</param>
/// <param name="region">Output region, if possible</param>
/// <param name="version">Output internal version of the game</param>
/// <param name="name">Output internal name of the game</param>
/// <returns></returns>
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string version)
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string version, out string name)
{
region = null; version = null;
region = null; version = null; name = null;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dumpinfo))
@@ -227,11 +232,15 @@ namespace MPF.Modules.CleanRip
string line = sr.ReadLine().Trim();
if (line.StartsWith("Version"))
{
version = line.Substring(9);
version = line.Substring("Version: ".Length);
}
else if (line.StartsWith("Internal Name"))
{
name = line.Substring("Internal Name: ".Length);
}
else if (line.StartsWith("Filename"))
{
string serial = line.Substring(10);
string serial = line.Substring("Filename: ".Length);
// char gameType = serial[0];
// string gameid = serial[1] + serial[2];

View File

@@ -2,6 +2,7 @@
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using RedumpLib.Data;
@@ -75,11 +76,14 @@ namespace MPF.Modules.DD
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
{
// TODO: Fill in submission info specifics for DD
string outputDirectory = Path.GetDirectoryName(basePath);
// TODO: Determine if there's a DD version anywhere
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
switch (this.Type)
{
// Determine type-specific differences
@@ -124,10 +128,12 @@ namespace MPF.Modules.DD
case RedumpSystem.SonyPlayStation4:
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? "";
break;
case RedumpSystem.SonyPlayStation5:
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? "";
break;
}
}

View File

@@ -45,6 +45,7 @@ namespace MPF.Modules.DiscImageCreator
public const string CopyrightManagementInformation = "/c";
public const string D8Opcode = "/d8";
public const string DisableBeep = "/q";
public const string DVDReread = "/rr";
public const string ExtractMicroSoftCabFile = "/mscf";
public const string Fix = "/fix";
public const string ForceUnitAccess = "/f";
@@ -67,6 +68,7 @@ namespace MPF.Modules.DiscImageCreator
public const string SkipSector = "/sk";
public const string SubchannelReadLevel = "/s";
public const string UseAnchorVolumeDescriptorPointer = "/avdp";
public const string VerifyAudio = "/vrfy";
public const string VideoNow = "/vn";
public const string VideoNowColor = "/vnc";
public const string VideoNowXP = "/vnx";

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.CueSheets;
@@ -89,7 +90,7 @@ namespace MPF.Modules.DiscImageCreator
public string BEOpcodeValue { get; set; }
/// <summary>
/// C2 reread options for dumping
/// C2 reread options for dumping [CD only]
/// [0] - Reread value (default 4000)
/// [1] - 0 reread issue sector (default), 1 reread all
/// [2] - First LBA to reread (default 0)
@@ -97,6 +98,11 @@ namespace MPF.Modules.DiscImageCreator
/// </summary>
public int?[] C2OpcodeValue { get; set; } = new int?[4];
/// <summary>
/// C2 reread options for dumping [DVD/HD-DVD/BD only] (default 10)
/// </summary>
public int? DVDRereadValue { get; set; }
/// <summary>
/// End LBA for fixing
/// </summary>
@@ -148,6 +154,12 @@ namespace MPF.Modules.DiscImageCreator
/// </summary>
public int? SubchannelReadLevelValue { get; set; }
/// <summary>
/// Set verify audio level for the dump
/// Possible values: 0 no offset (default), 1 with offset, 2 without dumping
/// </summary>
public int? VerifyAudioValue { get; set; }
/// <summary>
/// Set number of empty bytes to insert at the head of first track for VideoNow (default 0)
/// </summary>
@@ -219,7 +231,7 @@ namespace MPF.Modules.DiscImageCreator
switch (this.Type)
{
case MediaType.CDROM:
case MediaType.GDROM: // TODO: Verify GD-ROM outputs this
case MediaType.GDROM:
if (!File.Exists($"{basePath}.cue"))
missingFiles.Add($"{basePath}.cue");
if (!File.Exists($"{basePath}.img") && !File.Exists($"{basePath}.imgtmp"))
@@ -234,8 +246,13 @@ namespace MPF.Modules.DiscImageCreator
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}.ccd"))
missingFiles.Add($"{basePath}.ccd");
// GD-ROM and GD-R don't output this for the HD area
if (this.Type != MediaType.GDROM)
{
if (!File.Exists($"{basePath}.ccd"))
missingFiles.Add($"{basePath}.ccd");
}
if (!File.Exists($"{basePath}.dat"))
missingFiles.Add($"{basePath}.dat");
if (!File.Exists($"{basePath}.sub") && !File.Exists($"{basePath}.subtmp"))
@@ -354,19 +371,35 @@ namespace MPF.Modules.DiscImageCreator
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
{
string outputDirectory = Path.GetDirectoryName(basePath);
// Get the dumping program and version
(_, string dicVersion) = GetCommandFilePathAndVersion(basePath);
info.DumpingInfo.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {dicVersion ?? "Unknown Version"}";
// Fill in the hardware data
if (GetHardwareInfo($"{basePath}_drive.txt", out string manufacturer, out string model, out string firmware))
{
info.DumpingInfo.Manufacturer = manufacturer;
info.DumpingInfo.Model = model;
info.DumpingInfo.Firmware = firmware;
}
// Fill in the disc type data
if (GetDiscType($"{basePath}_disc.txt", out string discTypeOrBookType))
info.DumpingInfo.ReportedDiscType = discTypeOrBookType;
// Fill in the hash data
info.TracksAndWriteOffsets.ClrMameProData = GetDatfile(basePath + ".dat");
info.TracksAndWriteOffsets.ClrMameProData = GetDatfile($"{basePath}.dat");
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.CDROM:
case MediaType.GDROM: // TODO: Verify GD-ROM outputs this
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? "Disc has no PVD"; ;
info.Extras.PVD = GetPVD($"{basePath}_mainInfo.txt") ?? "Disc has no PVD"; ;
// Audio-only discs will fail if there are any C2 errors, so they would never get here
if (this.System.IsAudio())
@@ -376,25 +409,30 @@ namespace MPF.Modules.DiscImageCreator
else
{
long errorCount = -1;
if (File.Exists(basePath + ".img_EdcEcc.txt"))
errorCount = GetErrorCount(basePath + ".img_EdcEcc.txt");
else if (File.Exists(basePath + ".img_EccEdc.txt"))
errorCount = GetErrorCount(basePath + ".img_EccEdc.txt");
if (File.Exists($"{basePath}.img_EdcEcc.txt"))
errorCount = GetErrorCount($"{basePath}.img_EdcEcc.txt");
else if (File.Exists($"{basePath}.img_EccEdc.txt"))
errorCount = GetErrorCount($"{basePath}.img_EccEdc.txt");
info.CommonDiscInfo.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
}
info.TracksAndWriteOffsets.Cuesheet = GetFullFile(basePath + ".cue") ?? "";
//var cueSheet = new CueSheet(basePath + ".cue"); // TODO: Do something with this
info.TracksAndWriteOffsets.Cuesheet = GetFullFile($"{basePath}.cue") ?? "";
//var cueSheet = new CueSheet($"{basePath}.cue"); // TODO: Do something with this
// Audio CDs "all have an offset of 0" and should not be included
if (System != RedumpSystem.AudioCD)
{
string cdWriteOffset = GetWriteOffset(basePath + "_disc.txt") ?? "";
string cdWriteOffset = GetWriteOffset($"{basePath}_disc.txt") ?? "";
info.CommonDiscInfo.RingWriteOffset = cdWriteOffset;
info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset;
}
// Attempt to get multisession data
string cdMultiSessionInfo = GetMultisessionInformation($"{basePath}_disc.txt");
if (!string.IsNullOrWhiteSpace(cdMultiSessionInfo))
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession] = cdMultiSessionInfo;
break;
case MediaType.DVD:
@@ -412,12 +450,15 @@ namespace MPF.Modules.DiscImageCreator
// Deal with the layerbreaks
if (this.Type == MediaType.DVD)
{
string layerbreak = GetLayerbreak(basePath + "_disc.txt", System.IsXGD()) ?? "";
info.SizeAndChecksums.Layerbreak = !string.IsNullOrEmpty(layerbreak) ? Int64.Parse(layerbreak) : default;
if (!options.EnableRedumpCompatibility || !System.IsXGD())
{
string layerbreak = GetLayerbreak($"{basePath}_disc.txt", System.IsXGD()) ?? "";
info.SizeAndChecksums.Layerbreak = !string.IsNullOrEmpty(layerbreak) ? Int64.Parse(layerbreak) : default;
}
}
else if (this.Type == MediaType.BluRay)
{
if (GetLayerbreak(Path.Combine(outputDirectory, "PIC.bin"), out long? layerbreak1, out long? layerbreak2, out long? layerbreak3))
if (GetLayerbreak($"{basePath}_PIC.bin", out long? layerbreak1, out long? layerbreak2, out long? layerbreak3))
{
if (layerbreak1 != null && layerbreak1 * 2048 < info.SizeAndChecksums.Size)
info.SizeAndChecksums.Layerbreak = layerbreak1.Value;
@@ -431,11 +472,12 @@ namespace MPF.Modules.DiscImageCreator
}
// Read the PVD
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? "";
if (!options.EnableRedumpCompatibility || System != RedumpSystem.MicrosoftXbox)
info.Extras.PVD = GetPVD($"{basePath}_mainInfo.txt") ?? "";
// Bluray-specific options
if (this.Type == MediaType.BluRay)
info.Extras.PIC = GetPIC(Path.Combine(outputDirectory, "PIC.bin")) ?? "";
info.Extras.PIC = GetPIC($"{basePath}_PIC.bin") ?? "";
break;
}
@@ -447,18 +489,19 @@ namespace MPF.Modules.DiscImageCreator
case RedumpSystem.EnhancedCD:
case RedumpSystem.IBMPCcompatible:
case RedumpSystem.RainbowDisc:
if (File.Exists(basePath + "_subIntention.txt"))
case RedumpSystem.SonyElectronicBook:
if (File.Exists($"{basePath}_subIntention.txt"))
{
FileInfo fi = new FileInfo(basePath + "_subIntention.txt");
FileInfo fi = new FileInfo($"{basePath}_subIntention.txt");
if (fi.Length > 0)
info.CopyProtection.SecuROMData = GetFullFile(basePath + "_subIntention.txt") ?? "";
info.CopyProtection.SecuROMData = GetFullFile($"{basePath}_subIntention.txt") ?? "";
}
break;
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
info.CopyProtection.Protection = GetDVDProtection(basePath + "_CSSKey.txt", basePath + "_disc.txt") ?? "";
info.CopyProtection.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt") ?? "";
break;
case RedumpSystem.KonamiPython2:
@@ -474,17 +517,18 @@ namespace MPF.Modules.DiscImageCreator
break;
case RedumpSystem.MicrosoftXbox:
string xgd1XMID = GetXGD1XMID(Path.Combine(outputDirectory, "DMI.bin"));
string xgd1XMID = GetXGD1XMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
XgdInfo xgd1Info = new XgdInfo(xgd1XMID);
if (xgd1Info?.Initialized == true)
{
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = xgd1Info.XMID;
info.CommonDiscInfo.Serial = xgd1Info.GetSerial() ?? "";
info.VersionAndEditions.Version = xgd1Info.GetVersion() ?? "";
if (!options.EnableRedumpCompatibility)
info.VersionAndEditions.Version = xgd1Info.GetVersion() ?? "";
info.CommonDiscInfo.Region = xgd1Info.InternalRegion;
}
if (GetXGDAuxInfo(basePath + "_disc.txt", out string xgd1DMIHash, out string xgd1PFIHash, out string xgd1SSHash, out string xgd1SS, out string xgd1SSVer))
if (GetXGDAuxInfo($"{basePath}_disc.txt", out string xgd1DMIHash, out string xgd1PFIHash, out string xgd1SSHash, out string xgd1SS, out string xgd1SSVer))
{
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd1DMIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash;
@@ -496,17 +540,18 @@ namespace MPF.Modules.DiscImageCreator
break;
case RedumpSystem.MicrosoftXbox360:
string xgd23XeMID = GetXGD23XeMID(Path.Combine(outputDirectory, "DMI.bin"));
string xgd23XeMID = GetXGD23XeMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
XgdInfo xgd23Info = new XgdInfo(xgd23XeMID);
if (xgd23Info?.Initialized == true)
{
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = xgd23Info.XMID;
info.CommonDiscInfo.Serial = xgd23Info.GetSerial() ?? "";
info.VersionAndEditions.Version = xgd23Info.GetVersion() ?? "";
if (!options.EnableRedumpCompatibility)
info.VersionAndEditions.Version = xgd23Info.GetVersion() ?? "";
info.CommonDiscInfo.Region = xgd23Info.InternalRegion;
}
if (GetXGDAuxInfo(basePath + "_disc.txt", out string xgd23DMIHash, out string xgd23PFIHash, out string xgd23SSHash, out string xgd23SS, out string xgd23SSVer))
if (GetXGDAuxInfo($"{basePath}_disc.txt", out string xgd23DMIHash, out string xgd23PFIHash, out string xgd23SSHash, out string xgd23SS, out string xgd23SSVer))
{
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd23DMIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash;
@@ -520,7 +565,7 @@ namespace MPF.Modules.DiscImageCreator
case RedumpSystem.NamcoSegaNintendoTriforce:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? "";
// Take only the first 16 lines for GD-ROM
if (!string.IsNullOrEmpty(info.Extras.Header))
@@ -538,7 +583,7 @@ namespace MPF.Modules.DiscImageCreator
break;
case RedumpSystem.SegaMegaCDSegaCD:
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? "";
// Take only the last 16 lines for Sega CD
if (!string.IsNullOrEmpty(info.Extras.Header))
@@ -556,7 +601,7 @@ namespace MPF.Modules.DiscImageCreator
case RedumpSystem.SegaChihiro:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? "";
// Take only the first 16 lines for GD-ROM
if (!string.IsNullOrEmpty(info.Extras.Header))
@@ -576,7 +621,7 @@ namespace MPF.Modules.DiscImageCreator
case RedumpSystem.SegaDreamcast:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? "";
// Take only the first 16 lines for GD-ROM
if (!string.IsNullOrEmpty(info.Extras.Header))
@@ -596,7 +641,7 @@ namespace MPF.Modules.DiscImageCreator
case RedumpSystem.SegaNaomi:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? "";
// Take only the first 16 lines for GD-ROM
if (!string.IsNullOrEmpty(info.Extras.Header))
@@ -616,7 +661,7 @@ namespace MPF.Modules.DiscImageCreator
case RedumpSystem.SegaNaomi2:
if (this.Type == MediaType.CDROM)
{
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? "";
// Take only the first 16 lines for GD-ROM
if (!string.IsNullOrEmpty(info.Extras.Header))
@@ -634,7 +679,7 @@ namespace MPF.Modules.DiscImageCreator
break;
case RedumpSystem.SegaSaturn:
info.Extras.Header = GetSegaHeader(basePath + "_mainInfo.txt") ?? "";
info.Extras.Header = GetSegaHeader($"{basePath}_mainInfo.txt") ?? "";
// Take only the first 16 lines for Saturn
if (!string.IsNullOrEmpty(info.Extras.Header))
@@ -660,10 +705,10 @@ namespace MPF.Modules.DiscImageCreator
}
bool? psEdcStatus = null;
if (File.Exists(basePath + ".img_EdcEcc.txt"))
psEdcStatus = GetPlayStationEDCStatus(basePath + ".img_EdcEcc.txt");
else if (File.Exists(basePath + ".img_EccEdc.txt"))
psEdcStatus = GetPlayStationEDCStatus(basePath + ".img_EccEdc.txt");
if (File.Exists($"{basePath}.img_EdcEcc.txt"))
psEdcStatus = GetPlayStationEDCStatus($"{basePath}.img_EdcEcc.txt");
else if (File.Exists($"{basePath}.img_EccEdc.txt"))
psEdcStatus = GetPlayStationEDCStatus($"{basePath}.img_EccEdc.txt");
if (psEdcStatus == true)
info.EDC.EDC = YesNo.Yes;
@@ -672,7 +717,7 @@ namespace MPF.Modules.DiscImageCreator
else
info.EDC.EDC = YesNo.NULL;
info.CopyProtection.AntiModchip = GetPlayStationAntiModchipDetected(basePath + "_disc.txt") ? YesNo.Yes : YesNo.No;
info.CopyProtection.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}_disc.txt") ? YesNo.Yes : YesNo.No;
break;
case RedumpSystem.SonyPlayStation2:
@@ -689,64 +734,68 @@ namespace MPF.Modules.DiscImageCreator
case RedumpSystem.SonyPlayStation4:
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? "";
break;
case RedumpSystem.SonyPlayStation5:
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? "";
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
//if (File.Exists(basePath + ".c2"))
// info.Artifacts["c2"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".c2"));
if (File.Exists(basePath + "_c2Error.txt"))
info.Artifacts["c2Error"] = GetBase64(GetFullFile(basePath + "_c2Error.txt"));
if (File.Exists(basePath + ".ccd"))
info.Artifacts["ccd"] = GetBase64(GetFullFile(basePath + ".ccd"));
if (File.Exists(basePath + "_cmd.txt")) // TODO: Figure out how to read in the timestamp-named file
info.Artifacts["cmd"] = GetBase64(GetFullFile(basePath + "_cmd.txt"));
if (File.Exists(basePath + "_CSSKey.txt"))
info.Artifacts["csskey"] = GetBase64(GetFullFile(basePath + "_CSSKey.txt"));
if (File.Exists(basePath + ".cue"))
info.Artifacts["cue"] = GetBase64(GetFullFile(basePath + ".cue"));
if (File.Exists(basePath + ".dat"))
info.Artifacts["dat"] = GetBase64(GetFullFile(basePath + ".dat"));
if (File.Exists(basePath + "_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt"));
//if (File.Exists(Path.Combine(outputDirectory, "DMI.bin")))
// info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, "DMI.bin")));
if (File.Exists(basePath + "_drive.txt"))
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt"));
if (File.Exists(basePath + "_img.cue"))
info.Artifacts["img_cue"] = GetBase64(GetFullFile(basePath + "_img.cue"));
if (File.Exists(basePath + ".img_EdcEcc.txt"))
info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile(basePath + ".img_EdcEcc.txt"));
if (File.Exists(basePath + ".img_EccEdc.txt"))
info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile(basePath + ".img_EccEdc.txt"));
if (File.Exists(basePath + "_mainError.txt"))
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt"));
if (File.Exists(basePath + "_mainInfo.txt"))
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt"));
//if (File.Exists(Path.Combine(outputDirectory, "PFI.bin")))
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, "PFI.bin")));
//if (File.Exists(Path.Combine(outputDirectory, "SS.bin")))
// info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, "SS.bin")));
if (File.Exists(basePath + ".sub"))
info.Artifacts["sub"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".sub"));
if (File.Exists(basePath + "_subError.txt"))
info.Artifacts["subError"] = GetBase64(GetFullFile(basePath + "_subError.txt"));
if (File.Exists(basePath + "_subInfo.txt"))
info.Artifacts["subInfo"] = GetBase64(GetFullFile(basePath + "_subInfo.txt"));
if (File.Exists(basePath + "_subIntention.txt"))
info.Artifacts["subIntention"] = GetBase64(GetFullFile(basePath + "_subIntention.txt"));
//if (File.Exists(basePath + "_sub.txt"))
// info.Artifacts["subReadable"] = GetBase64(GetFullFile(basePath + "_sub.txt"));
//if (File.Exists(basePath + "_subReadable.txt"))
// info.Artifacts["subReadable"] = GetBase64(GetFullFile(basePath + "_subReadable.txt"));
if (File.Exists(basePath + "_volDesc.txt"))
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt"));
//if (File.Exists($"{basePath}.c2"))
// info.Artifacts["c2"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.c2"));
if (File.Exists($"{basePath}_c2Error.txt"))
info.Artifacts["c2Error"] = GetBase64(GetFullFile($"{basePath}_c2Error.txt"));
if (File.Exists($"{basePath}.ccd"))
info.Artifacts["ccd"] = GetBase64(GetFullFile($"{basePath}.ccd"));
if (File.Exists($"{basePath}_cmd.txt")) // TODO: Figure out how to read in the timestamp-named file
info.Artifacts["cmd"] = GetBase64(GetFullFile($"{basePath}_cmd.txt"));
if (File.Exists($"{basePath}_CSSKey.txt"))
info.Artifacts["csskey"] = GetBase64(GetFullFile($"{basePath}_CSSKey.txt"));
if (File.Exists($"{basePath}.cue"))
info.Artifacts["cue"] = GetBase64(GetFullFile($"{basePath}.cue"));
if (File.Exists($"{basePath}.dat"))
info.Artifacts["dat"] = GetBase64(GetFullFile($"{basePath}.dat"));
if (File.Exists($"{basePath}_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile($"{basePath}_disc.txt"));
//if (File.Exists(Path.Combine(outputDirectory, $"{basePath}_DMI.bin")))
// info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, $"{basePath}_DMI.bin")));
if (File.Exists($"{basePath}_drive.txt"))
info.Artifacts["drive"] = GetBase64(GetFullFile($"{basePath}_drive.txt"));
if (File.Exists($"{basePath}_img.cue"))
info.Artifacts["img_cue"] = GetBase64(GetFullFile($"{basePath}_img.cue"));
if (File.Exists($"{basePath}.img_EdcEcc.txt"))
info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile($"{basePath}.img_EdcEcc.txt"));
if (File.Exists($"{basePath}.img_EccEdc.txt"))
info.Artifacts["img_EdcEcc"] = GetBase64(GetFullFile($"{basePath}.img_EccEdc.txt"));
if (File.Exists($"{basePath}_mainError.txt"))
info.Artifacts["mainError"] = GetBase64(GetFullFile($"{basePath}_mainError.txt"));
if (File.Exists($"{basePath}_mainInfo.txt"))
info.Artifacts["mainInfo"] = GetBase64(GetFullFile($"{basePath}_mainInfo.txt"));
//if (File.Exists($"{basePath}_PFI.bin"))
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin"));
//if (File.Exists($"{basePath}_PIC.bin"))
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin"));
//if (File.Exists($"{basePath}_SS.bin"))
// info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_SS.bin"));
if (File.Exists($"{basePath}.sub"))
info.Artifacts["sub"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.sub"));
if (File.Exists($"{basePath}_subError.txt"))
info.Artifacts["subError"] = GetBase64(GetFullFile($"{basePath}_subError.txt"));
if (File.Exists($"{basePath}_subInfo.txt"))
info.Artifacts["subInfo"] = GetBase64(GetFullFile($"{basePath}_subInfo.txt"));
if (File.Exists($"{basePath}_subIntention.txt"))
info.Artifacts["subIntention"] = GetBase64(GetFullFile($"{basePath}_subIntention.txt"));
//if (File.Exists($"{basePath}_sub.txt"))
// info.Artifacts["subReadable"] = GetBase64(GetFullFile($"{basePath}_sub.txt"));
//if (File.Exists($"{basePath}_subReadable.txt"))
// info.Artifacts["subReadable"] = GetBase64(GetFullFile($"{basePath}_subReadable.txt"));
if (File.Exists($"{basePath}_volDesc.txt"))
info.Artifacts["volDesc"] = GetBase64(GetFullFile($"{basePath}_volDesc.txt"));
}
}
@@ -960,6 +1009,17 @@ namespace MPF.Modules.DiscImageCreator
parameters.Add(FlagStrings.DisableBeep);
}
// DVD/HD-DVD/BD Reread
if (IsFlagSupported(FlagStrings.DVDReread))
{
if (this[FlagStrings.DVDReread] == true)
{
parameters.Add(FlagStrings.DVDReread);
if (DVDRereadValue != null)
parameters.Add(DVDRereadValue.ToString());
}
}
// Extract MicroSoftCabFile
if (IsFlagSupported(FlagStrings.ExtractMicroSoftCabFile))
{
@@ -1179,6 +1239,22 @@ namespace MPF.Modules.DiscImageCreator
parameters.Add(FlagStrings.UseAnchorVolumeDescriptorPointer);
}
// Verify Audio
if (IsFlagSupported(FlagStrings.VerifyAudio))
{
if (this[FlagStrings.VerifyAudio] == true)
{
parameters.Add(FlagStrings.VerifyAudio);
if (VerifyAudioValue != null)
{
if (VerifyAudioValue >= 0 && VerifyAudioValue <= 2)
parameters.Add(VerifyAudioValue.ToString());
else
return null;
}
}
}
// VideoNow
if (IsFlagSupported(FlagStrings.VideoNow))
{
@@ -1239,8 +1315,8 @@ namespace MPF.Modules.DiscImageCreator
[CommandStrings.BluRay] = new List<string>()
{
FlagStrings.DisableBeep,
FlagStrings.DVDReread,
FlagStrings.ForceUnitAccess,
FlagStrings.NoSkipSS,
FlagStrings.UseAnchorVolumeDescriptorPointer,
},
@@ -1271,6 +1347,7 @@ namespace MPF.Modules.DiscImageCreator
FlagStrings.ScanSectorProtect,
FlagStrings.SeventyFour,
FlagStrings.SubchannelReadLevel,
FlagStrings.VerifyAudio,
FlagStrings.VideoNow,
FlagStrings.VideoNowColor,
FlagStrings.VideoNowXP,
@@ -1299,9 +1376,9 @@ namespace MPF.Modules.DiscImageCreator
{
FlagStrings.CopyrightManagementInformation,
FlagStrings.DisableBeep,
FlagStrings.DVDReread,
FlagStrings.Fix,
FlagStrings.ForceUnitAccess,
FlagStrings.NoSkipSS,
FlagStrings.PadSector,
FlagStrings.Raw,
FlagStrings.Resume,
@@ -1354,7 +1431,6 @@ namespace MPF.Modules.DiscImageCreator
[CommandStrings.SACD] = new List<string>()
{
FlagStrings.DisableBeep,
FlagStrings.ForceUnitAccess,
},
[CommandStrings.Start] = new List<string>()
@@ -1400,9 +1476,9 @@ namespace MPF.Modules.DiscImageCreator
[CommandStrings.XBOX] = new List<string>()
{
FlagStrings.DisableBeep,
FlagStrings.DVDReread,
FlagStrings.ForceUnitAccess,
FlagStrings.NoSkipSS,
FlagStrings.UseAnchorVolumeDescriptorPointer,
},
[CommandStrings.XBOXSwap] = new List<string>()
@@ -1434,15 +1510,13 @@ namespace MPF.Modules.DiscImageCreator
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
string parentDirectory = Path.GetDirectoryName(basePath);
var currentFiles = Directory.GetFiles(parentDirectory);
string cmdPath = currentFiles.FirstOrDefault(f => Regex.IsMatch(f, @"\d{8}T\d{6}"));
(string cmdPath, _) = GetCommandFilePathAndVersion(basePath);
List<string> logFiles = new List<string>();
switch (this.Type)
{
case MediaType.CDROM:
case MediaType.GDROM: // TODO: Verify GD-ROM outputs this
case MediaType.GDROM:
if (File.Exists($"{basePath}.c2"))
logFiles.Add($"{basePath}.c2");
if (File.Exists($"{basePath}_c2Error.txt"))
@@ -1512,12 +1586,14 @@ namespace MPF.Modules.DiscImageCreator
if (File.Exists($"{basePath}_volDesc.txt"))
logFiles.Add($"{basePath}_volDesc.txt");
if (File.Exists(Path.Combine(parentDirectory, "DMI.bin")))
logFiles.Add(Path.Combine(parentDirectory, "DMI.bin"));
if (File.Exists(Path.Combine(parentDirectory, "PFI.bin")))
logFiles.Add(Path.Combine(parentDirectory, "PFI.bin"));
if (File.Exists(Path.Combine(parentDirectory, "SS.bin")))
logFiles.Add(Path.Combine(parentDirectory, "SS.bin"));
if (File.Exists($"{basePath}_DMI.bin"))
logFiles.Add($"{basePath}_DMI.bin");
if (File.Exists($"{basePath}_PFI.bin"))
logFiles.Add($"{basePath}_PFI.bin");
if (File.Exists($"{basePath}_PIC.bin"))
logFiles.Add($"{basePath}_PIC.bin");
if (File.Exists($"{basePath}_SS.bin"))
logFiles.Add($"{basePath}_SS.bin");
break;
@@ -1587,6 +1663,7 @@ namespace MPF.Modules.DiscImageCreator
AddOffsetValue = null;
BEOpcodeValue = null;
C2OpcodeValue = new int?[4];
DVDRereadValue = null;
FixValue = null;
ForceUnitAccessValue = null;
NoSkipSecuritySectorValue = null;
@@ -1627,11 +1704,28 @@ namespace MPF.Modules.DiscImageCreator
break;
}
// Set the DVD/HD-DVD/BD reread count
switch (options.DICDVDRereadCount)
{
case -1:
DVDRereadValue = null;
break;
case 0:
DVDRereadValue = 10;
break;
default:
DVDRereadValue = options.DICDVDRereadCount;
break;
}
// Now sort based on disc type
switch (this.Type)
{
case MediaType.CDROM:
this[FlagStrings.C2Opcode] = true;
this[FlagStrings.MultiSectorRead] = options.DICMultiSectorRead;
if (options.DICMultiSectorRead)
this.MultiSectorReadValue = options.DICMultiSectorReadValue;
switch (this.System)
{
@@ -1664,15 +1758,17 @@ namespace MPF.Modules.DiscImageCreator
case MediaType.DVD:
this[FlagStrings.CopyrightManagementInformation] = options.DICUseCMIFlag;
this[FlagStrings.ScanFileProtect] = options.DICParanoidMode;
this[FlagStrings.DVDReread] = true;
break;
case MediaType.GDROM:
this[FlagStrings.C2Opcode] = true;
break;
case MediaType.HDDVD:
this[FlagStrings.CopyrightManagementInformation] = options.DICUseCMIFlag;
this[FlagStrings.DVDReread] = true;
break;
case MediaType.BluRay:
// Currently no defaults set
this[FlagStrings.DVDReread] = true;
break;
// Special Formats
@@ -2176,6 +2272,11 @@ namespace MPF.Modules.DiscImageCreator
// Disable Beep
ProcessFlagParameter(parts, FlagStrings.DisableBeep, ref i);
// DVD/HD-DVD/BD Reread
intValue = ProcessInt32Parameter(parts, FlagStrings.DVDReread, ref i, missingAllowed: true);
if (intValue != null && intValue != Int32.MinValue)
DVDRereadValue = intValue;
// Extract MS-CAB
ProcessFlagParameter(parts, FlagStrings.ExtractMicroSoftCabFile, ref i);
@@ -2299,6 +2400,11 @@ namespace MPF.Modules.DiscImageCreator
// SeventyFour
ProcessFlagParameter(parts, FlagStrings.UseAnchorVolumeDescriptorPointer, ref i);
// VerifyAudio
intValue = ProcessInt32Parameter(parts, FlagStrings.VerifyAudio, ref i, missingAllowed: true);
if (intValue != null && intValue != Int32.MinValue && intValue >= 0 && intValue <= 2)
VerifyAudioValue = intValue;
// VideoNow
intValue = ProcessInt32Parameter(parts, FlagStrings.VideoNow, ref i, missingAllowed: true);
if (intValue != null && intValue != Int32.MinValue && intValue >= 0)
@@ -2319,6 +2425,34 @@ namespace MPF.Modules.DiscImageCreator
#region Private Extra Methods
/// <summary>
/// Get the command file path and extract the version from it
/// </summary>
/// <param name="basePath">Base filename and path to use for checking</param>
/// <returns>Tuple of file path and version as strings, both null on error</returns>
private static (string, string) GetCommandFilePathAndVersion(string basePath)
{
// If we have an invalid base path, we can do nothing
if (string.IsNullOrWhiteSpace(basePath))
return (null, null);
// Generate the matching regex based on the base path
string basePathFileName = Path.GetFileName(basePath);
Regex cmdFilenameRegex = new Regex(Regex.Escape(basePathFileName) + @"_(\d{8})T\d{6}\.txt");
// Find the first match for the command file
string parentDirectory = Path.GetDirectoryName(basePath);
var currentFiles = Directory.GetFiles(parentDirectory);
string commandPath = currentFiles.FirstOrDefault(f => cmdFilenameRegex.IsMatch(f));
if (string.IsNullOrWhiteSpace(commandPath))
return (null, null);
// Extract the version string
var match = cmdFilenameRegex.Match(commandPath);
string version = match.Groups[1].Value;
return (commandPath, version);
}
/// <summary>
/// Set the DIC command to be used for a given system and media type
/// </summary>
@@ -2429,6 +2563,77 @@ namespace MPF.Modules.DiscImageCreator
}
}
/// <summary>
/// Get reported disc type information, if possible
/// </summary>
/// <param name="drive">_disc.txt file location</param>
/// <returns>True if disc type info was set, false otherwise</returns>
private static bool GetDiscType(string drive, out string discTypeOrBookType)
{
// Set the default values
discTypeOrBookType = null;
// If the file doesn't exist, we can't get the info
if (!File.Exists(drive))
return false;
using (StreamReader sr = File.OpenText(drive))
{
try
{
string line = sr.ReadLine();
while (line != null)
{
// Trim the line for later use
line = line.Trim();
// Concatenate all found values for each possible line type
if (line.StartsWith("DiscType:"))
{
// DiscType: <discType>
if (string.IsNullOrEmpty(discTypeOrBookType))
discTypeOrBookType = line.Substring("DiscType: ".Length);
else
discTypeOrBookType += $", {line.Substring("DiscType: ".Length)}";
}
else if (line.StartsWith("DiscTypeIdentifier:"))
{
// DiscType: <discType>
if (string.IsNullOrEmpty(discTypeOrBookType))
discTypeOrBookType = line.Substring("DiscTypeIdentifier: ".Length);
else
discTypeOrBookType += $", {line.Substring("DiscTypeIdentifier: ".Length)}";
}
else if (line.StartsWith("DiscTypeSpecific:"))
{
// DiscType: <discType>
if (string.IsNullOrEmpty(discTypeOrBookType))
discTypeOrBookType = line.Substring("DiscTypeSpecific: ".Length);
else
discTypeOrBookType += $", {line.Substring("DiscTypeSpecific: ".Length)}";
}
else if (line.StartsWith("BookType:"))
{
// BookType: <discType>
if (string.IsNullOrEmpty(discTypeOrBookType))
discTypeOrBookType = line.Substring("BookType: ".Length);
else
discTypeOrBookType += $", {line.Substring("BookType: ".Length)}";
}
line = sr.ReadLine();
}
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
}
/// <summary>
/// Get the DVD protection information, if possible
/// </summary>
@@ -2608,10 +2813,9 @@ namespace MPF.Modules.DiscImageCreator
try
{
string[] header = segaHeader.Split('\n');
string serialLine = header[2].Substring(58);
string versionLine = header[4].Substring(58);
string dateLine = header[5].Substring(58);
serial = serialLine.Substring(0, 4);
serial = versionLine.Substring(0, 10).TrimEnd();
version = versionLine.Substring(10, 6).TrimStart('V', 'v');
date = dateLine.Substring(0, 8);
return true;
@@ -2623,6 +2827,60 @@ namespace MPF.Modules.DiscImageCreator
}
}
/// <summary>
/// Get hardware information from the input file, if possible
/// </summary>
/// <param name="drive">_drive.txt file location</param>
/// <returns>True if hardware info was set, false otherwise</returns>
private static bool GetHardwareInfo(string drive, out string manufacturer, out string model, out string firmware)
{
// Set the default values
manufacturer = null; model = null; firmware = null;
// If the file doesn't exist, we can't get the info
if (!File.Exists(drive))
return false;
using (StreamReader sr = File.OpenText(drive))
{
try
{
string line = sr.ReadLine();
while (line != null)
{
// Trim the line for later use
line = line.Trim();
// Only take the first instance of each value
if (string.IsNullOrEmpty(manufacturer) && line.StartsWith("VendorId"))
{
// VendorId: <manufacturer>
manufacturer = line.Substring("VendorId: ".Length);
}
else if (string.IsNullOrEmpty(model) && line.StartsWith("ProductId"))
{
// ProductId: <model>
model = line.Substring("ProductId: ".Length);
}
else if (string.IsNullOrEmpty(firmware) && line.StartsWith("ProductRevisionLevel"))
{
// ProductRevisionLevel: <firmware>
firmware = line.Substring("ProductRevisionLevel: ".Length);
}
line = sr.ReadLine();
}
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
}
/// <summary>
/// Get the layerbreak from the input file, if possible
/// </summary>
@@ -2655,14 +2913,16 @@ namespace MPF.Modules.DiscImageCreator
else if (xgd && line.StartsWith("LayerBreak"))
{
// LayerBreak: <size> (L0 Video: <size>, L0 Middle: <size>, L0 Game: <size>)
return line.Split(' ')[1];
string[] split = line.Split(' ').Where(s => !string.IsNullOrEmpty(s)).ToArray();
return split[1];
}
// Dual-layer discs have a regular layerbreak
else if (!xgd && line.StartsWith("LayerZeroSector"))
{
// LayerZeroSector: <size> (<hex>)
return line.Split(' ')[1];
string[] split = line.Split(' ').Where(s => !string.IsNullOrEmpty(s)).ToArray();
return split[1];
}
line = sr.ReadLine();
@@ -2735,6 +2995,123 @@ namespace MPF.Modules.DiscImageCreator
}
}
/// <summary>
/// Get multisession information from the input file, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <returns>Formatted multisession information, null on error</returns>
private static string GetMultisessionInformation(string disc)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(disc))
return null;
using (StreamReader sr = File.OpenText(disc))
{
try
{
// Seek to the TOC data
string line = sr.ReadLine();
if (!line.StartsWith("========== TOC"))
while (!(line = sr.ReadLine()).StartsWith("========== TOC")) ;
// Create the required regex
Regex trackLengthRegex = new Regex(@"^\s*.*?Track\s*([0-9]{1,2}), LBA\s*[0-9]{1,8} - \s*[0-9]{1,8}, Length\s*([0-9]{1,8})$");
// Read in the track length data
var trackLengthMapping = new Dictionary<string, string>();
while ((line = sr.ReadLine()).Contains("Track"))
{
var match = trackLengthRegex.Match(line);
trackLengthMapping[match.Groups[1].Value] = match.Groups[2].Value;
}
// Seek to the FULL TOC data
line = sr.ReadLine();
if (!line.StartsWith("========== FULL TOC"))
while (!(line = sr.ReadLine()).StartsWith("========== FULL TOC")) ;
// Create the required regex
Regex trackSessionRegex = new Regex(@"^\s*Session\s*([0-9]{1,2}),.*?,\s*Track\s*([0-9]{1,2}).*?$");
// Read in the track session data
var trackSessionMapping = new Dictionary<string, string>();
while (!(line = sr.ReadLine()).StartsWith("========== OpCode"))
{
var match = trackSessionRegex.Match(line);
if (!match.Success)
continue;
trackSessionMapping[match.Groups[2].Value] = match.Groups[1].Value;
}
// If we have all Session 1, we can just skip out
if (trackSessionMapping.All(kvp => kvp.Value == "1"))
return null;
// Seek to the multisession data
line = sr.ReadLine().Trim();
if (!line.StartsWith("Lead-out length"))
while (!(line = sr.ReadLine().Trim()).StartsWith("Lead-out length")) ;
// TODO: Are there any examples of 3+ session discs?
// Read the first session lead-out
string firstSessionLeadOutLengthString = line.Substring("Lead-out length of 1st session: ".Length);
line = sr.ReadLine().Trim();
// Read the second session lead-in, if it exists
string secondSessionLeadInLengthString = null;
while (line.StartsWith("Lead-in length"))
{
secondSessionLeadInLengthString = line.Substring("Lead-in length of 2nd session: ".Length);
line = sr.ReadLine().Trim();
}
// Read the second session pregap
string secondSessionPregapLengthString = line.Substring("Pregap length of 1st track of 2nd session: ".Length);
// Calculate the session gap total
if (!int.TryParse(firstSessionLeadOutLengthString, out int firstSessionLeadOutLength))
firstSessionLeadOutLength = 0;
if (!int.TryParse(secondSessionLeadInLengthString, out int secondSessionLeadInLength))
secondSessionLeadInLength = 0;
if (!int.TryParse(secondSessionPregapLengthString, out int secondSessionPregapLength))
secondSessionPregapLength = 0;
int sessionGapTotal = firstSessionLeadOutLength + secondSessionLeadInLength + secondSessionPregapLength;
// Calculate first session length and total length
int firstSessionLength = 0, totalLength = 0;
foreach (var lengthMapping in trackLengthMapping)
{
if (!int.TryParse(lengthMapping.Value, out int trackLength))
trackLength = 0;
if (trackSessionMapping.TryGetValue(lengthMapping.Key, out string session))
firstSessionLength += session == "1" ? trackLength : 0;
totalLength += trackLength;
}
// Adjust the session gap in a consistent way
if (firstSessionLength - sessionGapTotal < 0)
sessionGapTotal = firstSessionLeadOutLength + secondSessionLeadInLength;
// Create and return the formatted output
string multisessionData =
$"Session 1: 0-{firstSessionLength - sessionGapTotal - 1}\n"
+ $"Session 2: {firstSessionLength}-{totalLength - 1}";
return multisessionData;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
/// <summary>
/// Get the hex contents of the PIC file
/// </summary>
@@ -2760,10 +3137,10 @@ namespace MPF.Modules.DiscImageCreator
}
/// <summary>
/// Get the existance of an anti-modchip string from the input file, if possible
/// Get the existence of an anti-modchip string from the input file, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <returns>Anti-modchip existance if possible, false on error</returns>
/// <returns>Anti-modchip existence if possible, false on error</returns>
private static bool GetPlayStationAntiModchipDetected(string disc)
{
// If the file doesn't exist, we can't get info from it
@@ -2854,7 +3231,7 @@ namespace MPF.Modules.DiscImageCreator
/// Get the PVD from the input file, if possible
/// </summary>
/// <param name="mainInfo">_mainInfo.txt file location</param>
/// <returns>Newline-deliminated PVD if possible, null on error</returns>
/// <returns>Newline-delimited PVD if possible, null on error</returns>
private static string GetPVD(string mainInfo)
{
// If the file doesn't exist, we can't get info from it

View File

@@ -1,12 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
<TargetFrameworks>net48;net6.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<Copyright>Copyright (c)2019-2022</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.3</Version>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -20,27 +20,19 @@
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
<DefineConstants>NET_FRAMEWORK</DefineConstants>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.CueSheets\MPF.CueSheets.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
<ProjectReference Include="..\CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Memory" Version="4.5.4" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,50 @@
namespace MPF.Modules.Redumper
{
/// <summary>
/// Top-level commands for Redumper
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
public const string CD = "cd";
public const string Dump = "dump";
public const string Info = "info";
public const string Protection = "protection";
public const string Refine = "refine";
public const string Split = "split";
}
/// <summary>
/// Dumping flags for Redumper
/// </summary>
public static class FlagStrings
{
public const string AudioSilenceThreshold = "--audio-silence-threshold";
public const string CDiCorrectOffset = "--cdi-correct-offset";
public const string CDiReadyNormalize = "--cdi-ready-normalize";
public const string DescrambleNew = "--descramble-new";
public const string Drive = "--drive";
public const string ForceOffset = "--force-offset";
public const string ForceQTOC = "--force-qtoc";
public const string ForceSplit = "--force-split";
public const string ForceTOC = "--force-toc";
public const string HelpLong = "--help";
public const string HelpShort = "-h";
public const string ISO9660Trim = "--iso9660-trim";
public const string ImageName = "--image-name";
public const string ImagePath = "--image-path";
public const string LeaveUnchanged = "--leave-unchanged";
public const string Overwrite = "--overwrite";
public const string RefineSubchannel = "--refine-subchannel";
public const string Retries = "--retries";
public const string RingSize = "--ring-size";
public const string Skip = "--skip";
public const string SkipFill = "--skip-fill";
public const string SkipLeadIn = "--skip-leadin";
public const string SkipSize = "--skip-size";
public const string Speed = "--speed";
public const string StopLBA = "--stop-lba";
public const string Unsupported = "--unsupported";
public const string Verbose = "--verbose";
}
}

View File

@@ -0,0 +1,22 @@
using RedumpLib.Data;
namespace MPF.Modules.Redumper
{
public static class Converters
{
#region Cross-enumeration conversions
/// <summary>
/// Get the default extension for a given disc type
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>Valid extension (with leading '.'), null on error</returns>
public static string Extension(MediaType? type)
{
// TODO: Determine what extensions are used for each supported type
return ".bin";
}
#endregion
}
}

View File

@@ -0,0 +1,863 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using RedumpLib.Data;
namespace MPF.Modules.Redumper
{
/// <summary>
/// Represents a generic set of Redumper parameters
/// </summary>
/// <remarks>
/// TODO: Redumper natively supports multiple, chained commands but MPF
/// doesn't have an easy way of dealing with that right now. Maybe have
/// BaseCommand be a space-deliminated list of the commands?
/// TODO: With chained commands, how do we tell what parameters are
/// actually supported? Do we go one by one and if it doesn't match
/// any, then it's incorrect?
/// </remarks>
public class Parameters : BaseParameters
{
#region Generic Dumping Information
/// <inheritdoc/>
public override string InputPath => DriveValue;
/// <inheritdoc/>
public override string OutputPath => Path.Combine(ImagePathValue ?? string.Empty, ImageNameValue ?? string.Empty);
/// <inheritdoc/>
public override int? Speed => SpeedValue;
#endregion
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.Redumper;
#endregion
#region Flag Values
/// <summary>
/// Maximum absolute sample value to treat it as silence (default: 32)
/// </summary>
public int? AudioSilenceThresholdValue { get; set; }
/// <summary>
/// Drive to use, first available drive with disc, if not provided
/// </summary>
public string DriveValue { get; set; }
/// <summary>
/// Override offset autodetection and use supplied value
/// </summary>
public int? ForceOffsetValue { get; set; }
/// <summary>
/// Dump files prefix, autogenerated in dump mode, if not provided
/// </summary>
public string ImageNameValue { get; set; }
/// <summary>
/// Dump files base directory
/// </summary>
public string ImagePathValue { get; set; }
/// <summary>
/// Number of sector retries in case of SCSI/C2 error (default: 0)
/// </summary>
public int? RetriesValue { get; set; }
/// <summary>
/// Rings mode, maximum ring size to stop subdivision (rings, default: 1024)
/// </summary>
public int? RingSizeValue { get; set; }
/// <summary>
/// LBA ranges of sectors to skip
/// </summary>
public string SkipValue { get; set; }
/// <summary>
/// Fill byte value for skipped sectors (default: 0x55)
/// </summary>
public byte? SkipFillValue { get; set; }
/// <summary>
/// Rings mode, number of sectors to skip on SCSI error (default: 4096)
/// </summary>
public int? SkipSizeValue { get; set; }
/// <summary>
/// Drive read speed, optimal drive speed will be used if not provided
/// </summary>
public int? SpeedValue { get; set; }
/// <summary>
/// LBA to stop dumping at (everything before the value, useful for discs with fake TOC
/// </summary>
public int? StopLBAValue { get; set; }
#endregion
/// <inheritdoc/>
public Parameters(string parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
: base(system, type, driveLetter, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
// TODO: Fill out
return (true, new List<string>());
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
{
// TODO: Fill in submission info specifics for Redumper
string outputDirectory = Path.GetDirectoryName(basePath);
// TODO: Determine if there's a Redumper version anywhere
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
switch (this.Type)
{
// Determine type-specific differences
}
switch (this.System)
{
case RedumpSystem.KonamiPython2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
break;
case RedumpSystem.SonyPlayStation:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
}
break;
case RedumpSystem.SonyPlayStation2:
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate))
{
// Ensure internal serial is pulled from local data
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
}
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
break;
case RedumpSystem.SonyPlayStation4:
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? "";
break;
case RedumpSystem.SonyPlayStation5:
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? "";
break;
}
}
/// <inheritdoc/>
public override string GenerateParameters()
{
List<string> parameters = new List<string>();
if (BaseCommand == null)
BaseCommand = CommandStrings.NONE;
if (!string.IsNullOrWhiteSpace(BaseCommand))
parameters.Add(BaseCommand);
else
return null;
// Audio Silence Threshold
if (IsFlagSupported(FlagStrings.AudioSilenceThreshold))
{
if (this[FlagStrings.AudioSilenceThreshold] == true)
{
if (AudioSilenceThresholdValue != null && AudioSilenceThresholdValue >= 0)
parameters.Add($"{FlagStrings.AudioSilenceThreshold}={AudioSilenceThresholdValue}");
else
return null;
}
}
// CD-i Correct Offset
if (IsFlagSupported(FlagStrings.CDiCorrectOffset))
{
if (this[FlagStrings.CDiCorrectOffset] == true)
parameters.Add(FlagStrings.CDiCorrectOffset);
}
// CD-i Ready Normalize
if (IsFlagSupported(FlagStrings.CDiReadyNormalize))
{
if (this[FlagStrings.CDiReadyNormalize] == true)
parameters.Add(FlagStrings.CDiReadyNormalize);
}
// Descramble New
if (IsFlagSupported(FlagStrings.DescrambleNew))
{
if (this[FlagStrings.DescrambleNew] == true)
parameters.Add(FlagStrings.DescrambleNew);
}
// Drive
if (IsFlagSupported(FlagStrings.Drive))
{
if (this[FlagStrings.Drive] == true)
{
if (DriveValue != null)
parameters.Add($"{FlagStrings.Drive}={DriveValue}");
else
return null;
}
}
// ForceOffset
if (IsFlagSupported(FlagStrings.ForceOffset))
{
if (this[FlagStrings.ForceOffset] == true)
{
if (ForceOffsetValue != null)
parameters.Add($"{FlagStrings.ForceOffset}={ForceOffsetValue}");
else
return null;
}
}
// Force QTOC
if (IsFlagSupported(FlagStrings.ForceQTOC))
{
if (this[FlagStrings.ForceQTOC] == true)
parameters.Add(FlagStrings.ForceQTOC);
}
// Force Split
if (IsFlagSupported(FlagStrings.ForceSplit))
{
if (this[FlagStrings.ForceSplit] == true)
parameters.Add(FlagStrings.ForceSplit);
}
// Force TOC
if (IsFlagSupported(FlagStrings.ForceTOC))
{
if (this[FlagStrings.ForceTOC] == true)
parameters.Add(FlagStrings.ForceTOC);
}
// Help
if (IsFlagSupported(FlagStrings.HelpLong))
{
if (this[FlagStrings.HelpLong] == true)
parameters.Add(FlagStrings.HelpLong);
}
// ISO9660 Trim
if (IsFlagSupported(FlagStrings.ISO9660Trim))
{
if (this[FlagStrings.ISO9660Trim] == true)
parameters.Add(FlagStrings.ISO9660Trim);
}
// Image Name
if (IsFlagSupported(FlagStrings.ImageName))
{
if (this[FlagStrings.ImageName] == true)
{
if (!string.IsNullOrWhiteSpace(ImageNameValue))
parameters.Add($"{FlagStrings.ImageName}={ImageNameValue}");
else
return null;
}
}
// Image Path
if (IsFlagSupported(FlagStrings.ImagePath))
{
if (this[FlagStrings.ImagePath] == true)
{
if (!string.IsNullOrWhiteSpace(ImagePathValue))
parameters.Add($"{FlagStrings.ImagePath}={ImagePathValue}");
else
return null;
}
}
// Leave Unchanged
if (IsFlagSupported(FlagStrings.LeaveUnchanged))
{
if (this[FlagStrings.LeaveUnchanged] == true)
parameters.Add(FlagStrings.LeaveUnchanged);
}
// Overwrite
if (IsFlagSupported(FlagStrings.Overwrite))
{
if (this[FlagStrings.Overwrite] == true)
parameters.Add(FlagStrings.Overwrite);
}
// Refine Subchannel
if (IsFlagSupported(FlagStrings.RefineSubchannel))
{
if (this[FlagStrings.RefineSubchannel] == true)
parameters.Add(FlagStrings.RefineSubchannel);
}
// Retries
if (IsFlagSupported(FlagStrings.Retries))
{
if (this[FlagStrings.Retries] == true)
{
if (RetriesValue != null && RetriesValue >= 0)
parameters.Add($"{FlagStrings.Retries}={RetriesValue}");
else
return null;
}
}
// Ring Size
if (IsFlagSupported(FlagStrings.RingSize))
{
if (this[FlagStrings.RingSize] == true)
{
if (RingSizeValue != null && RingSizeValue >= 0)
parameters.Add($"{FlagStrings.RingSize}={RingSizeValue}");
else
return null;
}
}
// Skip
if (IsFlagSupported(FlagStrings.Skip))
{
if (this[FlagStrings.Skip] == true)
{
if (!string.IsNullOrWhiteSpace(SkipValue))
parameters.Add($"{FlagStrings.Skip}={SkipValue}");
else
return null;
}
}
// Skip Fill
if (IsFlagSupported(FlagStrings.SkipFill))
{
if (this[FlagStrings.SkipFill] == true)
{
if (SkipFillValue != null && SkipFillValue >= 0)
parameters.Add($"{FlagStrings.SkipFill}={SkipFillValue:x}");
else
return null;
}
}
// Skip Lead-In
if (IsFlagSupported(FlagStrings.SkipLeadIn))
{
if (this[FlagStrings.SkipLeadIn] == true)
parameters.Add(FlagStrings.SkipLeadIn);
}
// Skip Size
if (IsFlagSupported(FlagStrings.SkipSize))
{
if (this[FlagStrings.SkipSize] == true)
{
if (SkipSizeValue != null && SkipSizeValue >= 0)
parameters.Add($"{FlagStrings.SkipSize}={SkipSizeValue}");
else
return null;
}
}
// Speed
if (IsFlagSupported(FlagStrings.Speed))
{
if (this[FlagStrings.Speed] == true)
{
if (SpeedValue != null && SkipSizeValue >= 1)
parameters.Add($"{FlagStrings.Speed}={SpeedValue}");
else
return null;
}
}
// Stop LBA
if (IsFlagSupported(FlagStrings.StopLBA))
{
if (this[FlagStrings.StopLBA] == true)
{
if (StopLBAValue != null)
parameters.Add($"{FlagStrings.StopLBA}={StopLBAValue}");
else
return null;
}
}
// Unsupported
if (IsFlagSupported(FlagStrings.Unsupported))
{
if (this[FlagStrings.Unsupported] == true)
parameters.Add(FlagStrings.Unsupported);
}
// Verbose
if (IsFlagSupported(FlagStrings.Verbose))
{
if (this[FlagStrings.Verbose] == true)
parameters.Add(FlagStrings.Verbose);
}
return string.Join(" ", parameters);
}
/// <inheritdoc/>
public override Dictionary<string, List<string>> GetCommandSupport()
{
// TODO: Figure out actual support for each flag
return new Dictionary<string, List<string>>()
{
[CommandStrings.NONE] = new List<string>()
{
FlagStrings.HelpLong,
FlagStrings.HelpShort,
},
[CommandStrings.CD] = new List<string>()
{
FlagStrings.AudioSilenceThreshold,
FlagStrings.CDiCorrectOffset,
FlagStrings.CDiReadyNormalize,
FlagStrings.DescrambleNew,
FlagStrings.Drive,
FlagStrings.ForceOffset,
FlagStrings.ForceQTOC,
FlagStrings.ForceSplit,
FlagStrings.ForceTOC,
FlagStrings.ISO9660Trim,
FlagStrings.ImageName,
FlagStrings.ImagePath,
FlagStrings.LeaveUnchanged,
FlagStrings.Overwrite,
FlagStrings.RefineSubchannel,
FlagStrings.Retries,
FlagStrings.RingSize,
FlagStrings.Skip,
FlagStrings.SkipFill,
FlagStrings.SkipLeadIn,
FlagStrings.SkipSize,
FlagStrings.Speed,
FlagStrings.StopLBA,
FlagStrings.Unsupported,
FlagStrings.Verbose,
},
[CommandStrings.Dump] = new List<string>()
{
FlagStrings.AudioSilenceThreshold,
FlagStrings.CDiCorrectOffset,
FlagStrings.CDiReadyNormalize,
FlagStrings.DescrambleNew,
FlagStrings.Drive,
FlagStrings.ForceOffset,
FlagStrings.ForceQTOC,
FlagStrings.ForceSplit,
FlagStrings.ForceTOC,
FlagStrings.ISO9660Trim,
FlagStrings.ImageName,
FlagStrings.ImagePath,
FlagStrings.LeaveUnchanged,
FlagStrings.Overwrite,
FlagStrings.RefineSubchannel,
FlagStrings.Retries,
FlagStrings.RingSize,
FlagStrings.Skip,
FlagStrings.SkipFill,
FlagStrings.SkipLeadIn,
FlagStrings.SkipSize,
FlagStrings.Speed,
FlagStrings.StopLBA,
FlagStrings.Unsupported,
FlagStrings.Verbose,
},
[CommandStrings.Info] = new List<string>()
{
FlagStrings.AudioSilenceThreshold,
FlagStrings.CDiCorrectOffset,
FlagStrings.CDiReadyNormalize,
FlagStrings.DescrambleNew,
FlagStrings.Drive,
FlagStrings.ForceOffset,
FlagStrings.ForceQTOC,
FlagStrings.ForceSplit,
FlagStrings.ForceTOC,
FlagStrings.ISO9660Trim,
FlagStrings.ImageName,
FlagStrings.ImagePath,
FlagStrings.LeaveUnchanged,
FlagStrings.Overwrite,
FlagStrings.RefineSubchannel,
FlagStrings.Retries,
FlagStrings.RingSize,
FlagStrings.Skip,
FlagStrings.SkipFill,
FlagStrings.SkipLeadIn,
FlagStrings.SkipSize,
FlagStrings.Speed,
FlagStrings.StopLBA,
FlagStrings.Unsupported,
FlagStrings.Verbose,
},
[CommandStrings.Protection] = new List<string>()
{
FlagStrings.AudioSilenceThreshold,
FlagStrings.CDiCorrectOffset,
FlagStrings.CDiReadyNormalize,
FlagStrings.DescrambleNew,
FlagStrings.Drive,
FlagStrings.ForceOffset,
FlagStrings.ForceQTOC,
FlagStrings.ForceSplit,
FlagStrings.ForceTOC,
FlagStrings.ISO9660Trim,
FlagStrings.ImageName,
FlagStrings.ImagePath,
FlagStrings.LeaveUnchanged,
FlagStrings.Overwrite,
FlagStrings.RefineSubchannel,
FlagStrings.Retries,
FlagStrings.RingSize,
FlagStrings.Skip,
FlagStrings.SkipFill,
FlagStrings.SkipLeadIn,
FlagStrings.SkipSize,
FlagStrings.Speed,
FlagStrings.StopLBA,
FlagStrings.Unsupported,
FlagStrings.Verbose,
},
[CommandStrings.Refine] = new List<string>()
{
FlagStrings.AudioSilenceThreshold,
FlagStrings.CDiCorrectOffset,
FlagStrings.CDiReadyNormalize,
FlagStrings.DescrambleNew,
FlagStrings.Drive,
FlagStrings.ForceOffset,
FlagStrings.ForceQTOC,
FlagStrings.ForceSplit,
FlagStrings.ForceTOC,
FlagStrings.ISO9660Trim,
FlagStrings.ImageName,
FlagStrings.ImagePath,
FlagStrings.LeaveUnchanged,
FlagStrings.Overwrite,
FlagStrings.RefineSubchannel,
FlagStrings.Retries,
FlagStrings.RingSize,
FlagStrings.Skip,
FlagStrings.SkipFill,
FlagStrings.SkipLeadIn,
FlagStrings.SkipSize,
FlagStrings.Speed,
FlagStrings.StopLBA,
FlagStrings.Unsupported,
FlagStrings.Verbose,
},
[CommandStrings.Split] = new List<string>()
{
FlagStrings.AudioSilenceThreshold,
FlagStrings.CDiCorrectOffset,
FlagStrings.CDiReadyNormalize,
FlagStrings.DescrambleNew,
FlagStrings.Drive,
FlagStrings.ForceOffset,
FlagStrings.ForceQTOC,
FlagStrings.ForceSplit,
FlagStrings.ForceTOC,
FlagStrings.ISO9660Trim,
FlagStrings.ImageName,
FlagStrings.ImagePath,
FlagStrings.LeaveUnchanged,
FlagStrings.Overwrite,
FlagStrings.RefineSubchannel,
FlagStrings.Retries,
FlagStrings.RingSize,
FlagStrings.Skip,
FlagStrings.SkipFill,
FlagStrings.SkipLeadIn,
FlagStrings.SkipSize,
FlagStrings.Speed,
FlagStrings.StopLBA,
FlagStrings.Unsupported,
FlagStrings.Verbose,
},
};
}
/// <inheritdoc/>
public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
List<string> logFiles = new List<string>();
// TODO: Determine output logfiles for Redumper
return logFiles;
}
/// <inheritdoc/>
public override bool IsDumpingCommand()
{
switch (this.BaseCommand)
{
case CommandStrings.CD:
case CommandStrings.Dump:
return true;
default:
return false;
}
}
/// <inheritdoc/>
protected override void ResetValues()
{
BaseCommand = CommandStrings.NONE;
flags = new Dictionary<string, bool?>();
AudioSilenceThresholdValue = null;
DriveValue = null;
ForceOffsetValue = null;
ImageNameValue = null;
ImagePathValue = null;
RetriesValue = null;
RingSizeValue = null;
SkipValue = null;
SkipFillValue = null;
SkipSizeValue = null;
SpeedValue = null;
StopLBAValue = null;
}
/// <inheritdoc/>
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
{
// TODO: Fill out
}
/// <inheritdoc/>
protected override bool ValidateAndSetParameters(string parameters)
{
BaseCommand = CommandStrings.NONE;
// The string has to be valid by itself first
if (string.IsNullOrWhiteSpace(parameters))
return false;
// Now split the string into parts for easier validation
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
parameters = parameters.Trim();
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+")
.Cast<Match>()
.Select(m => m.Value)
.ToList();
// Determine what the commandline should look like given the first item
BaseCommand = parts[0];
int index = 0;
switch (BaseCommand)
{
// These two are technically CommandStrings.NONE
case FlagStrings.HelpLong:
case FlagStrings.HelpShort:
// Normal commands
case CommandStrings.CD:
case CommandStrings.Dump:
case CommandStrings.Info:
case CommandStrings.Protection:
case CommandStrings.Refine:
case CommandStrings.Split:
break;
default:
return false;
}
// Loop through all auxiliary flags, if necessary
for (int i = index; i < parts.Count; i++)
{
// Flag read-out values
byte? byteValue = null;
int? intValue = null;
string stringValue = null;
// Audio Silence Threshold
intValue = ProcessInt32Parameter(parts, FlagStrings.AudioSilenceThreshold, ref i);
if (intValue != null)
{
if (intValue >= 0)
AudioSilenceThresholdValue = intValue;
else
return false;
}
// CD-i Correct Offset
ProcessFlagParameter(parts, FlagStrings.CDiCorrectOffset, ref i);
// CD-i Ready Normalize
ProcessFlagParameter(parts, FlagStrings.CDiReadyNormalize, ref i);
// Descramble New
ProcessFlagParameter(parts, FlagStrings.DescrambleNew, ref i);
// Drive -- TODO: No drive is technically supported
stringValue = ProcessStringParameter(parts, FlagStrings.Drive, ref i);
if (!string.IsNullOrEmpty(stringValue))
DriveValue = stringValue;
// Force Offset
intValue = ProcessInt32Parameter(parts, FlagStrings.ForceOffset, ref i);
if (intValue != null)
ForceOffsetValue = intValue;
// Force QTOC
ProcessFlagParameter(parts, FlagStrings.ForceQTOC, ref i);
// Force Split
ProcessFlagParameter(parts, FlagStrings.ForceSplit, ref i);
// Force TOC
ProcessFlagParameter(parts, FlagStrings.ForceTOC, ref i);
// Help
ProcessFlagParameter(parts, FlagStrings.HelpShort, FlagStrings.HelpLong, ref i);
// ISO9660 Trim
ProcessFlagParameter(parts, FlagStrings.ISO9660Trim, ref i);
// Image Name -- TODO: Empty image name technically supported
stringValue = ProcessStringParameter(parts, FlagStrings.ImageName, ref i);
if (!string.IsNullOrEmpty(stringValue))
ImageNameValue = stringValue;
// Image Path -- TODO: Empty image path technically supported
stringValue = ProcessStringParameter(parts, FlagStrings.ImagePath, ref i);
if (!string.IsNullOrEmpty(stringValue))
ImagePathValue = stringValue;
// Leave Unchanged
ProcessFlagParameter(parts, FlagStrings.LeaveUnchanged, ref i);
// Overwrite
ProcessFlagParameter(parts, FlagStrings.Overwrite, ref i);
// Refine Subchannel
ProcessFlagParameter(parts, FlagStrings.RefineSubchannel, ref i);
// Retries
intValue = ProcessInt32Parameter(parts, FlagStrings.Retries, ref i);
if (intValue != null)
{
if (intValue >= 0)
RetriesValue = intValue;
else
return false;
}
// Ring Size
intValue = ProcessInt32Parameter(parts, FlagStrings.RingSize, ref i);
if (intValue != null)
{
if (intValue >= 0)
RetriesValue = intValue;
else
return false;
}
// Skip -- TODO: Validate how this value should look
stringValue = ProcessStringParameter(parts, FlagStrings.Skip, ref i);
if (!string.IsNullOrEmpty(stringValue))
SkipValue = stringValue;
// Skip Fill
byteValue = ProcessUInt8Parameter(parts, FlagStrings.RingSize, ref i);
if (byteValue != null)
SkipFillValue = byteValue;
// Skip Lead-In
ProcessFlagParameter(parts, FlagStrings.SkipLeadIn, ref i);
// Skip Size
intValue = ProcessInt32Parameter(parts, FlagStrings.SkipSize, ref i);
if (intValue != null)
{
if (intValue >= 0)
SkipSizeValue = intValue;
else
return false;
}
// Speed
intValue = ProcessInt32Parameter(parts, FlagStrings.Speed, ref i);
if (intValue != null)
{
if (intValue >= 1)
Speed = intValue;
else
return false;
}
// Stop LBA
intValue = ProcessInt32Parameter(parts, FlagStrings.StopLBA, ref i);
if (intValue != null)
StopLBAValue = intValue;
// Unsupported
ProcessFlagParameter(parts, FlagStrings.Unsupported, ref i);
// Verbose
ProcessFlagParameter(parts, FlagStrings.Verbose, ref i);
}
return true;
}
#endregion
}
}

View File

@@ -2,7 +2,9 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using RedumpLib.Data;
namespace MPF.Modules.UmdImageCreator
@@ -60,8 +62,11 @@ namespace MPF.Modules.UmdImageCreator
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
{
// TODO: Determine if there's a UMDImageCreator version anywhere
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
// Extract info based generically on MediaType
switch (this.Type)
{

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if NET_FRAMEWORK
#if NETFRAMEWORK
using IMAPI2;
#endif
using MPF.Core.Converters;
@@ -25,7 +25,7 @@ namespace MPF.Test.Core.Converters
DriveType.Removable,
};
#if NET_FRAMEWORK
#if NETFRAMEWORK
/// <summary>
/// IMAPI_MEDIA_PHYSICAL_TYPE values that map to MediaType
/// </summary>
@@ -71,7 +71,7 @@ namespace MPF.Test.Core.Converters
Assert.NotNull(actual);
}
#if NET_FRAMEWORK
#if NETFRAMEWORK
/// <summary>
/// Check that every supported IMAPI_MEDIA_PHYSICAL_TYPE maps to an MediaType
/// </summary>
@@ -108,7 +108,7 @@ namespace MPF.Test.Core.Converters
return testData;
}
#if NET_FRAMEWORK
#if NETFRAMEWORK
/// <summary>
/// Generate a test set of IMAPI_MEDIA_PHYSICAL_TYPE values
/// </summary>

View File

@@ -18,9 +18,11 @@ namespace MPF.Test.Library
public void ParametersValidTest(string parameters, char letter, bool isFloppy, MediaType? mediaType, bool expected)
{
var options = new Options() { InternalProgram = InternalProgram.DiscImageCreator };
// TODO: This relies on creating real objects for the drive. Can we mock this out instead?
var drive = isFloppy
? new Drive(InternalDriveType.Floppy, new DriveInfo(letter.ToString()))
: new Drive(InternalDriveType.Optical, new DriveInfo(letter.ToString()));
? Drive.Create(InternalDriveType.Floppy, letter.ToString())
: Drive.Create(InternalDriveType.Optical, letter.ToString());
var env = new DumpEnvironment(options, string.Empty, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, parameters);

View File

@@ -25,9 +25,9 @@ namespace MPF.Test.Library
[InlineData(MediaType.DVD, 12345, 1, 2, 3, "DVD-ROM-9")]
[InlineData(MediaType.BluRay, 0, 0, 0, 0, "BD-ROM-25")]
[InlineData(MediaType.BluRay, 12345, 0, 0, 0, "BD-ROM-25")]
[InlineData(MediaType.BluRay, 26_843_531_857, 0, 0, 0, "BD-ROM-33")]
//[InlineData(MediaType.BluRay, 26_843_531_857, 0, 0, 0, "BD-ROM-33")]
[InlineData(MediaType.BluRay, 12345, 1, 0, 0, "BD-ROM-50")]
[InlineData(MediaType.BluRay, 53_687_063_713, 1, 0, 0, "BD-ROM-66")]
//[InlineData(MediaType.BluRay, 53_687_063_713, 1, 0, 0, "BD-ROM-66")]
[InlineData(MediaType.BluRay, 12345, 1, 2, 0, "BD-ROM-100")]
[InlineData(MediaType.BluRay, 12345, 1, 2, 3, "BD-ROM-128")]
[InlineData(MediaType.UMD, 0, 0, 0, 0, "UMD-SL")]

View File

@@ -1,15 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0-windows</TargetFrameworks>
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
<DefineConstants>NET_FRAMEWORK</DefineConstants>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
<COMReference Include="IMAPI2">
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
@@ -39,18 +35,21 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="16.11.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="Microsoft.CodeCoverage" Version="17.3.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
<PackageReference Include="xunit.assert" Version="2.4.1" />
<PackageReference Include="xunit.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageReference Include="xunit.runner.console" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<PackageReference Include="xunit.analyzers" Version="1.0.0" />
<PackageReference Include="xunit.assert" Version="2.4.2" />
<PackageReference Include="xunit.core" Version="2.4.2" />
<PackageReference Include="xunit.extensibility.core" Version="2.4.2" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.2" />
<PackageReference Include="xunit.runner.console" Version="2.4.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -19,38 +19,122 @@ namespace MPF.Test.Modules
[InlineData(RedumpSystem.RawThrillsVarious, MediaType.GDROM, null)]
public void ParametersFromSystemAndTypeTest(RedumpSystem? knownSystem, MediaType? mediaType, string expected)
{
var options = new Options { };
var options = new Options();
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
Assert.Equal(expected, actual.BaseCommand);
}
[Theory]
[InlineData(RedumpSystem.AppleMacintosh, MediaType.LaserDisc, true, 20, null, null)]
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, false, 20, null, new string[] { FlagStrings.Raw })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, 20, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
/* paranoid mode tests */
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, 1000, 2, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
[InlineData(RedumpSystem.AppleMacintosh, MediaType.CDROM, false, 20, null, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
/* reread c2 */
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, false, 1000, null, new string[] { FlagStrings.C2Opcode })]
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, false, -1, null, new string[] { FlagStrings.C2Opcode })]
public void ParametersFromOptionsTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoid, int rereadC2, int? subchannelLevel, string[] expected)
[InlineData(RedumpSystem.AppleMacintosh, MediaType.LaserDisc, null)] // Deliberately unsupported
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, new string[] { FlagStrings.Raw })]
public void ParametersFromOptionsSpecialDefaultTest(RedumpSystem? knownSystem, MediaType? mediaType,string[] expected)
{
var options = new Options { DICParanoidMode = paranoid, DICRereadCount = rereadC2 };
var options = new Options();
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
HashSet<string> actualSet = new HashSet<string>(actual.Keys ?? new string[0]);
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
}
[Theory]
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, 1000, new string[] { FlagStrings.C2Opcode })]
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, -1, new string[] { FlagStrings.C2Opcode })]
public void ParametersFromOptionsC2RereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadC2, string[] expected)
{
var options = new Options { DICRereadCount = rereadC2 };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (rereadC2 == -1 || !knownSystem.MediaTypes().Contains(mediaType))
Assert.Null(actual.C2OpcodeValue[0]);
else
Assert.Equal(rereadC2, actual.C2OpcodeValue[0]);
Assert.Equal(subchannelLevel, actual.SubchannelReadLevelValue);
}
[Theory]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, 1000, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, -1, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.BluRay, 1000, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.BluRay, -1, new string[] { FlagStrings.DVDReread })]
public void ParametersFromOptionsDVDRereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadDVDBD, string[] expected)
{
var options = new Options { DICDVDRereadCount = rereadDVDBD };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (rereadDVDBD == -1 || !knownSystem.MediaTypes().Contains(mediaType))
Assert.Null(actual.DVDRereadValue);
else
Assert.Equal(rereadDVDBD, actual.DVDRereadValue);
}
[Theory]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, true, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, false, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.MultiSectorRead, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, false, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, new string[] { FlagStrings.DVDReread })]
public void ParametersFromOptionsMultiSectorReadTest(RedumpSystem? knownSystem, MediaType? mediaType, bool multiSectorRead, string[] expected)
{
var options = new Options { DICMultiSectorRead = multiSectorRead };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (expectedSet.Count != 1 && multiSectorRead)
Assert.Equal(0, actual.MultiSectorReadValue);
}
[Theory]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, true, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, false, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, false, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, new string[] { FlagStrings.DVDReread, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, new string[] { FlagStrings.DVDReread })]
public void ParametersFromOptionsParanoidModeTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoidMode, string[] expected)
{
var options = new Options { DICParanoidMode = paranoidMode };
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (paranoidMode)
{
if (actualSet.Contains(FlagStrings.ScanSectorProtect))
Assert.True(actual[FlagStrings.ScanSectorProtect]);
if (actualSet.Contains(FlagStrings.SubchannelReadLevel))
{
Assert.True(actual[FlagStrings.SubchannelReadLevel]);
Assert.Equal(2, actual.SubchannelReadLevelValue);
}
}
else
{
if (actualSet.Contains(FlagStrings.ScanSectorProtect))
Assert.False(actual[FlagStrings.ScanSectorProtect]);
if (actualSet.Contains(FlagStrings.SubchannelReadLevel))
Assert.False(actual[FlagStrings.SubchannelReadLevel]);
Assert.Null(actual.SubchannelReadLevelValue);
}
}
[Theory]
@@ -78,7 +162,7 @@ namespace MPF.Test.Modules
[InlineData(MediaType.NONE, null)]
public void MediaTypeToExtensionTest(MediaType? mediaType, string expected)
{
string actual = MPF.Modules.DiscImageCreator.Converters.Extension(mediaType);
string actual = Converters.Extension(mediaType);
Assert.Equal(expected, actual);
}
@@ -102,7 +186,7 @@ namespace MPF.Test.Modules
[InlineData(CommandStrings.XBOX, MediaType.DVD)]
public void BaseCommandToMediaTypeTest(string command, MediaType? expected)
{
MediaType? actual = MPF.Modules.DiscImageCreator.Converters.ToMediaType(command);
MediaType? actual = Converters.ToMediaType(command);
Assert.Equal(expected, actual);
}
@@ -126,7 +210,7 @@ namespace MPF.Test.Modules
[InlineData(CommandStrings.XBOX, RedumpSystem.MicrosoftXbox)]
public void BaseCommandToRedumpSystemTest(string command, RedumpSystem? expected)
{
RedumpSystem? actual = MPF.Modules.DiscImageCreator.Converters.ToRedumpSystem(command);
RedumpSystem? actual = Converters.ToRedumpSystem(command);
Assert.Equal(expected, actual);
}
@@ -161,5 +245,25 @@ namespace MPF.Test.Modules
Assert.NotNull(newParameters);
Assert.Equal(originalParameters, newParameters);
}
/// <summary>
/// Generate a HashSet of keys that are considered to be set
/// </summary>
/// <param name="parameters">Parameters object to get keys from</param>
/// <returns>HashSet representing the strings</returns>
private static HashSet<string> GenerateUsedKeys(Parameters parameters)
{
HashSet<string> usedKeys = new HashSet<string>();
if (parameters?.Keys == null)
return usedKeys;
foreach (string key in parameters.Keys)
{
if (parameters[key] == true)
usedKeys.Add(key);
}
return usedKeys;
}
}
}

View File

@@ -18,12 +18,17 @@ namespace MPF.Test.RedumpLib
private static readonly DiscType?[] _mappableDiscTypes = new DiscType?[]
{
DiscType.BD25,
//DiscType.BD33,
DiscType.BD50,
//DiscType.BD66,
DiscType.BD100,
DiscType.BD128,
DiscType.CD,
DiscType.DVD5,
DiscType.DVD9,
DiscType.GDROM,
DiscType.HDDVDSL,
DiscType.HDDVDDL,
DiscType.NintendoGameCubeGameDisc,
DiscType.NintendoWiiOpticalDiscSL,
DiscType.NintendoWiiOpticalDiscDL,
@@ -573,17 +578,6 @@ namespace MPF.Test.RedumpLib
#region System
/// <summary>
/// RedumpSystem values that are considered markers and not real systems
/// </summary>
private static readonly RedumpSystem?[] _markerSystemTypes = new RedumpSystem?[]
{
RedumpSystem.MarkerArcadeEnd,
RedumpSystem.MarkerComputerEnd,
RedumpSystem.MarkerDiscBasedConsoleEnd,
RedumpSystem.MarkerOtherEnd,
};
/// <summary>
/// Check that every RedumpSystem has a long name provided
/// </summary>
@@ -634,7 +628,7 @@ namespace MPF.Test.RedumpLib
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
{
// We want to skip all markers for this
if (_markerSystemTypes.Contains(redumpSystem))
if (redumpSystem.IsMarker())
continue;
testData.Add(new object[] { redumpSystem, false });

View File

@@ -42,7 +42,8 @@ namespace MPF.Test.RedumpLib
var submissionInfo = new SubmissionInfo()
{
SchemaVersion = 1,
MatchedIDs = new List<int> { 0, 1, 2, 3 },
FullyMatchedID = 3,
PartiallyMatchedIDs = new List<int> { 0, 1, 2, 3 },
Added = DateTime.UtcNow,
LastModified = DateTime.UtcNow,
@@ -132,7 +133,7 @@ namespace MPF.Test.RedumpLib
DumpersAndStatus = new DumpersAndStatusSection()
{
Status = DumpStatus.TwoOrMoHumanReadablesGreen,
Status = DumpStatus.TwoOrMoreGreen,
Dumpers = new string[] { "Dumper1", "Dumper2" },
OtherDumpers = "Dumper3",
},
@@ -156,6 +157,15 @@ namespace MPF.Test.RedumpLib
SHA1 = "SHA1",
},
DumpingInfo = new DumpingInfoSection()
{
DumpingProgram = "DiscImageCreator 20500101",
Manufacturer = "ATAPI",
Model = "Optical Drive",
Firmware = "1.23",
ReportedDiscType = "CD-R",
},
Artifacts = new Dictionary<string, string>()
{
["Sample Artifact"] = "Sample Data",

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using MPF.Core.Converters;
namespace MPF
namespace MPF.UI.Core.ComboBoxItems
{
/// <summary>
/// A generic combo box element

View File

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

View File

Before

Width:  |  Height:  |  Size: 423 KiB

After

Width:  |  Height:  |  Size: 423 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

View File

@@ -0,0 +1,61 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<Title>MPF.UI.Core</Title>
<AssemblyName>MPF.UI.Core</AssemblyName>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2022</Copyright>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
<PropertyGroup>
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Resource Include="Images\ring-code-guide-1-layer.png" />
<Resource Include="Images\ring-code-guide-2-layer.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationFramework.Aero" />
</ItemGroup>
<ItemGroup>
<Folder Include="Images\" />
</ItemGroup>
<ItemGroup>
<Page Update="UserControls\UserInput.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Windows\DiscInformationWindow.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Windows\RingCodeGuideWindow.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
</ItemGroup>
</Project>

View File

@@ -1,4 +1,4 @@
<UserControl x:Class="MPF.UserControls.UserInput"
<UserControl x:Class="MPF.UI.Core.UserControls.UserInput"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

View File

@@ -1,7 +1,7 @@
using System.Windows;
using System.Windows.Controls;
namespace MPF.UserControls
namespace MPF.UI.Core.UserControls
{
/// <summary>
/// Interaction logic for UserInput.xaml
@@ -106,7 +106,6 @@ namespace MPF.UserControls
#endregion
public UserInput()
{
// Set default values

View File

@@ -1,11 +1,13 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.Windows;
using MPF.UI.Core.ComboBoxItems;
using MPF.UI.Core.Windows;
using RedumpLib.Data;
namespace MPF.GUI.ViewModels
namespace MPF.UI.Core.ViewModels
{
public class DiscInformationViewModel
{
@@ -16,6 +18,11 @@ namespace MPF.GUI.ViewModels
/// </summary>
public DiscInformationWindow Parent { get; private set; }
/// <summary>
/// Application-level Options object
/// </summary>
public Options Options { get; private set; }
/// <summary>
/// SubmissionInfo object to fill and save
/// </summary>
@@ -35,11 +42,152 @@ namespace MPF.GUI.ViewModels
/// </summary>
public List<Element<Region>> Regions { get; private set; } = Element<Region>.GenerateElements().ToList();
/// <summary>
/// List of Redump-supported Regions
/// </summary>
private static readonly List<Region> RedumpRegions = new List<Region>
{
Region.Argentina,
Region.Asia,
Region.AsiaEurope,
Region.AsiaUSA,
Region.Australia,
Region.AustraliaGermany,
Region.AustraliaNewZealand,
Region.Austria,
Region.AustriaSwitzerland,
Region.Belarus,
Region.Belgium,
Region.BelgiumNetherlands,
Region.Brazil,
Region.Bulgaria,
Region.Canada,
Region.China,
Region.Croatia,
Region.Czechia,
Region.Denmark,
Region.Estonia,
Region.Europe,
Region.EuropeAsia,
Region.EuropeAustralia,
Region.EuropeCanada,
Region.EuropeGermany,
Region.Export,
Region.Finland,
Region.France,
Region.FranceSpain,
Region.Germany,
Region.GreaterChina,
Region.Greece,
Region.Hungary,
Region.Iceland,
Region.India,
Region.Ireland,
Region.Israel,
Region.Italy,
Region.Japan,
Region.JapanAsia,
Region.JapanEurope,
Region.JapanKorea,
Region.JapanUSA,
Region.SouthKorea,
Region.LatinAmerica,
Region.Lithuania,
Region.Netherlands,
Region.NewZealand,
Region.Norway,
Region.Poland,
Region.Portugal,
Region.Romania,
Region.RussianFederation,
Region.Scandinavia,
Region.Serbia,
Region.Singapore,
Region.Slovakia,
Region.SouthAfrica,
Region.Spain,
Region.SpainPortugal,
Region.Sweden,
Region.Switzerland,
Region.Taiwan,
Region.Thailand,
Region.Turkey,
Region.UnitedArabEmirates,
Region.UnitedKingdom,
Region.UKAustralia,
Region.Ukraine,
Region.UnitedStatesOfAmerica,
Region.USAAsia,
Region.USAAustralia,
Region.USABrazil,
Region.USACanada,
Region.USAEurope,
Region.USAGermany,
Region.USAJapan,
Region.USAKorea,
Region.World,
};
/// <summary>
/// List of available languages
/// </summary>
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
/// <summary>
/// List of Redump-supported Languages
/// </summary>
private static readonly List<Language> RedumpLanguages = new List<Language>
{
Language.Afrikaans,
Language.Albanian,
Language.Arabic,
Language.Armenian,
Language.Basque,
Language.Belarusian,
Language.Bulgarian,
Language.Catalan,
Language.Chinese,
Language.Croatian,
Language.Czech,
Language.Danish,
Language.Dutch,
Language.English,
Language.Estonian,
Language.Finnish,
Language.French,
Language.Gaelic,
Language.German,
Language.Greek,
Language.Hebrew,
Language.Hindi,
Language.Hungarian,
Language.Icelandic,
Language.Indonesian,
Language.Italian,
Language.Japanese,
Language.Korean,
Language.Latin,
Language.Latvian,
Language.Lithuanian,
Language.Macedonian,
Language.Norwegian,
Language.Polish,
Language.Portuguese,
Language.Panjabi,
Language.Romanian,
Language.Russian,
Language.Serbian,
Language.Slovak,
Language.Slovenian,
Language.Spanish,
Language.Swedish,
Language.Tamil,
Language.Thai,
Language.Turkish,
Language.Ukrainian,
Language.Vietnamese,
};
/// <summary>
/// List of available languages
/// </summary>
@@ -50,11 +198,19 @@ namespace MPF.GUI.ViewModels
/// <summary>
/// Constructor
/// </summary>
public DiscInformationViewModel(DiscInformationWindow parent, SubmissionInfo submissionInfo)
public DiscInformationViewModel(DiscInformationWindow parent, Options options, SubmissionInfo submissionInfo)
{
Parent = parent;
Options = options;
SubmissionInfo = submissionInfo.Clone() as SubmissionInfo ?? new SubmissionInfo();
// Limit lists, if necessary
if (this.Options.EnableRedumpCompatibility)
{
SetRedumpRegions();
SetRedumpLanguages();
}
// Add handlers
Parent.AcceptButton.Click += OnAcceptClick;
Parent.CancelButton.Click += OnCancelClick;
@@ -74,15 +230,19 @@ namespace MPF.GUI.ViewModels
{
// Additional Information
Parent.CommentsTextBox.Tab = true;
// Contents
Parent.GeneralContent.Tab = true;
Parent.ExtrasTextBox.Tab = true;
Parent.GameFootageTextBox.Tab = true;
Parent.GamesTextBox.Tab = true;
Parent.NetYarozeGamesTextBox.Tab = true;
Parent.PatchesTextBox.Tab = true;
Parent.PlayableDemosTextBox.Tab = true;
Parent.RollingDemosTextBox.Tab = true;
Parent.SavegamesTextBox.Tab = true;
Parent.TechDemosTextBox.Tab = true;
Parent.GameFootageTextBox.Tab = true;
Parent.VideosTextBox.Tab = true;
Parent.PatchesTextBox.Tab = true;
Parent.SavegamesTextBox.Tab = true;
Parent.ExtrasTextBox.Tab = true;
// L0
Parent.L0MasteringRing.Tab = true;
@@ -114,14 +274,22 @@ namespace MPF.GUI.ViewModels
/// </summary>
private void HideReadOnlyFields()
{
if (SubmissionInfo?.MatchedIDs == null)
Parent.MatchedIDs.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.FullyMatchedID == null)
Parent.FullyMatchedID.Visibility = Visibility.Collapsed;
else
Parent.MatchedIDs.Text = string.Join(", ", SubmissionInfo.MatchedIDs);
Parent.FullyMatchedID.Text = SubmissionInfo.FullyMatchedID.ToString();
if (SubmissionInfo?.PartiallyMatchedIDs == null)
Parent.PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
else
Parent.PartiallyMatchedIDs.Text = string.Join(", ", SubmissionInfo.PartiallyMatchedIDs);
if (SubmissionInfo?.CopyProtection?.AntiModchip == null)
Parent.AntiModchip.Visibility = Visibility.Collapsed;
else
Parent.AntiModchip.Text = SubmissionInfo.CopyProtection.AntiModchip.LongName();
if (SubmissionInfo?.TracksAndWriteOffsets?.OtherWriteOffsets == null)
Parent.DiscOffset.Visibility = Visibility.Collapsed;
else
Parent.DiscOffset.Text = SubmissionInfo.TracksAndWriteOffsets.OtherWriteOffsets;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.DMIHash) != true)
Parent.DMIHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CommonDiscInfo?.ErrorsCount))
@@ -132,8 +300,12 @@ namespace MPF.GUI.ViewModels
Parent.EDC.Visibility = Visibility.Collapsed;
else
Parent.EDC.Text = SubmissionInfo.EDC.EDC.LongName();
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Filename) != true)
Parent.Filename.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.Header))
Parent.Header.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.InternalName) != true)
Parent.InternalName.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.InternalSerialName) != true)
Parent.InternalSerialName.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CopyProtection?.LibCrypt == null)
@@ -146,10 +318,10 @@ namespace MPF.GUI.ViewModels
Parent.PFIHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.PIC))
Parent.PIC.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.Protection))
Parent.Protection.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.PVD))
Parent.PVD.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Multisession) != true)
Parent.Multisession.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.SecuROMData))
Parent.SecuROMData.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSHash) != true)
@@ -172,7 +344,7 @@ namespace MPF.GUI.ViewModels
private void ManipulateFields()
{
// Enable tabs in all fields, if required
if (App.Options.EnableTabsInInputFields)
if (this.Options.EnableTabsInInputFields)
EnableTabsInInputFields();
// Hide read-only fields that don't have values set
@@ -283,8 +455,14 @@ namespace MPF.GUI.ViewModels
// Read-Only Information
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DMIHash))
Parent.DMIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Filename))
Parent.Filename.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalName))
Parent.InternalName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalSerialName))
Parent.InternalSerialName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Multisession))
Parent.Multisession.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PFIHash))
Parent.PFIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSHash))
@@ -398,7 +576,10 @@ namespace MPF.GUI.ViewModels
// Read-Only Information
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = Parent.DMIHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename] = Parent.Filename.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = Parent.InternalName.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = Parent.InternalSerialName.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession] = Parent.Multisession.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = Parent.PFIHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = Parent.SSHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = Parent.SSVersion.Text;
@@ -431,6 +612,22 @@ namespace MPF.GUI.ViewModels
#endregion
}
/// <summary>
/// Repopulate the list of Languages based on Redump support
/// </summary>
private void SetRedumpLanguages()
{
this.Languages = RedumpLanguages.Select(l => new Element<Language>(l)).ToList();
}
/// <summary>
/// Repopulate the list of Regions based on Redump support
/// </summary>
private void SetRedumpRegions()
{
this.Regions = RedumpRegions.Select(r => new Element<Region>(r)).ToList();
}
/// <summary>
/// Update visible fields and sections based on the media type
/// </summary>
@@ -462,8 +659,13 @@ namespace MPF.GUI.ViewModels
case DiscType.DVD5:
case DiscType.DVD9:
case DiscType.HDDVDSL:
case DiscType.HDDVDDL:
case DiscType.BD25:
//case DiscType.BD33:
case DiscType.BD50:
//case DiscType.BD66:
case DiscType.BD100:
case DiscType.BD128:
case DiscType.NintendoGameCubeGameDisc:
case DiscType.NintendoWiiOpticalDiscSL:
case DiscType.NintendoWiiOpticalDiscDL:

View File

@@ -1,10 +1,10 @@
<windows:WindowBase x:Class="MPF.Windows.DiscInformationWindow"
<windows:WindowBase x:Class="MPF.UI.Core.Windows.DiscInformationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:MPF.UserControls"
xmlns:windows="clr-namespace:MPF.Windows"
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
xmlns:windows="clr-namespace:MPF.UI.Core.Windows"
mc:Ignorable="d"
Title="Disc Information" Width="515" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
@@ -165,6 +165,9 @@
Text="{Binding SubmissionInfo.CommonDiscInfo.Comments, Mode=TwoWay}" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top" />
<controls:UserInput x:Name="GenreTextBox" Label="Genre"/>
<controls:UserInput x:Name="ProtectionTextBox" Label="Protection" ToolTip="CAUTION: Only edit if you know what you are doing!"
Text="{Binding SubmissionInfo.CopyProtection.Protection, Mode=TwoWay}" TextHeight="75" TextWrapping="Wrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
@@ -335,18 +338,26 @@
<TabItem Header="Read-Only Info" Style="{DynamicResource CustomTabItemStyle}">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="MatchedIDs" Label="Matched ID(s)" IsReadOnly="True"/>
<controls:UserInput x:Name="FullyMatchedID" Label="Fully Matched ID" IsReadOnly="True"/>
<controls:UserInput x:Name="PartiallyMatchedIDs" Label="Partially Matched ID(s)" IsReadOnly="True"/>
<controls:UserInput x:Name="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"/>
<controls:UserInput x:Name="DiscOffset" Label="Disc Offset" IsReadOnly="True"/>
<controls:UserInput x:Name="DMIHash" Label="DMI Hash" IsReadOnly="True"/>
<controls:UserInput x:Name="EDC" Label="EDC" IsReadOnly="True"/>
<controls:UserInput x:Name="ErrorsCount" Label="Error(s) Count" IsReadOnly="True"
Text="{Binding SubmissionInfo.CommonDiscInfo.ErrorsCount, Mode=TwoWay}"/>
<controls:UserInput x:Name="EXEDateBuildDate" Label="EXE/Build Date" IsReadOnly="True"
Text="{Binding SubmissionInfo.CommonDiscInfo.EXEDateBuildDate, Mode=TwoWay}"/>
<controls:UserInput x:Name="Filename" Label="Filename" IsReadOnly="True"
TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="Header" Label="Header" IsReadOnly="True"
Text="{Binding SubmissionInfo.Extras.Header, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="InternalName" Label="Internal Names" IsReadOnly="True"/>
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"/>
<controls:UserInput x:Name="Multisession" Label="Multisession" IsReadOnly="True" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"/>
<controls:UserInput x:Name="LibCryptData" Label="LibCrypt Data" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.LibCryptData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
@@ -355,9 +366,6 @@
<controls:UserInput x:Name="PIC" Label="PIC" IsReadOnly="True"
Text="{Binding SubmissionInfo.Extras.PIC, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="Protection" Label="Protection" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.Protection, Mode=TwoWay}" TextHeight="75" TextWrapping="Wrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="PVD" Label="PVD" IsReadOnly="True"
Text="{Binding SubmissionInfo.Extras.PVD, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>

View File

@@ -1,7 +1,8 @@
using MPF.GUI.ViewModels;
using MPF.Core.Data;
using MPF.UI.Core.ViewModels;
using RedumpLib.Data;
namespace MPF.Windows
namespace MPF.UI.Core.Windows
{
/// <summary>
/// Interaction logic for DiscInformationWindow.xaml
@@ -16,10 +17,10 @@ namespace MPF.Windows
/// <summary>
/// Constructor
/// </summary>
public DiscInformationWindow(SubmissionInfo submissionInfo)
public DiscInformationWindow(Options options, SubmissionInfo submissionInfo)
{
InitializeComponent();
DataContext = new DiscInformationViewModel(this, submissionInfo);
DataContext = new DiscInformationViewModel(this, options, submissionInfo);
}
}
}

View File

@@ -1,11 +1,10 @@
<windows:WindowBase x:Class="MPF.Windows.RingCodeGuideWindow"
<windows:WindowBase x:Class="MPF.UI.Core.Windows.RingCodeGuideWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MPF"
xmlns:controls="clr-namespace:MPF.UserControls"
xmlns:windows="clr-namespace:MPF.Windows"
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
xmlns:windows="clr-namespace:MPF.UI.Core.Windows"
mc:Ignorable="d"
Title="Ring Code Guide" Width="500" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
@@ -81,12 +80,12 @@
<RowDefinition/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="/Images/ring-code-guide-1-layer.png"
<Image Grid.Row="0" Source="../Images/ring-code-guide-1-layer.png"
HorizontalAlignment="Center" Stretch="Fill" Width="500" Height="500"
RenderOptions.BitmapScalingMode="HighQuality" />
<Label Grid.Row="1">
<Label.Content>
<TextBlock><Bold Foreground="Red">1. Mastering Ring:</Bold> Sony DADC&lt;tab&gt;A0100368905-0101&lt;tab&gt;15</TextBlock>
<TextBlock><Bold Foreground="Red">1. Mastering Ring:</Bold> Sony DADC&lt;tab&gt;A0100368905-0101&lt;tab&gt;13</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="2">
@@ -122,7 +121,7 @@
<RowDefinition/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Source="/Images/ring-code-guide-2-layer.png"
<Image Grid.Row="0" Source="../Images/ring-code-guide-2-layer.png"
HorizontalAlignment="Center" Stretch="Fill" Width="475" Height="500"
RenderOptions.BitmapScalingMode="HighQuality" />
<Label Grid.Row="1">
@@ -132,7 +131,7 @@
</Label>
<Label Grid.Row="2">
<Label.Content>
<TextBlock><Bold Foreground="Green">2. Inner Mastering Ring:</Bold> IM01501A-L0&lt;tab&gt;03 +&lt;tab&gt;+</TextBlock>
<TextBlock><Bold Foreground="Green">2. Inner Mastering Ring:</Bold> IM01501A-L0&lt;tab&gt;01 +&lt;tab&gt;+</TextBlock>
</Label.Content>
</Label>
<Label Grid.Row="3">

View File

@@ -1,4 +1,4 @@
namespace MPF.Windows
namespace MPF.UI.Core.Windows
{
/// <summary>
/// Interaction logic for RingCodeGuideWindow.xaml

View File

@@ -1,7 +1,7 @@
using System.Windows;
using System.Windows.Input;
namespace MPF.Windows
namespace MPF.UI.Core.Windows
{
public class WindowBase : Window
{

159
MPF.sln
View File

@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28803.156
# Visual Studio Version 17
VisualStudioVersion = 17.1.32407.343
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF", "MPF\MPF.csproj", "{7B1B75EB-8940-466F-BD51-76471A57F9BE}"
EndProject
@@ -15,6 +15,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
ProjectSection(SolutionItems) = preProject
appveyor.yml = appveyor.yml
CHANGELIST.md = CHANGELIST.md
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
.github\ISSUE_TEMPLATE\informational.md = .github\ISSUE_TEMPLATE\informational.md
.github\ISSUE_TEMPLATE\issue-report.md = .github\ISSUE_TEMPLATE\issue-report.md
README.md = README.md
EndProjectSection
EndProject
@@ -22,12 +25,52 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RedumpLib", "RedumpLib\Redu
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.CueSheets", "MPF.CueSheets\MPF.CueSheets.csproj", "{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CICMMetadataEditor", "CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj", "{E4271454-6217-4500-BC36-F8856AC7AD6B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Core", "MPF.Core\MPF.Core.csproj", "{70B1265D-FE49-472A-A83D-0B462152D37A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Modules", "MPF.Modules\MPF.Modules.csproj", "{8A4254BD-552F-4238-B8EB-D59AACD768B9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.UI.Core", "MPF.UI.Core\MPF.UI.Core.csproj", "{EA3768DB-694A-4653-82E4-9FF71B8963F3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.CommonTypes", "Aaru\Aaru.CommonTypes\Aaru.CommonTypes.csproj", "{F2B84194-26EB-4227-B1C5-6602517E85AE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Devices", "Aaru\Aaru.Devices\Aaru.Devices.csproj", "{57BB2341-AB62-48FD-91B8-46F5A2F9ED51}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Console", "Aaru\Aaru.Console\Aaru.Console.csproj", "{CCAA7AFE-C094-4D82-A66D-630DE8A3F545}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Decoders", "Aaru\Aaru.Decoders\Aaru.Decoders.csproj", "{0BEB3088-B634-4289-AE17-CDF2D25D00D5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Checksums", "Aaru\Aaru.Checksums\Aaru.Checksums.csproj", "{CC48B324-A532-4A45-87A6-6F91F7141E8D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Helpers", "Aaru\Aaru.Helpers\Aaru.Helpers.csproj", "{F8BDF57B-1571-4CD0-84B3-B422088D359A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CICMMetadataEditor", "Aaru\CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj", "{7BCE5DC7-BA1C-4B59-9AA9-1AB5F81A703C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Core", "Aaru\Aaru.Core\Aaru.Core.csproj", "{679659B8-25D0-4279-B632-56EF8F94ADC0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Decryption", "Aaru\Aaru.Decryption\Aaru.Decryption.csproj", "{B38840FC-7A2F-4F68-9508-6DE313E04C6E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Archives", "Aaru\Aaru.Archives\Aaru.Archives.csproj", "{282271D0-CCC2-4ED7-BA38-EC06A84BB974}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Dto", "Aaru\Aaru.Dto\Aaru.Dto.csproj", "{F4399FF5-9BD0-475A-9EA7-3DAE45291FE2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Database", "Aaru\Aaru.Database\Aaru.Database.csproj", "{EE2CE4AA-1422-4A44-8B46-086E01D9AC08}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Filesystems", "Aaru\Aaru.Filesystems\Aaru.Filesystems.csproj", "{D7016DF2-5A5E-4524-B40D-BA2D59576688}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Filters", "Aaru\Aaru.Filters\Aaru.Filters.csproj", "{D571B8EF-903D-4353-BDD5-B834F9F029EF}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Images", "Aaru\Aaru.Images\Aaru.Images.csproj", "{74032CBC-339B-42F3-AF6F-E96C261F3E6A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Partitions", "Aaru\Aaru.Partitions\Aaru.Partitions.csproj", "{DA7AB65D-B5BA-4003-8893-A51BB071BA2F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Settings", "Aaru\Aaru.Settings\Aaru.Settings.csproj", "{5C4C7BAA-CF60-4233-84ED-39CB2312AF38}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aaru.Compression", "Aaru\Aaru.Compression\Aaru.Compression.csproj", "{858398D1-7321-4763-8BAB-56BBFEC74E29}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aaru", "Aaru", "{29ED0535-C038-4CDB-87D8-11F6F33D95CF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MPF", "MPF", "{4160167D-681D-480B-ABC6-06AC869E5769}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -58,10 +101,6 @@ Global
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Release|Any CPU.Build.0 = Release|Any CPU
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Release|Any CPU.Build.0 = Release|Any CPU
{70B1265D-FE49-472A-A83D-0B462152D37A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{70B1265D-FE49-472A-A83D-0B462152D37A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{70B1265D-FE49-472A-A83D-0B462152D37A}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -70,10 +109,114 @@ Global
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Release|Any CPU.Build.0 = Release|Any CPU
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Release|Any CPU.Build.0 = Release|Any CPU
{F2B84194-26EB-4227-B1C5-6602517E85AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F2B84194-26EB-4227-B1C5-6602517E85AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F2B84194-26EB-4227-B1C5-6602517E85AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F2B84194-26EB-4227-B1C5-6602517E85AE}.Release|Any CPU.Build.0 = Release|Any CPU
{57BB2341-AB62-48FD-91B8-46F5A2F9ED51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57BB2341-AB62-48FD-91B8-46F5A2F9ED51}.Debug|Any CPU.Build.0 = Debug|Any CPU
{57BB2341-AB62-48FD-91B8-46F5A2F9ED51}.Release|Any CPU.ActiveCfg = Release|Any CPU
{57BB2341-AB62-48FD-91B8-46F5A2F9ED51}.Release|Any CPU.Build.0 = Release|Any CPU
{CCAA7AFE-C094-4D82-A66D-630DE8A3F545}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCAA7AFE-C094-4D82-A66D-630DE8A3F545}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCAA7AFE-C094-4D82-A66D-630DE8A3F545}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCAA7AFE-C094-4D82-A66D-630DE8A3F545}.Release|Any CPU.Build.0 = Release|Any CPU
{0BEB3088-B634-4289-AE17-CDF2D25D00D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0BEB3088-B634-4289-AE17-CDF2D25D00D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0BEB3088-B634-4289-AE17-CDF2D25D00D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0BEB3088-B634-4289-AE17-CDF2D25D00D5}.Release|Any CPU.Build.0 = Release|Any CPU
{CC48B324-A532-4A45-87A6-6F91F7141E8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC48B324-A532-4A45-87A6-6F91F7141E8D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC48B324-A532-4A45-87A6-6F91F7141E8D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC48B324-A532-4A45-87A6-6F91F7141E8D}.Release|Any CPU.Build.0 = Release|Any CPU
{F8BDF57B-1571-4CD0-84B3-B422088D359A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8BDF57B-1571-4CD0-84B3-B422088D359A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8BDF57B-1571-4CD0-84B3-B422088D359A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F8BDF57B-1571-4CD0-84B3-B422088D359A}.Release|Any CPU.Build.0 = Release|Any CPU
{7BCE5DC7-BA1C-4B59-9AA9-1AB5F81A703C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7BCE5DC7-BA1C-4B59-9AA9-1AB5F81A703C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7BCE5DC7-BA1C-4B59-9AA9-1AB5F81A703C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7BCE5DC7-BA1C-4B59-9AA9-1AB5F81A703C}.Release|Any CPU.Build.0 = Release|Any CPU
{679659B8-25D0-4279-B632-56EF8F94ADC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{679659B8-25D0-4279-B632-56EF8F94ADC0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{679659B8-25D0-4279-B632-56EF8F94ADC0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{679659B8-25D0-4279-B632-56EF8F94ADC0}.Release|Any CPU.Build.0 = Release|Any CPU
{B38840FC-7A2F-4F68-9508-6DE313E04C6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B38840FC-7A2F-4F68-9508-6DE313E04C6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B38840FC-7A2F-4F68-9508-6DE313E04C6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B38840FC-7A2F-4F68-9508-6DE313E04C6E}.Release|Any CPU.Build.0 = Release|Any CPU
{282271D0-CCC2-4ED7-BA38-EC06A84BB974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{282271D0-CCC2-4ED7-BA38-EC06A84BB974}.Debug|Any CPU.Build.0 = Debug|Any CPU
{282271D0-CCC2-4ED7-BA38-EC06A84BB974}.Release|Any CPU.ActiveCfg = Release|Any CPU
{282271D0-CCC2-4ED7-BA38-EC06A84BB974}.Release|Any CPU.Build.0 = Release|Any CPU
{F4399FF5-9BD0-475A-9EA7-3DAE45291FE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F4399FF5-9BD0-475A-9EA7-3DAE45291FE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4399FF5-9BD0-475A-9EA7-3DAE45291FE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F4399FF5-9BD0-475A-9EA7-3DAE45291FE2}.Release|Any CPU.Build.0 = Release|Any CPU
{EE2CE4AA-1422-4A44-8B46-086E01D9AC08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE2CE4AA-1422-4A44-8B46-086E01D9AC08}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE2CE4AA-1422-4A44-8B46-086E01D9AC08}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EE2CE4AA-1422-4A44-8B46-086E01D9AC08}.Release|Any CPU.Build.0 = Release|Any CPU
{D7016DF2-5A5E-4524-B40D-BA2D59576688}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D7016DF2-5A5E-4524-B40D-BA2D59576688}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D7016DF2-5A5E-4524-B40D-BA2D59576688}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D7016DF2-5A5E-4524-B40D-BA2D59576688}.Release|Any CPU.Build.0 = Release|Any CPU
{D571B8EF-903D-4353-BDD5-B834F9F029EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D571B8EF-903D-4353-BDD5-B834F9F029EF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D571B8EF-903D-4353-BDD5-B834F9F029EF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D571B8EF-903D-4353-BDD5-B834F9F029EF}.Release|Any CPU.Build.0 = Release|Any CPU
{74032CBC-339B-42F3-AF6F-E96C261F3E6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{74032CBC-339B-42F3-AF6F-E96C261F3E6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{74032CBC-339B-42F3-AF6F-E96C261F3E6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{74032CBC-339B-42F3-AF6F-E96C261F3E6A}.Release|Any CPU.Build.0 = Release|Any CPU
{DA7AB65D-B5BA-4003-8893-A51BB071BA2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA7AB65D-B5BA-4003-8893-A51BB071BA2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA7AB65D-B5BA-4003-8893-A51BB071BA2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA7AB65D-B5BA-4003-8893-A51BB071BA2F}.Release|Any CPU.Build.0 = Release|Any CPU
{5C4C7BAA-CF60-4233-84ED-39CB2312AF38}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5C4C7BAA-CF60-4233-84ED-39CB2312AF38}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5C4C7BAA-CF60-4233-84ED-39CB2312AF38}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5C4C7BAA-CF60-4233-84ED-39CB2312AF38}.Release|Any CPU.Build.0 = Release|Any CPU
{858398D1-7321-4763-8BAB-56BBFEC74E29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{858398D1-7321-4763-8BAB-56BBFEC74E29}.Debug|Any CPU.Build.0 = Debug|Any CPU
{858398D1-7321-4763-8BAB-56BBFEC74E29}.Release|Any CPU.ActiveCfg = Release|Any CPU
{858398D1-7321-4763-8BAB-56BBFEC74E29}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7B1B75EB-8940-466F-BD51-76471A57F9BE} = {4160167D-681D-480B-ABC6-06AC869E5769}
{7CC064D2-38AB-4A05-8519-28660DE4562A} = {4160167D-681D-480B-ABC6-06AC869E5769}
{51AB0928-13F9-44BF-A407-B6957A43A056} = {4160167D-681D-480B-ABC6-06AC869E5769}
{8CFDE289-E171-4D49-A40D-5293265C1253} = {4160167D-681D-480B-ABC6-06AC869E5769}
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1} = {4160167D-681D-480B-ABC6-06AC869E5769}
{70B1265D-FE49-472A-A83D-0B462152D37A} = {4160167D-681D-480B-ABC6-06AC869E5769}
{8A4254BD-552F-4238-B8EB-D59AACD768B9} = {4160167D-681D-480B-ABC6-06AC869E5769}
{EA3768DB-694A-4653-82E4-9FF71B8963F3} = {4160167D-681D-480B-ABC6-06AC869E5769}
{F2B84194-26EB-4227-B1C5-6602517E85AE} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{57BB2341-AB62-48FD-91B8-46F5A2F9ED51} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{CCAA7AFE-C094-4D82-A66D-630DE8A3F545} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{0BEB3088-B634-4289-AE17-CDF2D25D00D5} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{CC48B324-A532-4A45-87A6-6F91F7141E8D} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{F8BDF57B-1571-4CD0-84B3-B422088D359A} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{7BCE5DC7-BA1C-4B59-9AA9-1AB5F81A703C} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{679659B8-25D0-4279-B632-56EF8F94ADC0} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{B38840FC-7A2F-4F68-9508-6DE313E04C6E} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{282271D0-CCC2-4ED7-BA38-EC06A84BB974} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{F4399FF5-9BD0-475A-9EA7-3DAE45291FE2} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{EE2CE4AA-1422-4A44-8B46-086E01D9AC08} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{D7016DF2-5A5E-4524-B40D-BA2D59576688} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{D571B8EF-903D-4353-BDD5-B834F9F029EF} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{74032CBC-339B-42F3-AF6F-E96C261F3E6A} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{DA7AB65D-B5BA-4003-8893-A51BB071BA2F} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{5C4C7BAA-CF60-4233-84ED-39CB2312AF38} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
{858398D1-7321-4763-8BAB-56BBFEC74E29} = {29ED0535-C038-4CDB-87D8-11F6F33D95CF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {73C62E6A-6584-4D93-83B5-ECB1FBDB469B}
EndGlobalSection

View File

@@ -1,6 +1,7 @@
using System.Windows;
using MPF.Core.Data;
using MPF.GUI.ViewModels;
using MPF.Core.Utilities;
using MPF.UI.ViewModels;
using MPF.Windows;
namespace MPF

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Core.Converters;
using MPF.Core.Utilities;
using MPF.UI.Core.ComboBoxItems;
using RedumpLib.Data;
namespace MPF

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0-windows</TargetFrameworks>
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms>
@@ -11,9 +11,9 @@
<AssemblyName>MPF</AssemblyName>
<Description>Frontend for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2021</Copyright>
<Copyright>Copyright (c)2019-2022</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.3</Version>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
@@ -27,11 +27,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.0" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.3.4" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0-rc.1.21451.13" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@@ -41,22 +41,14 @@
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<ItemGroup>
<None Remove="Images\ring-code-guide-2-layer.png" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Icon.ico" />
<Resource Include="Images\ring-code-guide-2-layer.png" />
<Resource Include="Images\ring-code-guide-1-layer.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj">
<Project>{51ab0928-13f9-44bf-a407-b6957a43a056}</Project>
<Name>MPF.Library</Name>
</ProjectReference>
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
<ProjectReference Include="..\MPF.UI.Core\MPF.UI.Core.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>

View File

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

View File

@@ -1,5 +1,5 @@
using System.Windows.Controls;
using MPF.GUI.ViewModels;
using MPF.UI.ViewModels;
namespace MPF.UserControls
{

View File

@@ -10,7 +10,7 @@ using System.Windows.Media;
using MPF.Core.Data;
using MPF.UserControls;
namespace MPF.GUI.ViewModels
namespace MPF.UI.ViewModels
{
public class LogViewModel
{

View File

@@ -1,20 +1,22 @@
using BurnOutSharp;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.Library;
using MPF.Windows;
using RedumpLib.Data;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using BurnOutSharp;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.Library;
using MPF.UI.Core.ComboBoxItems;
using MPF.Windows;
using MPF.UI.Core.Windows;
using RedumpLib.Data;
using WPFCustomMessageBox;
using WinForms = System.Windows.Forms;
namespace MPF.GUI.ViewModels
namespace MPF.UI.ViewModels
{
public class MainViewModel
{
@@ -76,6 +78,7 @@ namespace MPF.GUI.ViewModels
// Disable buttons until we load fully
App.Instance.StartStopButton.IsEnabled = false;
App.Instance.MediaScanButton.IsEnabled = false;
App.Instance.UpdateVolumeLabel.IsEnabled = false;
App.Instance.CopyProtectScanButton.IsEnabled = false;
// Add the click handlers to the UI
@@ -105,6 +108,10 @@ namespace MPF.GUI.ViewModels
// Always enable the media scan
App.Instance.MediaScanButton.IsEnabled = true;
App.Instance.UpdateVolumeLabel.IsEnabled = true;
// If we have a selected drive, keep track of it
char? lastSelectedDrive = (App.Instance.DriveLetterComboBox.SelectedValue as Drive)?.Letter;
// Populate the list of drives and add it to the combo box
Drives = Drive.CreateListOfDrives(App.Options.IgnoreFixedDrives);
@@ -114,20 +121,26 @@ namespace MPF.GUI.ViewModels
{
App.Logger.VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Letter))}");
// Check for active optical drives first
int index = Drives.FindIndex(d => d.MarkedActive && d.InternalDriveType == InternalDriveType.Optical);
// Check for the last selected drive, if possible
int index = -1;
if (lastSelectedDrive != null)
index = Drives.FindIndex(d => d.MarkedActive && d.Letter == lastSelectedDrive);
// Then we check for floppy drives
// Check for active optical drives
if (index == -1)
index = Drives.FindIndex(d => d.MarkedActive && d.InternalDriveType == InternalDriveType.Optical);
// Check for active floppy drives
if (index == -1)
index = Drives.FindIndex(d => d.MarkedActive && d.InternalDriveType == InternalDriveType.Floppy);
// Then we try all other drive types
// Check for any active drives
if (index == -1)
index = Drives.FindIndex(d => d.MarkedActive);
// Set the selected index
App.Instance.DriveLetterComboBox.SelectedIndex = (index != -1 ? index : 0);
App.Instance.StatusLabel.Content = "Valid drive found! Choose your Media Type";
App.Instance.StatusLabel.Text = "Valid drive found! Choose your Media Type";
App.Instance.CopyProtectScanButton.IsEnabled = true;
// Get the current system type
@@ -141,7 +154,7 @@ namespace MPF.GUI.ViewModels
{
App.Logger.VerboseLogLn("Found no drives");
App.Instance.DriveLetterComboBox.SelectedIndex = -1;
App.Instance.StatusLabel.Content = "No valid drive found!";
App.Instance.StatusLabel.Text = "No valid drive found!";
App.Instance.StartStopButton.IsEnabled = false;
App.Instance.CopyProtectScanButton.IsEnabled = false;
}
@@ -271,14 +284,15 @@ namespace MPF.GUI.ViewModels
var submissionInfo = new SubmissionInfo()
{
SchemaVersion = 1,
MatchedIDs = new List<int> { 0, 1, 2, 3 },
FullyMatchedID = 3,
PartiallyMatchedIDs = new List<int> { 0, 1, 2, 3 },
Added = DateTime.UtcNow,
LastModified = DateTime.UtcNow,
CommonDiscInfo = new CommonDiscInfoSection()
{
System = RedumpSystem.IBMPCcompatible,
Media = DiscType.BD50,
Media = DiscType.BD128,
Title = "Game Title",
ForeignTitleNonLatin = "Foreign Game Title",
DiscNumberLetter = "1",
@@ -361,7 +375,7 @@ namespace MPF.GUI.ViewModels
DumpersAndStatus = new DumpersAndStatusSection()
{
Status = DumpStatus.TwoOrMoHumanReadablesGreen,
Status = DumpStatus.TwoOrMoreGreen,
Dumpers = new string[] { "Dumper1", "Dumper2" },
OtherDumpers = "Dumper3",
},
@@ -385,6 +399,15 @@ namespace MPF.GUI.ViewModels
SHA1 = "SHA1",
},
DumpingInfo = new DumpingInfoSection()
{
DumpingProgram = "DiscImageCreator 20500101",
Manufacturer = "ATAPI",
Model = "Optical Drive",
Firmware = "1.23",
ReportedDiscType = "CD-R",
},
Artifacts = new Dictionary<string, string>()
{
["Sample Artifact"] = "Sample Data",
@@ -506,7 +529,7 @@ namespace MPF.GUI.ViewModels
// Populate the list of drives and determine the system
if (rescanDrives)
{
App.Instance.StatusLabel.Content = "Creating drive list, please wait!";
App.Instance.StatusLabel.Text = "Creating drive list, please wait!";
await App.Instance.Dispatcher.InvokeAsync(PopulateDrives);
}
else
@@ -548,6 +571,7 @@ namespace MPF.GUI.ViewModels
App.Instance.CopyProtectScanButton.Click += CopyProtectScanButtonClick;
App.Instance.EnableParametersCheckBox.Click += EnableParametersCheckBoxClick;
App.Instance.MediaScanButton.Click += MediaScanButtonClick;
App.Instance.UpdateVolumeLabel.Click += UpdateVolumeLabelClick;
App.Instance.OutputDirectoryBrowseButton.Click += OutputDirectoryBrowseButtonClick;
App.Instance.StartStopButton.Click += StartStopButtonClick;
@@ -612,6 +636,7 @@ namespace MPF.GUI.ViewModels
App.Instance.EnableParametersCheckBox.IsEnabled = false;
App.Instance.StartStopButton.Content = Interface.StopDumping;
App.Instance.MediaScanButton.IsEnabled = false;
App.Instance.UpdateVolumeLabel.IsEnabled = false;
App.Instance.CopyProtectScanButton.IsEnabled = false;
}
@@ -631,6 +656,7 @@ namespace MPF.GUI.ViewModels
App.Instance.EnableParametersCheckBox.IsEnabled = true;
App.Instance.StartStopButton.Content = Interface.StartDumping;
App.Instance.MediaScanButton.IsEnabled = true;
App.Instance.UpdateVolumeLabel.IsEnabled = true;
App.Instance.CopyProtectScanButton.IsEnabled = true;
}
@@ -776,7 +802,15 @@ namespace MPF.GUI.ViewModels
/// </summary>
private void BrowseFolder()
{
WinForms.FolderBrowserDialog folderDialog = new WinForms.FolderBrowserDialog { ShowNewFolderButton = false, SelectedPath = System.AppDomain.CurrentDomain.BaseDirectory };
string currentPath = App.Options.DefaultOutputPath;
if (string.IsNullOrWhiteSpace(currentPath) || !Directory.Exists(currentPath))
currentPath = System.AppDomain.CurrentDomain.BaseDirectory;
WinForms.FolderBrowserDialog folderDialog = new WinForms.FolderBrowserDialog
{
ShowNewFolderButton = false,
SelectedPath = currentPath,
};
WinForms.DialogResult result = folderDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
@@ -815,7 +849,7 @@ namespace MPF.GUI.ViewModels
// If we got an error message, post it to the log
if (errorMessage != null)
App.Logger.VerboseLogLn($"Error in detecting media type: {errorMessage}");
App.Logger.VerboseLogLn($"Message from detecting media type: {errorMessage}");
// If we got either an error or no media, default to the current System default
if (detectedMediaType == null)
@@ -921,7 +955,7 @@ namespace MPF.GUI.ViewModels
// Get the status to write out
Result result = Tools.GetSupportStatus(Env.System, Env.Type);
App.Instance.StatusLabel.Content = result.Message;
App.Instance.StatusLabel.Text = result.Message;
// Set the index for the current disc type
SetCurrentDiscType();
@@ -1006,7 +1040,7 @@ namespace MPF.GUI.ViewModels
App.Instance.DriveLetterComboBox.SelectedIndex = driveIndex;
}
catch { }
int driveSpeed = Env.Parameters.Speed ?? -1;
if (driveSpeed > 0)
App.Instance.DriveSpeedComboBox.SelectedValue = driveSpeed;
@@ -1063,15 +1097,17 @@ namespace MPF.GUI.ViewModels
{
App.Logger.VerboseLogLn($"Scanning for copy protection in {drive.Letter}");
var tempContent = App.Instance.StatusLabel.Content;
App.Instance.StatusLabel.Content = "Scanning for copy protection... this might take a while!";
var tempContent = App.Instance.StatusLabel.Text;
App.Instance.StatusLabel.Text = "Scanning for copy protection... this might take a while!";
App.Instance.StartStopButton.IsEnabled = false;
App.Instance.MediaScanButton.IsEnabled = false;
App.Instance.UpdateVolumeLabel.IsEnabled = false;
App.Instance.CopyProtectScanButton.IsEnabled = false;
var progress = new Progress<ProtectionProgress>();
progress.ProgressChanged += ProgressUpdated;
(bool success, string output) = await Protection.RunProtectionScanOnPath(drive.Letter + ":\\", App.Options, progress);
(var protections, string error) = await Protection.RunProtectionScanOnPath(drive.Letter + ":\\", App.Options, progress);
string output = Protection.FormatProtections(protections);
// If SmartE is detected on the current disc, remove `/sf` from the flags for DIC only
if (Env.Options.InternalProgram == InternalProgram.DiscImageCreator && output.Contains("SmartE"))
@@ -1082,20 +1118,21 @@ namespace MPF.GUI.ViewModels
if (!App.Instance.LogPanel.IsExpanded)
{
if (success)
if (string.IsNullOrEmpty(error))
CustomMessageBox.Show(output, "Detected Protection(s)", MessageBoxButton.OK, MessageBoxImage.Information);
else
CustomMessageBox.Show("An exception occurred, see the log for details", "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
}
if (success)
if (string.IsNullOrEmpty(error))
App.Logger.LogLn($"Detected the following protections in {drive.Letter}:\r\n\r\n{output}");
else
App.Logger.ErrorLogLn($"Path could not be scanned! Exception information:\r\n\r\n{output}");
App.Logger.ErrorLogLn($"Path could not be scanned! Exception information:\r\n\r\n{error}");
App.Instance.StatusLabel.Content = tempContent;
App.Instance.StatusLabel.Text = tempContent;
App.Instance.StartStopButton.IsEnabled = ShouldEnableDumpingButton();
App.Instance.MediaScanButton.IsEnabled = true;
App.Instance.UpdateVolumeLabel.IsEnabled = true;
App.Instance.CopyProtectScanButton.IsEnabled = true;
}
}
@@ -1114,7 +1151,7 @@ namespace MPF.GUI.ViewModels
if (index != -1)
App.Instance.MediaTypeComboBox.SelectedIndex = index;
else
App.Instance.StatusLabel.Content = $"Disc of type '{CurrentMediaType.LongName()}' found, but the current system does not support it!";
App.Instance.StatusLabel.Text = $"Disc of type '{CurrentMediaType.LongName()}' found, but the current system does not support it!";
// Ensure the UI gets updated
App.Instance.UpdateLayout();
@@ -1180,7 +1217,7 @@ namespace MPF.GUI.ViewModels
if (App.Options.ShowDiscEjectReminder)
CustomMessageBox.Show(App.Instance, "It is now safe to eject the disc", "Eject", MessageBoxButton.OK, MessageBoxImage.Information);
var discInformationWindow = new DiscInformationWindow(submissionInfo)
var discInformationWindow = new DiscInformationWindow(App.Options, submissionInfo)
{
Owner = App.Instance,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
@@ -1202,8 +1239,8 @@ namespace MPF.GUI.ViewModels
// One last check to determine environment, just in case
Env = DetermineEnvironment();
// Run path adjustments for DiscImageCreator
Env.AdjustPathsForDiscImageCreator();
// Force an internal drive refresh in case the user entered things manually
Env.Drive.RefreshDrive();
// If still in custom parameter mode, check that users meant to continue or not
if (App.Instance.EnableParametersCheckBox.IsChecked == true)
@@ -1220,54 +1257,20 @@ namespace MPF.GUI.ViewModels
// If "No", then we continue with the current known environment
}
// Run path adjustments for DiscImageCreator
Env.AdjustPathsForDiscImageCreator();
try
{
// Validate that the user explicitly wants an inactive drive to be considered for dumping
if (!Env.Drive.MarkedActive)
{
string message = "The currently selected drive does not appear to contain a disc! "
+ (!Env.System.DetectedByWindows() ? $"This is normal for {Env.System.LongName()} as the discs may not be readable on Windows. " : string.Empty)
+ "Do you want to continue?";
MessageBoxResult mbresult = CustomMessageBox.Show(message, "No Disc Detected", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None)
{
App.Logger.LogLn("Dumping aborted!");
return;
}
}
// If a complete dump already exists
(bool foundFiles, List<string> _) = InfoTool.FoundAllFiles(Env.OutputDirectory, Env.OutputFilename, Env.Parameters, true);
if (foundFiles)
{
MessageBoxResult mbresult = CustomMessageBox.Show("A complete dump already exists! Are you sure you want to overwrite?", "Overwrite?", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None)
{
App.Logger.LogLn("Dumping aborted!");
return;
}
}
// Validate that at least some space exists
// TODO: Tie this to the size of the disc, type of disc, etc.
string fullPath = Path.GetFullPath(Env.OutputDirectory);
var driveInfo = new DriveInfo(Path.GetPathRoot(fullPath));
if (driveInfo.AvailableFreeSpace < Math.Pow(2, 30))
{
MessageBoxResult mbresult = CustomMessageBox.Show("There is less than 1gb of space left on the target drive. Are you sure you want to continue?", "Low Space", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None)
{
App.Logger.LogLn("Dumping aborted!");
return;
}
}
// Run pre-dumping validation checks
if (!ValidateBeforeDumping())
return;
// Disable all UI elements apart from dumping button
DisableAllUIElements();
// Output to the label and log
App.Instance.StatusLabel.Content = "Starting dumping process... Please wait!";
App.Instance.StatusLabel.Text = "Starting dumping process... Please wait!";
App.Logger.LogLn("Starting dumping process... Please wait!");
if (App.Options.ToolsInSeparateWindow)
App.Logger.LogLn("Look for the separate command window for more details");
@@ -1289,7 +1292,7 @@ namespace MPF.GUI.ViewModels
if (!Env.Parameters.IsDumpingCommand())
{
App.Logger.LogLn("No dumping command was run, submission information will not be gathered.");
App.Instance.StatusLabel.Content = "Execution complete!";
App.Instance.StatusLabel.Text = "Execution complete!";
// Reset all UI elements
EnableAllUIElements();
@@ -1304,13 +1307,13 @@ namespace MPF.GUI.ViewModels
else
{
App.Logger.ErrorLogLn(result.Message);
App.Instance.StatusLabel.Content = "Execution failed!";
App.Instance.StatusLabel.Text = "Execution failed!";
}
}
catch (Exception ex)
{
App.Logger.ErrorLogLn(ex.ToString());
App.Instance.StatusLabel.Content = "An exception occurred!";
App.Instance.StatusLabel.Text = "An exception occurred!";
}
finally
{
@@ -1319,6 +1322,57 @@ namespace MPF.GUI.ViewModels
}
}
/// <summary>
/// Perform validation, including user input, before attempting to start dumping
/// </summary>
/// <returns>True if dumping should start, false otherwise</returns>
private bool ValidateBeforeDumping()
{
// Validate that the user explicitly wants an inactive drive to be considered for dumping
if (!Env.Drive.MarkedActive)
{
string message = "The currently selected drive does not appear to contain a disc! "
+ (!Env.System.DetectedByWindows() ? $"This is normal for {Env.System.LongName()} as the discs may not be readable on Windows. " : string.Empty)
+ "Do you want to continue?";
MessageBoxResult mbresult = CustomMessageBox.Show(message, "No Disc Detected", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None)
{
App.Logger.LogLn("Dumping aborted!");
return false;
}
}
// If a complete dump already exists
(bool foundFiles, List<string> _) = InfoTool.FoundAllFiles(Env.OutputDirectory, Env.OutputFilename, Env.Parameters, true);
if (foundFiles)
{
MessageBoxResult mbresult = CustomMessageBox.Show("A complete dump already exists! Are you sure you want to overwrite?", "Overwrite?", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None)
{
App.Logger.LogLn("Dumping aborted!");
return false;
}
}
// Validate that at least some space exists
// TODO: Tie this to the size of the disc, type of disc, etc.
string fullPath = Path.GetFullPath(Env.OutputDirectory);
var driveInfo = new DriveInfo(Path.GetPathRoot(fullPath));
if (driveInfo.AvailableFreeSpace < Math.Pow(2, 30))
{
MessageBoxResult mbresult = CustomMessageBox.Show("There is less than 1gb of space left on the target drive. Are you sure you want to continue?", "Low Space", MessageBoxButton.YesNo, MessageBoxImage.Exclamation);
if (mbresult == MessageBoxResult.No || mbresult == MessageBoxResult.Cancel || mbresult == MessageBoxResult.None)
{
App.Logger.LogLn("Dumping aborted!");
return false;
}
}
// If nothing above fails, we want to continue
return true;
}
#endregion
#region Event Handlers
@@ -1353,9 +1407,9 @@ namespace MPF.GUI.ViewModels
// Update the label with only the first line of output
if (message.Contains("\n"))
App.Instance.StatusLabel.Content = value.Message.Split('\n')[0] + " (See log output)";
App.Instance.StatusLabel.Text = value.Message.Split('\n')[0] + " (See log output)";
else
App.Instance.StatusLabel.Content = value.Message;
App.Instance.StatusLabel.Text = value.Message;
// Log based on success or failure
if (value)
@@ -1370,7 +1424,7 @@ namespace MPF.GUI.ViewModels
private void ProgressUpdated(object sender, ProtectionProgress value)
{
string message = $"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}";
App.Instance.StatusLabel.Content = message;
App.Instance.StatusLabel.Text = message;
App.Logger.VerboseLogLn(message);
}
@@ -1490,12 +1544,21 @@ namespace MPF.GUI.ViewModels
/// <summary>
/// Handler for SystemTypeComboBox SelectionChanged event
/// </summary>
public void SystemTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
private void SystemTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (_canExecuteSelectionChanged)
ChangeSystem();
}
/// <summary>
/// Handler for UpdateVolumeLabel Click event
/// </summary>
private void UpdateVolumeLabelClick(object sender, RoutedEventArgs e)
{
if (_canExecuteSelectionChanged)
InitializeUIValues(removeEventHandlers: true, rescanDrives: false);
}
#endregion
#endregion // Event Handlers

View File

@@ -2,14 +2,16 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using MPF.Core.Data;
using MPF.UI.Core.ComboBoxItems;
using MPF.Windows;
using RedumpLib.Web;
using WPFCustomMessageBox;
namespace MPF.GUI.ViewModels
namespace MPF.UI.ViewModels
{
public class OptionsViewModel
{
@@ -103,7 +105,7 @@ namespace MPF.GUI.ViewModels
/// </summary>
private static List<Element<InternalProgram>> PopulateInternalPrograms()
{
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.DD };
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, /*InternalProgram.Redumper,*/ InternalProgram.DD };
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
}
@@ -186,23 +188,30 @@ namespace MPF.GUI.ViewModels
/// <summary>
/// Test Redump login credentials
/// </summary>
private void TestRedumpLogin()
#if NET48 || NETSTANDARD2_1
private bool? TestRedumpLogin()
#else
private async Task<bool?> TestRedumpLogin()
#endif
{
using (RedumpWebClient wc = new RedumpWebClient())
{
bool? loggedIn = wc.Login(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
if (loggedIn == true)
CustomMessageBox.Show(Parent, "Redump login credentials accepted!", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
else if (loggedIn == false)
CustomMessageBox.Show(Parent, "Redump login credentials denied!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
else
CustomMessageBox.Show(Parent, "Error validating credentials!", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
#if NET48 || NETSTANDARD2_1
(bool? success, string message) = RedumpWebClient.ValidateCredentials(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
#else
(bool? success, string message) = await RedumpHttpClient.ValidateCredentials(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
#endif
if (success == true)
CustomMessageBox.Show(Parent, message, "Success", MessageBoxButton.OK, MessageBoxImage.Information);
else if (success == false)
CustomMessageBox.Show(Parent, message, "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
else
CustomMessageBox.Show(Parent, message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return success;
}
#endregion
#endregion
#region UI Functionality
#region UI Functionality
/// <summary>
/// Create an open folder dialog box
@@ -231,9 +240,9 @@ namespace MPF.GUI.ViewModels
private System.Windows.Controls.TextBox TextBoxForPathSetting(string name) =>
Parent.FindName(name + "TextBox") as System.Windows.Controls.TextBox;
#endregion
#endregion
#region Event Handlers
#region Event Handlers
/// <summary>
/// Handler for generic Click event
@@ -256,9 +265,14 @@ namespace MPF.GUI.ViewModels
/// <summary>
/// Test Redump credentials for validity
/// </summary>
#if NET48 || NETSTANDARD2_1
private void OnRedumpTestClick(object sender, EventArgs e) =>
TestRedumpLogin();
#else
private async void OnRedumpTestClick(object sender, EventArgs e) =>
_ = await TestRedumpLogin();
#endif
#endregion
#endregion
}
}

View File

@@ -1,15 +1,15 @@
<windows:WindowBase x:Class="MPF.Windows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:MPF.UserControls"
xmlns:viewModels="clr-namespace:MPF.GUI.ViewModels"
xmlns:windows="clr-namespace:MPF.Windows"
mc:Ignorable="d"
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<coreWindows:WindowBase x:Class="MPF.Windows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:MPF.UserControls"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows;assembly=MPF.UI.Core"
xmlns:viewModels="clr-namespace:MPF.UI.ViewModels"
mc:Ignorable="d"
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
@@ -24,7 +24,7 @@
<Grid Margin="0,2,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
@@ -163,11 +163,13 @@
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Controls">
<UniformGrid Columns="3" Margin="5,5,5,5">
<UniformGrid Columns="4" Margin="5,5,5,5">
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True" Content="Start Dumping"
IsEnabled="False" Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="MediaScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for discs"
Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="UpdateVolumeLabel" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Update Label"
Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="CopyProtectScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for protection"
Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
@@ -175,7 +177,7 @@
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
<Label x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Waiting for media..." />
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Text="Waiting for media..." />
</UniformGrid>
</GroupBox>
@@ -184,4 +186,4 @@
</Expander>
</StackPanel>
</Grid>
</windows:WindowBase>
</coreWindows:WindowBase>

View File

@@ -1,5 +1,6 @@
using System;
using MPF.GUI.ViewModels;
using MPF.UI.Core.Windows;
using MPF.UI.ViewModels;
namespace MPF.Windows
{

View File

@@ -1,14 +1,14 @@
<windows:WindowBase x:Class="MPF.Windows.OptionsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MPF"
xmlns:windows="clr-namespace:MPF.Windows"
mc:Ignorable="d"
Title="Options" Width="515.132" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<coreWindows:WindowBase x:Class="MPF.Windows.OptionsWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MPF"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows;assembly=MPF.UI.Core"
mc:Ignorable="d"
Title="Options" Width="515.132" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<WindowChrome.WindowChrome>
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
@@ -123,7 +123,7 @@
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
<UniformGrid Columns="2" Rows="8">
<UniformGrid Columns="2" Rows="9">
<CheckBox VerticalAlignment="Center" Content="Skip Type Detect"
IsChecked="{Binding Options.SkipMediaTypeDetection}"
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
@@ -162,6 +162,11 @@
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
/>
<CheckBox VerticalAlignment="Center" Content="Protection File"
IsChecked="{Binding Options.OutputSeparateProtectionFile}" IsEnabled="{Binding Options.ScanForProtection}"
ToolTip="Output protection information to a separate file" Margin="0,4,0,0"
/>
<CheckBox VerticalAlignment="Center" Content="Eject After Dump"
IsChecked="{Binding Options.EjectAfterDump}"
ToolTip="Eject the disc from the drive after dumping has completed" Margin="0,4"
@@ -173,9 +178,14 @@
/>
<CheckBox VerticalAlignment="Center" Content="Enable Tab Input"
IsChecked="{Binding Options.EnableTabsInInputFields}"
ToolTip="Enable entering tabs in supported input fields instead of tab navigation" Margin="0,4"
/>
IsChecked="{Binding Options.EnableTabsInInputFields}"
ToolTip="Enable entering tabs in supported input fields instead of tab navigation" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Redump Compatibility"
IsChecked="{Binding Options.EnableRedumpCompatibility}"
ToolTip="Enable limiting outputs to only those supported by Redump" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Show Eject Reminder"
IsChecked="{Binding Options.ShowDiscEjectReminder}"
@@ -193,7 +203,7 @@
/>
<CheckBox VerticalAlignment="Center" Content="Include Artifacts"
IsChecked="{Binding Options.IncludeArtifacts}" IsEnabled="{Binding Options.OutputSubmissionJSON}"
IsChecked="{Binding Options.IncludeArtifacts}" IsEnabled="{Binding Options.OutputSubmissionJSON}"
ToolTip="Include log files in serialized JSON data" Margin="0,4"
/>
@@ -256,11 +266,6 @@
ToolTip="Include executable packers in outputted protections" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Force Scanning All Files"
IsChecked="{Binding Options.ForceScanningForProtection}"
ToolTip="Force scanning all files even if they're not executables" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
ToolTip="Include debug information during protection scans" Margin="0,4"
@@ -300,7 +305,7 @@
</TabItem>
<TabItem Header="DiscImageCreator" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="3">
<UniformGrid Columns="2" Rows="6">
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
IsChecked="{Binding Options.DICQuietMode}"
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
@@ -321,10 +326,28 @@
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<CheckBox VerticalAlignment="Center" Content="Multi-Sector Read"
IsChecked="{Binding Options.DICMultiSectorRead}"
ToolTip="Enable the /mr flag for BD drive dumping" Margin="0,4"
/>
<Label/> <!-- Empty label for padding -->
<Label Content="Multi-Sector Read Value:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.DICMultiSectorReadValue}" IsEnabled="{Binding Options.DICMultiSectorRead}"
ToolTip="Set the default value for the /mr flag"
/>
<Label Content="CD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.DICRereadCount}"
ToolTip="Specifies how many rereads are attempted on C2 error"
ToolTip="Specifies how many rereads are attempted on C2 error [CD only]"
/>
<Label Content="DVD/HD-DVD/BD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.DICDVDRereadCount}"
ToolTip="Specifies how many rereads are attempted on read error [DVD/HD-DVD/BD only]"
/>
</UniformGrid>
</TabItem>
@@ -385,4 +408,4 @@
</GroupBox>
</StackPanel>
</Grid>
</windows:WindowBase>
</coreWindows:WindowBase>

View File

@@ -1,4 +1,5 @@
using MPF.GUI.ViewModels;
using MPF.UI.Core.Windows;
using MPF.UI.ViewModels;
namespace MPF.Windows
{

View File

@@ -36,9 +36,10 @@ A list of all changes in each stable release and current WIP builds can now be f
MPF uses some external libraries to assist with additional information gathering after the dumping process.
- **Aaru** - Device and media type retrieval - [GitHub](https://github.com/aaru-dps/Aaru)
- **BurnOutSharp** - Protection scanning - [GitHub](https://github.com/mnadareski/BurnOutSharp)
- **WPFCustomMessageBox.thabse** - Custom message boxes in UI - [GitHub](https://github.com/thabse/WPFCustomMessageBox)
- **UnshieldSharp** - Protection scanning - [GitHub](https://github.com/mnadareski/UnshieldSharp)
- **WPFCustomMessageBox.thabse** - Custom message boxes in UI - [GitHub](https://github.com/thabse/WPFCustomMessageBox)
## Contributors

View File

@@ -177,12 +177,12 @@ namespace RedumpLib.Data
public const string DiscPageUrl = @"http://redump.org/disc/{0}/";
/// <summary>
/// Redump last modified search URL
/// Redump last modified search URL template
/// </summary>
public const string LastModifiedUrl = @"http://redump.org/discs/sort/modified/dir/desc?page={0}";
/// <summary>
/// Redump login page URL
/// Redump login page URL template
/// </summary>
public const string LoginUrl = "http://forum.redump.org/login/";
@@ -231,6 +231,11 @@ namespace RedumpLib.Data
/// </summary>
public const string UserDumpsUrl = @"http://redump.org/discs/dumper/{0}/?page={1}";
/// <summary>
/// Redump last modified user dumps URL template
/// </summary>
public const string UserDumpsLastModifiedUrl = @"http://redump.org/discs/sort/modified/dir/desc/dumper/{0}?page={1}";
/// <summary>
/// Redump WIP disc page URL template
/// </summary>

View File

@@ -44,6 +44,11 @@ namespace RedumpLib.Data
/// <summary>
/// List of all disc types
/// </summary>
/// <remarks>
/// All names here match Redump names for the types, not official
/// naming. Some names had to be extrapolated due to no current support
/// in the Redump site.
/// </remarks>
public enum DiscType
{
NONE = 0,
@@ -51,9 +56,21 @@ namespace RedumpLib.Data
[HumanReadable(LongName = "BD-25")]
BD25,
//[HumanReadable(LongName = "BD-33")]
//BD33,
[HumanReadable(LongName = "BD-50")]
BD50,
//[HumanReadable(LongName = "BD-66")]
//BD66,
[HumanReadable(LongName = "BD-100")]
BD100,
[HumanReadable(LongName = "BD-128")]
BD128,
[HumanReadable(LongName = "CD")]
CD,
@@ -62,31 +79,34 @@ namespace RedumpLib.Data
[HumanReadable(LongName = "DVD-9")]
DVD9,
[HumanReadable(LongName = "GD-ROM")]
GDROM,
[HumanReadable(LongName = "HD-DVD SL")]
HDDVDSL,
[HumanReadable(LongName = "HD-DVD DL")]
HDDVDDL,
[HumanReadable(LongName = "MIL-CD")]
MILCD,
[HumanReadable(LongName = "Nintendo GameCube Game Disc")]
NintendoGameCubeGameDisc,
[HumanReadable(LongName = "Nintendo Wii Optical Disc SL")]
NintendoWiiOpticalDiscSL,
[HumanReadable(LongName = "Nintendo Wii Optical Disc DL")]
NintendoWiiOpticalDiscDL,
[HumanReadable(LongName = "Nintendo Wii U Optical Disc SL")]
NintendoWiiUOpticalDiscSL,
[HumanReadable(LongName = "UMD SL")]
UMDSL,
[HumanReadable(LongName = "UMD DL")]
UMDDL,
}
@@ -99,7 +119,7 @@ namespace RedumpLib.Data
BadDumpRed = 2,
PossibleBadDumpYellow = 3,
OriginalMediaBlue = 4,
TwoOrMoHumanReadablesGreen = 5,
TwoOrMoreGreen = 5,
}
/// <summary>
@@ -472,7 +492,7 @@ namespace RedumpLib.Data
[Language(LongName = "Erzya", ThreeLetterCode = "myv")]
Erzya,
[Language(LongName = "Esperanto", TwoLetterCode="eo", ThreeLetterCode = "epo")]
[Language(LongName = "Esperanto", TwoLetterCode = "eo", ThreeLetterCode = "epo")]
Esperanto,
[Language(LongName = "Estonian", TwoLetterCode = "et", ThreeLetterCode = "est")]
@@ -1093,7 +1113,7 @@ namespace RedumpLib.Data
// Ossetian; Ossetic
[Language(LongName = "Ossetian", TwoLetterCode = "os", ThreeLetterCode = "oss")]
Ossetian,
#endregion
#region P
@@ -1754,7 +1774,7 @@ namespace RedumpLib.Data
[HumanReadable(LongName = "Language selector")]
LanguageSelector,
[HumanReadable(LongName = "Options menu")]
OptionsMenu,
}
@@ -2204,7 +2224,7 @@ namespace RedumpLib.Data
[System(Category = SystemCategory.Computer, LongName = "IBM PC compatible", ShortName = "pc", HasCues = true, HasDat = true, HasLsd = true, HasSbi = true)]
IBMPCcompatible,
[System(Category = SystemCategory.Computer, LongName = "NEC PC-88 series", ShortName = "pc-88", HasCues = true, HasDat = true)]
NECPC88series,
@@ -2376,7 +2396,7 @@ namespace RedumpLib.Data
[System(Category = SystemCategory.Arcade, LongName = "TAB-Austria Quizard", ShortName = "quizard", HasCues = true, HasDat = true)]
TABAustriaQuizard,
[System(Category = SystemCategory.Arcade, Available = false, LongName = "Tsunami TsuMo Multi-Game Motion System")]
TsunamiTsuMoMultiGameMotionSystem,
@@ -2426,6 +2446,9 @@ namespace RedumpLib.Data
[System(Category = SystemCategory.Other, LongName = "Sega Prologue 21 Multimedia Karaoke System", ShortName = "sp21", HasCues = true, HasDat = true)]
SegaPrologue21MultimediaKaraokeSystem,
[System(Category = SystemCategory.Other, Available = false, LongName = "Sony Electronic Book")]
SonyElectronicBook,
[System(Category = SystemCategory.Other, Available = false, LongName = "Super Audio CD")]
SuperAudioCD,
@@ -3517,6 +3540,10 @@ namespace RedumpLib.Data
[HumanReadable(ShortName = "[T:X]", LongName = "<b>Extras</b>:")]
Extras,
// TODO: This doesn't have a site tag yet
[HumanReadable(ShortName = "<b>Filename</b>:", LongName = "<b>Filename</b>:")]
Filename,
[HumanReadable(ShortName = "[T:FIID]", LongName = "<b>Fox Interactive ID</b>:")]
FoxInteractiveID,
@@ -3533,6 +3560,10 @@ namespace RedumpLib.Data
[HumanReadable(ShortName = "[T:GTID]", LongName = "<b>GT Interactive ID</b>:")]
GTInteractiveID,
// TODO: This doesn't have a site tag yet
[HumanReadable(ShortName = "<b>Internal Name</b>:", LongName = "<b>Internal Name</b>:")]
InternalName,
[HumanReadable(ShortName = "[T:ISN]", LongName = "<b>Internal Serial</b>:")]
InternalSerialName,
@@ -3561,6 +3592,10 @@ namespace RedumpLib.Data
[HumanReadable(ShortName = "<b>Microsoft ID</b>:", LongName = "<b>Microsoft ID</b>:")]
MicrosoftID,
// TODO: This doesn't have a site tag yet
[HumanReadable(ShortName = "<b>Multisession</b>:", LongName = "<b>Multisession</b>:")]
Multisession,
[HumanReadable(ShortName = "[T:NGID]", LongName = "<b>Nagano ID</b>:")]
NaganoID,

View File

@@ -12,7 +12,7 @@ namespace RedumpLib.Data
{
#region Cross-Enumeration
/// <summary>
/// <summary>
/// Get a list of valid MediaTypes for a given RedumpSystem
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
@@ -673,6 +673,11 @@ namespace RedumpLib.Data
types.Add(MediaType.CDROM);
break;
// UNKNOWN
case RedumpSystem.SonyElectronicBook:
types.Add(MediaType.CDROM);
break;
// https://en.wikipedia.org/wiki/Super_Audio_CD
case RedumpSystem.SuperAudioCD:
types.Add(MediaType.CDROM);
@@ -747,7 +752,11 @@ namespace RedumpLib.Data
switch (discType)
{
case DiscType.BD25:
//case DiscType.BD33:
case DiscType.BD50:
//case DiscType.BD66:
case DiscType.BD100:
case DiscType.BD128:
return MediaType.BluRay;
case DiscType.CD:
return MediaType.CDROM;
@@ -757,6 +766,7 @@ namespace RedumpLib.Data
case DiscType.GDROM:
return MediaType.GDROM;
case DiscType.HDDVDSL:
case DiscType.HDDVDDL:
return MediaType.HDDVD;
// case DiscType.MILCD: // TODO: Support this?
// return MediaType.MILCD;
@@ -847,9 +857,21 @@ namespace RedumpLib.Data
case "bd25":
case "bd-25":
return DiscType.BD25;
//case "bd33":
//case "bd-33":
// return DiscType.BD33;
case "bd50":
case "bd-50":
return DiscType.BD50;
//case "bd66":
//case "bd-66":
// return DiscType.BD66;
case "bd100":
case "bd-100":
return DiscType.BD100;
case "bd128":
case "bd-128":
return DiscType.BD128;
case "cd":
case "cdrom":
case "cd-rom":
@@ -868,6 +890,9 @@ namespace RedumpLib.Data
case "hddvdsl":
case "hd-dvd sl":
return DiscType.HDDVDSL;
case "hddvddl":
case "hd-dvd dl":
return DiscType.HDDVDDL;
case "milcd":
case "mil-cd":
return DiscType.MILCD;
@@ -1001,6 +1026,24 @@ namespace RedumpLib.Data
#region Media Type
/// <summary>
/// List all media types with their short usable names
/// </summary>
public static List<string> ListMediaTypes()
{
var mediaTypes = new List<string>();
foreach (var val in Enum.GetValues(typeof(MediaType)))
{
if (((MediaType)val) == MediaType.NONE)
continue;
mediaTypes.Add($"{((MediaType?)val).ShortName()} - {((MediaType?)val).LongName()}");
}
return mediaTypes;
}
/// <summary>
/// Get the Redump longnames for each known media type
/// </summary>
@@ -1076,6 +1119,46 @@ namespace RedumpLib.Data
#region System
/// <summary>
/// Determine if a system is a marker value
/// </summary>
/// <param name="system">RedumpSystem value to check</param>
/// <returns>True if the system is a marker value, false otherwise</returns>
public static bool IsMarker(this RedumpSystem? system)
{
switch (system)
{
case RedumpSystem.MarkerArcadeEnd:
case RedumpSystem.MarkerComputerEnd:
case RedumpSystem.MarkerDiscBasedConsoleEnd:
// case RedumpSystem.MarkerOtherConsoleEnd:
case RedumpSystem.MarkerOtherEnd:
return true;
default:
return false;
}
}
/// <summary>
/// List all systems with their short usable names
/// </summary>
public static List<string> ListSystems()
{
var systems = new List<string>();
var knownSystems = Enum.GetValues(typeof(RedumpSystem))
.OfType<RedumpSystem?>()
.Where(s => s != null && !s.IsMarker() && s.GetCategory() != SystemCategory.NONE)
.OrderBy(s => s.LongName() ?? string.Empty);
foreach (var val in knownSystems)
{
systems.Add($"{val.ShortName()} - {val.LongName()}");
}
return systems;
}
/// <summary>
/// Get the Redump longnames for each known system
/// </summary>
@@ -1153,12 +1236,14 @@ namespace RedumpLib.Data
case "xboxbios":
case "xbox bios":
case "xbox-bios":
case "microsoftxboxbios":
case "microsoftxbox bios":
case "microsoft xbox bios":
return RedumpSystem.MicrosoftXboxBIOS;
case "gcbios":
case "gc bios":
case "gc-bios":
case "gamecubebios":
case "ngcbios":
case "ngc bios":
@@ -1169,6 +1254,7 @@ namespace RedumpLib.Data
case "ps1 bios":
case "psxbios":
case "psx bios":
case "psx-bios":
case "playstationbios":
case "playstation bios":
case "sonyps1bios":
@@ -1183,6 +1269,7 @@ namespace RedumpLib.Data
return RedumpSystem.SonyPlayStationBIOS;
case "ps2bios":
case "ps2 bios":
case "ps2-bios":
case "playstation2bios":
case "playstation2 bios":
case "playstation 2 bios":
@@ -1198,6 +1285,7 @@ namespace RedumpLib.Data
#region Consoles
case "ajcd":
case "jaguar":
case "jagcd":
case "jaguarcd":
@@ -1207,6 +1295,7 @@ namespace RedumpLib.Data
case "atarijaguarcd":
case "atari jaguar cd":
return RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem;
case "qis":
case "playdia":
case "playdiaqis":
case "playdiaquickinteractivesystem":
@@ -1250,20 +1339,24 @@ namespace RedumpLib.Data
case "fujitsufmtownsmarty":
case "fujitsu fm towns marty":
return RedumpSystem.FujitsuFMTownsMarty;
case "hvn":
case "videonow":
case "hasbrovideonow":
case "hasbro videonow":
return RedumpSystem.HasbroVideoNow;
case "hvnc":
case "videonowcolor":
case "videonow color":
case "hasbrovideonowcolor":
case "hasbro videonow color":
return RedumpSystem.HasbroVideoNowColor;
case "hvnjr":
case "videonowjr":
case "videonow jr":
case "hasbrovideonowjr":
case "hasbro videonow jr":
return RedumpSystem.HasbroVideoNowJr;
case "xvnxp":
case "videonowxp":
case "videonow xp":
case "hasbrovideonowxp":
@@ -1282,6 +1375,7 @@ namespace RedumpLib.Data
case "mattel fisherprice ixl":
case "mattel fisher-price ixl":
return RedumpSystem.MattelFisherPriceiXL;
case "hs":
case "hyperscan":
case "mattelhyperscan":
case "mattel hyperscan":
@@ -1323,6 +1417,7 @@ namespace RedumpLib.Data
case "microsoft xbox series x":
case "microsoft xbox series x and s":
return RedumpSystem.MicrosoftXboxSeriesXS;
case "pce":
case "pcecd":
case "pce-cd":
case "tgcd":
@@ -1418,6 +1513,7 @@ namespace RedumpLib.Data
case "segadreamcast":
case "sega dreamcast":
return RedumpSystem.SegaDreamcast;
case "ss":
case "saturn":
case "segasaturn":
case "sega saturn":
@@ -1527,6 +1623,7 @@ namespace RedumpLib.Data
#region Computers
case "acorn":
case "archcd":
case "archimedes":
case "acornarchimedes":
case "acorn archimedes":
@@ -1539,6 +1636,7 @@ namespace RedumpLib.Data
case "apple mac":
case "apple macintosh":
return RedumpSystem.AppleMacintosh;
case "acd":
case "amiga":
case "commodoreamiga":
case "commodore amiga":
@@ -1569,6 +1667,7 @@ namespace RedumpLib.Data
case "nec pc-98":
return RedumpSystem.NECPC98series;
case "x68k":
case "x68kcd":
case "x68000":
case "sharpx68k":
case "sharp x68k":
@@ -1640,6 +1739,7 @@ namespace RedumpLib.Data
case "ice pc":
case "ice pc-based hardware":
return RedumpSystem.ICEPCHardware;
case "ite":
case "iteagle":
case "eagle":
case "incredible technologies eagle":
@@ -1647,6 +1747,7 @@ namespace RedumpLib.Data
case "itpc":
case "incredible technologies pc-based systems":
return RedumpSystem.IncredibleTechnologiesVarious;
case "kea":
case "eamusement":
case "e-amusement":
case "konamieamusement":
@@ -1654,10 +1755,12 @@ namespace RedumpLib.Data
case "konamie-amusement":
case "konami e-amusement":
return RedumpSystem.KonamieAmusement;
case "kfb":
case "firebeat":
case "konamifirebeat":
case "konami firebeat":
return RedumpSystem.KonamiFireBeat;
case "ksgv":
case "gvsystem":
case "gv system":
case "konamigvsystem":
@@ -1667,6 +1770,7 @@ namespace RedumpLib.Data
case "konamisystemgv":
case "konami system gv":
return RedumpSystem.KonamiSystemGV;
case "km2":
case "konamim2":
case "konami m2":
return RedumpSystem.KonamiM2;
@@ -1679,11 +1783,13 @@ namespace RedumpLib.Data
case "konamipython2":
case "konami python 2":
return RedumpSystem.KonamiPython2;
case "ks573":
case "system573":
case "system 573":
case "konamisystem573":
case "konami system 573":
return RedumpSystem.KonamiSystem573;
case "kt":
case "twinkle":
case "konamitwinkle":
case "konami twinkle":
@@ -1716,6 +1822,7 @@ namespace RedumpLib.Data
case "meritindustriesmegatouchxl":
case "merit industries megatouch xl":
return RedumpSystem.MeritIndustriesMegaTouchXL;
case "ns246":
case "system246":
case "system 246":
case "namcosystem246":
@@ -1748,6 +1855,7 @@ namespace RedumpLib.Data
case "nintendo triforce":
case "namco / sega / nintendo triforce":
return RedumpSystem.NamcoSegaNintendoTriforce;
case "ns12":
case "system12":
case "system 12":
case "namcosystem12":
@@ -1779,6 +1887,7 @@ namespace RedumpLib.Data
case "nichibutsu xrs":
case "nichibutsu x-rate system":
return RedumpSystem.NichibutsuXRateSystem;
case "m2":
case "panasonicm2":
case "panasonic m2":
return RedumpSystem.PanasonicM2;
@@ -1822,10 +1931,12 @@ namespace RedumpLib.Data
case "seganu":
case "sega nu":
return RedumpSystem.SegaNu;
case "sre":
case "ringedge":
case "segaringedge":
case "sega ringedge":
return RedumpSystem.SegaRingEdge;
case "sre2":
case "ringedge2":
case "ringedge 2":
case "segaringedge2":
@@ -1868,6 +1979,7 @@ namespace RedumpLib.Data
case "audio":
case "audiocd":
case "audio cd":
case "audio-cd":
return RedumpSystem.AudioCD;
case "bdvideo":
case "bd-video":
@@ -1885,6 +1997,7 @@ namespace RedumpLib.Data
return RedumpSystem.DVDVideo;
case "enhancedcd":
case "enhanced cd":
case "enhanced-cd":
case "enhancedcdrom":
case "enhanced cdrom":
case "enhanced cd-rom":
@@ -1895,6 +2008,7 @@ namespace RedumpLib.Data
case "hddvd-video":
case "hd-dvd-video":
return RedumpSystem.HDDVDVideo;
case "navi21":
case "naviken":
case "naviken21":
case "naviken 2.1":
@@ -1909,7 +2023,9 @@ namespace RedumpLib.Data
case "photo":
case "photocd":
case "photo cd":
case "photo-cd":
return RedumpSystem.PhotoCD;
case "psxgs":
case "gameshark":
case "psgameshark":
case "ps gameshark":
@@ -1925,6 +2041,7 @@ namespace RedumpLib.Data
case "rainbowdisc":
case "rainbow disc":
return RedumpSystem.RainbowDisc;
case "sp21":
case "pl21":
case "prologue21":
case "prologue 21":
@@ -1932,6 +2049,10 @@ namespace RedumpLib.Data
case "sega prologue21":
case "sega prologue 21":
return RedumpSystem.SegaPrologue21MultimediaKaraokeSystem;
case "electronicbook":
case "sonyelectronicbook":
case "sony electronic book":
return RedumpSystem.SonyElectronicBook;
case "sacd":
case "superaudiocd":
case "super audio cd":
@@ -1940,6 +2061,7 @@ namespace RedumpLib.Data
case "taoiktv":
case "tao iktv":
return RedumpSystem.TaoiKTV;
case "ksite":
case "kisssite":
case "kiss-site":
case "tomykisssite":
@@ -1959,7 +2081,7 @@ namespace RedumpLib.Data
}
#endregion
#region System Category
/// <summary>

View File

@@ -12,13 +12,19 @@ namespace RedumpLib.Data
/// Version of the current schema
/// </summary>
[JsonProperty(PropertyName = "schema_version", DefaultValueHandling = DefaultValueHandling.Ignore)]
public int SchemaVersion { get; set; } = 1;
public int SchemaVersion { get; set; } = 2;
/// <summary>
/// List of matched Redump IDs
/// Fully matched Redump ID
/// </summary>
[JsonIgnore]
public List<int> MatchedIDs { get; set; }
public int? FullyMatchedID { get; set; }
/// <summary>
/// List of partially matched Redump IDs
/// </summary>
[JsonIgnore]
public List<int> PartiallyMatchedIDs { get; set; }
/// <summary>
/// DateTime of when the disc was added
@@ -59,15 +65,19 @@ namespace RedumpLib.Data
[JsonProperty(PropertyName = "size_and_checksums", DefaultValueHandling = DefaultValueHandling.Ignore)]
public SizeAndChecksumsSection SizeAndChecksums { get; set; } = new SizeAndChecksumsSection();
[JsonProperty(PropertyName = "dumping_info", DefaultValueHandling = DefaultValueHandling.Ignore)]
public DumpingInfoSection DumpingInfo { get; set; } = new DumpingInfoSection();
[JsonProperty(PropertyName = "artifacts", DefaultValueHandling = DefaultValueHandling.Ignore)]
public Dictionary<string, string> Artifacts { get; set; } = new Dictionary<string, string>();
public object Clone()
{
return new SubmissionInfo
{
SchemaVersion = this.SchemaVersion,
MatchedIDs = this.MatchedIDs,
FullyMatchedID = this.FullyMatchedID,
PartiallyMatchedIDs = this.PartiallyMatchedIDs,
Added = this.Added,
LastModified = this.LastModified,
CommonDiscInfo = this.CommonDiscInfo?.Clone() as CommonDiscInfoSection,
@@ -79,6 +89,7 @@ namespace RedumpLib.Data
DumpersAndStatus = this.DumpersAndStatus?.Clone() as DumpersAndStatusSection,
TracksAndWriteOffsets = this.TracksAndWriteOffsets?.Clone() as TracksAndWriteOffsetsSection,
SizeAndChecksums = this.SizeAndChecksums?.Clone() as SizeAndChecksumsSection,
DumpingInfo = this.DumpingInfo?.Clone() as DumpingInfoSection,
Artifacts = this.Artifacts?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
};
}
@@ -148,7 +159,7 @@ namespace RedumpLib.Data
[JsonProperty(PropertyName = "d_ring_0_mo1_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer0MouldSID { get; set; }
[JsonProperty(PropertyName = "dr_ring_0_mo1", NullValueHandling = NullValueHandling.Ignore)]
[JsonProperty(PropertyName = "d_ring_0_mo1", NullValueHandling = NullValueHandling.Ignore)]
public string Layer0AdditionalMould { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma2", Required = Required.AllowNull)]
@@ -163,7 +174,7 @@ namespace RedumpLib.Data
[JsonProperty(PropertyName = "d_ring_0_mo2_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer1MouldSID { get; set; }
[JsonProperty(PropertyName = "dr_ring_0_mo2", NullValueHandling = NullValueHandling.Ignore)]
[JsonProperty(PropertyName = "d_ring_0_mo2", NullValueHandling = NullValueHandling.Ignore)]
public string Layer1AdditionalMould { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma3", Required = Required.AllowNull)]
@@ -395,6 +406,9 @@ namespace RedumpLib.Data
[JsonProperty(PropertyName = "d_protection", NullValueHandling = NullValueHandling.Ignore)]
public string Protection { get; set; }
[JsonIgnore]
public Dictionary<string, List<string>> FullProtections { get; set; }
[JsonProperty(PropertyName = "d_securom", NullValueHandling = NullValueHandling.Ignore)]
public string SecuROMData { get; set; }
@@ -406,6 +420,7 @@ namespace RedumpLib.Data
LibCrypt = this.LibCrypt,
LibCryptData = this.LibCryptData,
Protection = this.Protection,
FullProtections = this.FullProtections?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
SecuROMData = this.SecuROMData,
};
}
@@ -505,4 +520,42 @@ namespace RedumpLib.Data
};
}
}
/// <summary>
/// Dumping info section for moderation
/// </summary>
public class DumpingInfoSection : ICloneable
{
// Name not defined by Redump
[JsonProperty(PropertyName = "d_dumping_program", Required = Required.AllowNull)]
public string DumpingProgram { get; set; }
// Name not defined by Redump
[JsonProperty(PropertyName = "d_drive_manufacturer", Required = Required.AllowNull)]
public string Manufacturer { get; set; }
// Name not defined by Redump
[JsonProperty(PropertyName = "d_drive_model", Required = Required.AllowNull)]
public string Model { get; set; }
// Name not defined by Redump
[JsonProperty(PropertyName = "d_drive_firmware", Required = Required.AllowNull)]
public string Firmware { get; set; }
// Name not defined by Redump
[JsonProperty(PropertyName = "d_reported_disc_type", Required = Required.AllowNull)]
public string ReportedDiscType { get; set; }
public object Clone()
{
return new DumpingInfoSection
{
DumpingProgram = this.DumpingProgram,
Manufacturer = this.Manufacturer,
Model = this.Model,
Firmware = this.Firmware,
ReportedDiscType = this.ReportedDiscType,
};
}
}
}

View File

@@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<TargetFrameworks>net48;netstandard2.1;net6.0</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>

View File

@@ -0,0 +1,808 @@
#if NET6_0
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using RedumpLib.Data;
namespace RedumpLib.Web
{
public class RedumpHttpClient : HttpClient
{
#region Properties
/// <summary>
/// Determines if user is logged into Redump
/// </summary>
public bool LoggedIn { get; private set; } = false;
/// <summary>
/// Determines if the user is a staff member
/// </summary>
public bool IsStaff { get; private set; } = false;
#endregion
/// <summary>
/// Constructor
/// </summary>
public RedumpHttpClient()
: base(new HttpClientHandler { UseCookies = true })
{
}
#region Credentials
/// <summary>
/// Validate supplied credentials
/// </summary>
public async static Task<(bool?, string)> ValidateCredentials(string username, string password)
{
// If options are invalid or we're missing something key, just return
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
return (false, null);
// Try logging in with the supplied credentials otherwise
using RedumpHttpClient httpClient = new();
bool? loggedIn = await httpClient.Login(username, password);
if (loggedIn == true)
return (true, "Redump username and password accepted!");
else if (loggedIn == false)
return (false, "Redump username and password denied!");
else
return (null, "An error occurred validating your credentials!");
}
/// <summary>
/// Login to Redump, if possible
/// </summary>
/// <param name="username">Redump username</param>
/// <param name="password">Redump password</param>
/// <returns>True if the user could be logged in, false otherwise, null on error</returns>
public async Task<bool?> Login(string username, string password)
{
// Credentials verification
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
{
Console.WriteLine("Credentials entered, will attempt Redump login...");
}
else if (!string.IsNullOrWhiteSpace(username) && string.IsNullOrWhiteSpace(password))
{
Console.WriteLine("Only a username was specified, will not attempt Redump login...");
return false;
}
else if (string.IsNullOrWhiteSpace(username))
{
Console.WriteLine("No credentials entered, will not attempt Redump login...");
return false;
}
// HTTP encode the password
password = WebUtility.UrlEncode(password);
// Attempt to login up to 3 times
for (int i = 0; i < 3; i++)
{
try
{
// Get the current token from the login page
var loginPage = await GetStringAsync(Constants.LoginUrl);
string token = Constants.TokenRegex.Match(loginPage).Groups[1].Value;
// Construct the login request
var postContent = new StringContent($"form_sent=1&redirect_url=&csrf_token={token}&req_username={username}&req_password={password}&save_pass=0", Encoding.UTF8);
postContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
// Send the login request and get the result
var response = await PostAsync(Constants.LoginUrl, postContent);
string responseContent = await response?.Content?.ReadAsStringAsync();
if (string.IsNullOrWhiteSpace(responseContent))
{
Console.WriteLine($"An error occurred while trying to log in on attempt {i}: No response");
continue;
}
if (responseContent.Contains("Incorrect username and/or password."))
{
Console.WriteLine("Invalid credentials entered, continuing without logging in...");
return false;
}
// The user was able to be logged in
Console.WriteLine("Credentials accepted! Logged into Redump...");
LoggedIn = true;
// If the user is a moderator or staff, set accordingly
if (responseContent.Contains("http://forum.redump.org/forum/9/staff/"))
IsStaff = true;
return true;
}
catch (Exception ex)
{
Console.WriteLine($"An exception occurred while trying to log in on attempt {i}: {ex}");
}
}
Console.WriteLine("Could not login to Redump in 3 attempts, continuing without logging in...");
return false;
}
#endregion
#region Single Page Helpers
/// <summary>
/// Process a Redump site page as a list of possible IDs or disc page
/// </summary>
/// <param name="url">Base URL to download using</param>
/// <returns>List of IDs from the page, empty on error</returns>
public async Task<List<int>> CheckSingleSitePage(string url)
{
List<int> ids = new();
// Try up to 3 times to retrieve the data
string dumpsPage = await DownloadString(url, retries: 3);
// If we have no dumps left
if (dumpsPage == null || dumpsPage.Contains("No discs found."))
return ids;
// If we have a single disc page already
if (dumpsPage.Contains("<b>Download:</b>"))
{
var value = Regex.Match(dumpsPage, @"/disc/(\d+)/sfv/").Groups[1].Value;
if (int.TryParse(value, out int id))
ids.Add(id);
return ids;
}
// Otherwise, traverse each dump on the page
var matches = Constants.DiscRegex.Matches(dumpsPage);
foreach (Match match in matches)
{
try
{
if (int.TryParse(match.Groups[1].Value, out int value))
ids.Add(value);
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
continue;
}
}
return ids;
}
/// <summary>
/// Process a Redump site page as a list of possible IDs or disc page
/// </summary>
/// <param name="url">Base URL to download using</param>
/// <param name="outDir">Output directory to save data to</param>
/// <param name="failOnSingle">True to return on first error, false otherwise</param>
/// <returns>True if the page could be downloaded, false otherwise</returns>
public async Task<bool> CheckSingleSitePage(string url, string outDir, bool failOnSingle)
{
// Try up to 3 times to retrieve the data
string dumpsPage = await DownloadString(url, retries: 3);
// If we have no dumps left
if (dumpsPage == null || dumpsPage.Contains("No discs found."))
return false;
// If we have a single disc page already
if (dumpsPage.Contains("<b>Download:</b>"))
{
var value = Regex.Match(dumpsPage, @"/disc/(\d+)/sfv/").Groups[1].Value;
if (int.TryParse(value, out int id))
{
bool downloaded = await DownloadSingleSiteID(id, outDir, false);
if (!downloaded && failOnSingle)
return false;
}
return false;
}
// Otherwise, traverse each dump on the page
var matches = Constants.DiscRegex.Matches(dumpsPage);
foreach (Match match in matches)
{
try
{
if (int.TryParse(match.Groups[1].Value, out int value))
{
bool downloaded = await DownloadSingleSiteID(value, outDir, false);
if (!downloaded && failOnSingle)
return false;
}
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
continue;
}
}
return true;
}
/// <summary>
/// Process a Redump WIP page as a list of possible IDs or disc page
/// </summary>
/// <param name="wc">RedumpWebClient to access the packs</param>
/// <returns>List of IDs from the page, empty on error</returns>
public async Task<List<int>> CheckSingleWIPPage(string url)
{
List<int> ids = new();
// Try up to 3 times to retrieve the data
string dumpsPage = await DownloadString(url, retries: 3);
// If we have no dumps left
if (dumpsPage == null || dumpsPage.Contains("No discs found."))
return ids;
// Otherwise, traverse each dump on the page
var matches = Constants.NewDiscRegex.Matches(dumpsPage);
foreach (Match match in matches)
{
try
{
if (int.TryParse(match.Groups[2].Value, out int value))
ids.Add(value);
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
continue;
}
}
return ids;
}
/// <summary>
/// Process a Redump WIP page as a list of possible IDs or disc page
/// </summary>
/// <param name="wc">RedumpWebClient to access the packs</param>
/// <param name="outDir">Output directory to save data to</param>
/// <param name="failOnSingle">True to return on first error, false otherwise</param>
/// <returns>True if the page could be downloaded, false otherwise</returns>
public async Task<bool> CheckSingleWIPPage(string url, string outDir, bool failOnSingle)
{
// Try up to 3 times to retrieve the data
string dumpsPage = await DownloadString(url, retries: 3);
// If we have no dumps left
if (dumpsPage == null || dumpsPage.Contains("No discs found."))
return false;
// Otherwise, traverse each dump on the page
var matches = Constants.NewDiscRegex.Matches(dumpsPage);
foreach (Match match in matches)
{
try
{
if (int.TryParse(match.Groups[2].Value, out int value))
{
bool downloaded = await DownloadSingleWIPID(value, outDir, false);
if (!downloaded && failOnSingle)
return false;
}
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
continue;
}
}
return true;
}
#endregion
#region Download Helpers
/// <summary>
/// Download a single pack
/// </summary>
/// <param name="url">Base URL to download using</param>
/// <param name="system">System to download packs for</param>
/// <returns>Byte array containing the downloaded pack, null on error</returns>
public async Task<byte[]> DownloadSinglePack(string url, RedumpSystem? system)
{
try
{
return await GetByteArrayAsync(string.Format(url, system.ShortName()));
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
return null;
}
}
/// <summary>
/// Download a single pack
/// </summary>
/// <param name="url">Base URL to download using</param>
/// <param name="system">System to download packs for</param>
/// <param name="outDir">Output directory to save data to</param>
/// <param name="subfolder">Named subfolder for the pack, used optionally</param>
public async Task<bool> DownloadSinglePack(string url, RedumpSystem? system, string outDir, string subfolder)
{
try
{
// If no output directory is defined, use the current directory instead
if (string.IsNullOrWhiteSpace(outDir))
outDir = Environment.CurrentDirectory;
string tempfile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString());
string packUri = string.Format(url, system.ShortName());
// Make the call to get the pack
string remoteFileName = await DownloadFile(packUri, tempfile);
MoveOrDelete(tempfile, remoteFileName, outDir, subfolder);
return true;
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
return false;
}
}
/// <summary>
/// Download an individual site ID data, if possible
/// </summary>
/// <param name="id">Redump disc ID to retrieve</param>
/// <returns>String containing the page contents if successful, null on error</returns>
public async Task<string> DownloadSingleSiteID(int id)
{
string paddedId = id.ToString().PadLeft(5, '0');
Console.WriteLine($"Processing ID: {paddedId}");
try
{
// Try up to 3 times to retrieve the data
string discPageUri = string.Format(Constants.DiscPageUrl, +id);
string discPage = await DownloadString(discPageUri, retries: 3);
if (discPage == null || discPage.Contains($"Disc with ID \"{id}\" doesn't exist"))
{
Console.WriteLine($"ID {paddedId} could not be found!");
return null;
}
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
return discPage;
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
return null;
}
}
/// <summary>
/// Download an individual site ID data, if possible
/// </summary>
/// <param name="id">Redump disc ID to retrieve</param>
/// <param name="outDir">Output directory to save data to</param>
/// <param name="rename">True to rename deleted entries, false otherwise</param>
/// <returns>True if all data was downloaded, false otherwise</returns>
public async Task<bool> DownloadSingleSiteID(int id, string outDir, bool rename)
{
// If no output directory is defined, use the current directory instead
if (string.IsNullOrWhiteSpace(outDir))
outDir = Environment.CurrentDirectory;
string paddedId = id.ToString().PadLeft(5, '0');
string paddedIdDir = Path.Combine(outDir, paddedId);
Console.WriteLine($"Processing ID: {paddedId}");
try
{
// Try up to 3 times to retrieve the data
string discPageUri = string.Format(Constants.DiscPageUrl, +id);
string discPage = await DownloadString(discPageUri, retries: 3);
if (discPage == null || discPage.Contains($"Disc with ID \"{id}\" doesn't exist"))
{
try
{
if (rename)
{
if (Directory.Exists(paddedIdDir) && rename)
Directory.Move(paddedIdDir, paddedIdDir + "-deleted");
else
Directory.CreateDirectory(paddedIdDir + "-deleted");
}
}
catch { }
Console.WriteLine($"ID {paddedId} could not be found!");
return false;
}
// Check if the page has been updated since the last time it was downloaded, if possible
if (File.Exists(Path.Combine(paddedIdDir, "disc.html")))
{
// Read in the cached file
var oldDiscPage = File.ReadAllText(Path.Combine(paddedIdDir, "disc.html"));
// Check for the last modified date in both pages
var oldResult = Constants.LastModifiedRegex.Match(oldDiscPage);
var newResult = Constants.LastModifiedRegex.Match(discPage);
// If both pages contain the same modified date, skip it
if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value)
{
Console.WriteLine($"ID {paddedId} has not been changed since last download");
return false;
}
// If neither page contains a modified date, skip it
else if (!oldResult.Success && !newResult.Success)
{
Console.WriteLine($"ID {paddedId} has not been changed since last download");
return false;
}
}
// Create ID subdirectory
Directory.CreateDirectory(paddedIdDir);
// View Edit History
if (discPage.Contains($"<a href=\"/disc/{id}/changes/\""))
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.ChangesExt, Path.Combine(paddedIdDir, "changes.html"));
// CUE
if (discPage.Contains($"<a href=\"/disc/{id}/cue/\""))
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.CueExt, Path.Combine(paddedIdDir, paddedId + ".cue"));
// Edit disc
if (discPage.Contains($"<a href=\"/disc/{id}/edit/\""))
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.EditExt, Path.Combine(paddedIdDir, "edit.html"));
// GDI
if (discPage.Contains($"<a href=\"/disc/{id}/gdi/\""))
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.GdiExt, Path.Combine(paddedIdDir, paddedId + ".gdi"));
// KEYS
if (discPage.Contains($"<a href=\"/disc/{id}/key/\""))
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.KeyExt, Path.Combine(paddedIdDir, paddedId + ".key"));
// LSD
if (discPage.Contains($"<a href=\"/disc/{id}/lsd/\""))
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.LsdExt, Path.Combine(paddedIdDir, paddedId + ".lsd"));
// MD5
if (discPage.Contains($"<a href=\"/disc/{id}/md5/\""))
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.Md5Ext, Path.Combine(paddedIdDir, paddedId + ".md5"));
// Review WIP entry
if (Constants.NewDiscRegex.IsMatch(discPage))
{
var match = Constants.NewDiscRegex.Match(discPage);
_ = await DownloadFile(string.Format(Constants.WipDiscPageUrl, match.Groups[2].Value), Path.Combine(paddedIdDir, "newdisc.html"));
}
// SBI
if (discPage.Contains($"<a href=\"/disc/{id}/sbi/\""))
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.SbiExt, Path.Combine(paddedIdDir, paddedId + ".sbi"));
// SFV
if (discPage.Contains($"<a href=\"/disc/{id}/sfv/\""))
await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.SfvExt, Path.Combine(paddedIdDir, paddedId + ".sfv"));
// SHA1
if (discPage.Contains($"<a href=\"/disc/{id}/sha1/\""))
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.Sha1Ext, Path.Combine(paddedIdDir, paddedId + ".sha1"));
// HTML (Last in case of errors)
using (var discStreamWriter = File.CreateText(Path.Combine(paddedIdDir, "disc.html")))
{
discStreamWriter.Write(discPage);
}
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
return false;
}
}
/// <summary>
/// Download an individual WIP ID data, if possible
/// </summary>
/// <param name="id">Redump WIP disc ID to retrieve</param>
/// <returns>String containing the page contents if successful, null on error</returns>
public async Task<string> DownloadSingleWIPID(int id)
{
string paddedId = id.ToString().PadLeft(5, '0');
Console.WriteLine($"Processing ID: {paddedId}");
try
{
// Try up to 3 times to retrieve the data
string discPageUri = string.Format(Constants.WipDiscPageUrl, +id);
string discPage = await DownloadString(discPageUri, retries: 3);
if (discPage == null || discPage.Contains($"WIP disc with ID \"{id}\" doesn't exist"))
{
Console.WriteLine($"ID {paddedId} could not be found!");
return null;
}
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
return discPage;
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
return null;
}
}
/// <summary>
/// Download an individual WIP ID data, if possible
/// </summary>
/// <param name="id">Redump WIP disc ID to retrieve</param>
/// <param name="outDir">Output directory to save data to</param>
/// <param name="rename">True to rename deleted entries, false otherwise</param>
/// <returns>True if all data was downloaded, false otherwise</returns>
public async Task<bool> DownloadSingleWIPID(int id, string outDir, bool rename)
{
// If no output directory is defined, use the current directory instead
if (string.IsNullOrWhiteSpace(outDir))
outDir = Environment.CurrentDirectory;
string paddedId = id.ToString().PadLeft(5, '0');
string paddedIdDir = Path.Combine(outDir, paddedId);
Console.WriteLine($"Processing ID: {paddedId}");
try
{
// Try up to 3 times to retrieve the data
string discPageUri = string.Format(Constants.WipDiscPageUrl, +id);
string discPage = await DownloadString(discPageUri, retries: 3);
if (discPage == null || discPage.Contains($"WIP disc with ID \"{id}\" doesn't exist"))
{
try
{
if (rename)
{
if (Directory.Exists(paddedIdDir) && rename)
Directory.Move(paddedIdDir, paddedIdDir + "-deleted");
else
Directory.CreateDirectory(paddedIdDir + "-deleted");
}
}
catch { }
Console.WriteLine($"ID {paddedId} could not be found!");
return false;
}
// Check if the page has been updated since the last time it was downloaded, if possible
if (File.Exists(Path.Combine(paddedIdDir, "disc.html")))
{
// Read in the cached file
var oldDiscPage = File.ReadAllText(Path.Combine(paddedIdDir, "disc.html"));
// Check for the full match ID in both pages
var oldResult = Constants.FullMatchRegex.Match(oldDiscPage);
var newResult = Constants.FullMatchRegex.Match(discPage);
// If both pages contain the same ID, skip it
if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value)
{
Console.WriteLine($"ID {paddedId} has not been changed since last download");
return false;
}
// If neither page contains an ID, skip it
else if (!oldResult.Success && !newResult.Success)
{
Console.WriteLine($"ID {paddedId} has not been changed since last download");
return false;
}
}
// Create ID subdirectory
Directory.CreateDirectory(paddedIdDir);
// HTML
using (var discStreamWriter = File.CreateText(Path.Combine(paddedIdDir, "disc.html")))
{
discStreamWriter.Write(discPage);
}
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
return true;
}
catch (Exception ex)
{
Console.WriteLine($"An exception has occurred: {ex}");
return false;
}
}
#endregion
#region Helpers
/// <summary>
/// Download a set of packs
/// </summary>
/// <param name="url">Base URL to download using</param>
/// <param name="system">Systems to download packs for</param>
/// <param name="title">Name of the pack that is downloading</param>
public async Task<Dictionary<RedumpSystem, byte[]>> DownloadPacks(string url, RedumpSystem?[] systems, string title)
{
var packsDictionary = new Dictionary<RedumpSystem, byte[]>();
Console.WriteLine($"Downloading {title}");
foreach (var system in systems)
{
// If the system is invalid, we can't do anything
if (system == null || !system.IsAvailable())
continue;
// If we didn't have credentials
if (!LoggedIn && system.IsBanned())
continue;
// If the system is unknown, we can't do anything
string longName = system.LongName();
if (string.IsNullOrWhiteSpace(longName))
continue;
Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName.Length - 1)}");
byte[] pack = await DownloadSinglePack(url, system);
if (pack != null)
packsDictionary.Add(system.Value, pack);
}
Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}");
Console.WriteLine();
return packsDictionary;
}
/// <summary>
/// Download a set of packs
/// </summary>
/// <param name="url">Base URL to download using</param>
/// <param name="systems">Systems to download packs for</param>
/// <param name="title">Name of the pack that is downloading</param>
/// <param name="outDir">Output directory to save data to</param>
/// <param name="subfolder">Named subfolder for the pack, used optionally</param>
public async Task<bool> DownloadPacks(string url, RedumpSystem?[] systems, string title, string outDir, string subfolder)
{
Console.WriteLine($"Downloading {title}");
foreach (var system in systems)
{
// If the system is invalid, we can't do anything
if (system == null || !system.IsAvailable())
continue;
// If we didn't have credentials
if (!LoggedIn && system.IsBanned())
continue;
// If the system is unknown, we can't do anything
string longName = system.LongName();
if (string.IsNullOrWhiteSpace(longName))
continue;
Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName.Length - 1)}");
await DownloadSinglePack(url, system, outDir, subfolder);
}
Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}");
Console.WriteLine();
return true;
}
/// <summary>
/// Download from a URI to a local file
/// </summary>
/// <param name="uri">Remote URI to retrieve</param>
/// <param name="fileName">Filename to write to</param>
/// <returns>The remote filename from the URI, null on error</returns>
private async Task<string> DownloadFile(string uri, string fileName)
{
// Make the call to get the file
var response = await GetAsync(uri);
if (response?.Content?.Headers == null || !response.IsSuccessStatusCode)
{
Console.WriteLine($"Could not download {uri}");
return null;
}
// Copy the data to a local temp file
using (var responseStream = await response.Content.ReadAsStreamAsync())
using (var tempFileStream = File.OpenWrite(fileName))
{
responseStream.CopyTo(tempFileStream);
}
return response.Content.Headers.ContentDisposition?.FileName?.Replace("\"", "");
}
/// <summary>
/// Download from a URI to a string
/// </summary>
/// <param name="uri">Remote URI to retrieve</param>
/// <param name="retries">Number of times to retry on error</param>
/// <returns>String from the URI, null on error</returns>
private async Task<string> DownloadString(string uri, int retries = 3)
{
// Only retry a positive number of times
if (retries <= 0)
return null;
for (int i = 0; i < retries; i++)
{
try
{
return await GetStringAsync(uri);
}
catch { }
}
return null;
}
/// <summary>
/// Move a tempfile to a new name unless it aleady exists, in which case, delete the tempfile
/// </summary>
/// <param name="tempfile">Path to existing temporary file</param>
/// <param name="newfile">Path to new output file</param>
/// <param name="outDir">Output directory to save data to</param>
/// <param name="subfolder">Optional subfolder to append to the path</param>
private static void MoveOrDelete(string tempfile, string newfile, string outDir, string subfolder)
{
// If we don't have a file to move to, just delete the temp file
if (string.IsNullOrWhiteSpace(newfile))
{
File.Delete(tempfile);
return;
}
// If we have a subfolder, create it and update the newfile name
if (!string.IsNullOrWhiteSpace(subfolder))
{
if (!Directory.Exists(Path.Combine(outDir, subfolder)))
Directory.CreateDirectory(Path.Combine(outDir, subfolder));
newfile = Path.Combine(subfolder, newfile);
}
// If the file already exists, don't overwrite it
if (File.Exists(Path.Combine(outDir, newfile)))
File.Delete(tempfile);
else
File.Move(tempfile, Path.Combine(outDir, newfile));
}
#endregion
}
}
#endif

View File

@@ -1,4 +1,6 @@
using System;
#if NET48 || NETSTANDARD2_1
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
@@ -32,29 +34,58 @@ namespace RedumpLib.Web
// If the response headers are null or empty
if (ResponseHeaders == null || ResponseHeaders.Count == 0)
return null;
// If we don't have the response header we care about
string headerValue = ResponseHeaders.Get("Content-Disposition");
if (string.IsNullOrWhiteSpace(headerValue))
return null;
// Extract the filename from the value
#if NETSTANDARD2_1
return headerValue[(headerValue.IndexOf("filename=") + 9)..].Replace("\"", "");
#else
return headerValue.Substring(headerValue.IndexOf("filename=") + 9).Replace("\"", "");
#endif
}
/// <inheritdoc/>
protected override WebRequest GetWebRequest(Uri address)
{
WebRequest request = base.GetWebRequest(address);
HttpWebRequest webRequest = request as HttpWebRequest;
if (webRequest != null)
{
if (request is HttpWebRequest webRequest)
webRequest.CookieContainer = m_container;
}
return request;
}
/// <summary>
/// Validate supplied credentials
/// </summary>
public static (bool?, string) ValidateCredentials(string username, string password)
{
// If options are invalid or we're missing something key, just return
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
return (false, null);
// Try logging in with the supplied credentials otherwise
#if NETSTANDARD2_1
using RedumpWebClient wc = new RedumpWebClient();
#else
using (RedumpWebClient wc = new RedumpWebClient())
{
#endif
bool? loggedIn = wc.Login(username, password);
if (loggedIn == true)
return (true, "Redump username and password accepted!");
else if (loggedIn == false)
return (false, "Redump username and password denied!");
else
return (null, "An error occurred validating your credentials!");
#if NET48
}
#endif
}
/// <summary>
/// Login to Redump, if possible
/// </summary>
@@ -502,7 +533,7 @@ namespace RedumpLib.Web
// View Edit History
if (discPage.Contains($"<a href=\"/disc/{id}/changes/\""))
DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.ChangesExt, Path.Combine(paddedIdDir, "changes.html"));
// CUE
if (discPage.Contains($"<a href=\"/disc/{id}/cue/\""))
DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.CueExt, Path.Combine(paddedIdDir, paddedId + ".cue"));
@@ -719,7 +750,7 @@ namespace RedumpLib.Web
// If we didn't have credentials
if (!LoggedIn && system.IsBanned())
continue;
// If the system is unknown, we can't do anything
string longName = system.LongName();
if (string.IsNullOrWhiteSpace(longName))
@@ -765,7 +796,7 @@ namespace RedumpLib.Web
Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName.Length - 1)}");
DownloadSinglePack(url, system, outDir, subfolder);
}
}
Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}");
Console.WriteLine();
@@ -778,7 +809,7 @@ namespace RedumpLib.Web
/// <param name="newfile">Path to new output file</param>
/// <param name="outDir">Output directory to save data to</param>
/// <param name="subfolder">Optional subfolder to append to the path</param>
private void MoveOrDelete(string tempfile, string newfile, string outDir, string subfolder)
private static void MoveOrDelete(string tempfile, string newfile, string outDir, string subfolder)
{
if (!string.IsNullOrWhiteSpace(newfile))
{
@@ -802,3 +833,5 @@ namespace RedumpLib.Web
#endregion
}
}
#endif

View File

@@ -6,7 +6,7 @@ pull_requests:
do_not_increment_build_number: true
# vm template
image: Visual Studio 2019
image: Visual Studio 2022
# environment variables
environment:
@@ -35,42 +35,38 @@ build:
# post-build step
after_build:
- ps: appveyor DownloadFile https://github.com/aaru-dps/Aaru/releases/download/v5.3.0/aaru-5.3.0_windows_x64.zip
- ps: appveyor DownloadFile https://github.com/aaru-dps/Aaru/releases/download/v5.3.1/aaru-5.3.1_windows_x64.zip
- ps: appveyor DownloadFile http://www.chrysocome.net/downloads/8ab730cd2a29e76ddd89be1f99357942/dd-0.6beta3.zip
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/7453967/DiscImageCreator_20211101.zip
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/9536254/DiscImageCreator_20220909.zip
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_51/redumper-2022.09.26_build51-win64.zip
- ps: appveyor DownloadFile https://archive.org/download/subdump_fua_0x28/subdump_fua_0x28.zip
- 7z x aaru-5.3.0_windows_x64.zip -oMPF\bin\Debug\net48\Programs\Aaru *
#- 7z x aaru-5.3.0_windows_x64.zip -oMPF\bin\Debug\netcoreapp3.1\Programs\Aaru *
#- 7z x aaru-5.3.0_windows_x64.zip -oMPF\bin\Debug\net5.0\Programs\Aaru *
- 7z x aaru-5.3.1_windows_x64.zip -oMPF\bin\Debug\net48\Programs\Aaru *
#- 7z x aaru-5.3.1_windows_x64.zip -oMPF\bin\Debug\net6.0\Programs\Aaru *
- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net48\Programs\DD *
#- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\netcoreapp3.1\Programs\DD *
#- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net5.0\Programs\DD *
#- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net6.0\Programs\DD *
- 7z e DiscImageCreator_20211101.zip -oMPF\bin\Debug\net48\Programs\Creator Release_ANSI\*
#- 7z e DiscImageCreator_20211101.zip -oMPF\bin\Debug\netcoreapp3.1\Programs\Creator Release_ANSI\*
#- 7z e DiscImageCreator_20211101.zip -oMPF\bin\Debug\net5.0\Programs\Creator Release_ANSI\*
- 7z e DiscImageCreator_20220909.zip -oMPF\bin\Debug\net48\Programs\Creator Release_ANSI\*
#- 7z e DiscImageCreator_20220909.zip -oMPF\bin\Debug\net6.0\Programs\Creator Release_ANSI\*
- 7z e redumper-2022.09.26_build51-win64.zip -oMPF\bin\Debug\net48\Programs\Redumper redumper-2022.09.26_build51-win64\bin\*
#- 7z e redumper-2022.09.26_build51-win64.zip -oMPF\bin\Debug\net6.0\Programs\Redumper redumper-2022.09.26_build51-win64\bin\*
- 7z e subdump_fua_0x28.zip -oMPF\bin\Debug\net48 *
- mkdir MPF\bin\Debug\net48\Programs\Subdump
- mv MPF\bin\Debug\net48\subdump_fua_0x28.exe MPF\bin\Debug\net48\Programs\Subdump\subdump.exe
#- 7z e subdump_fua_0x28.zip -oMPF\bin\Debug\netcoreapp3.1 *
#- mkdir MPF\bin\Debug\netcoreapp3.1\Programs\Subdump
#- mv MPF\bin\Debug\netcoreapp3.1\subdump_fua_0x28.exe MPF\bin\Debug\netcoreapp3.1\Programs\Subdump\subdump.exe
#- 7z e subdump_fua_0x28.zip -oMPF\bin\Debug\net5.0 *
#- mkdir MPF\bin\Debug\net5.0\Programs\Subdump
#- mv MPF\bin\Debug\net5.0\subdump_fua_0x28.exe MPF\bin\Debug\net5.0\Programs\Subdump\subdump.exe
#- 7z e subdump_fua_0x28.zip -oMPF\bin\Debug\net6.0 *
#- mkdir MPF\bin\Debug\net6.0\Programs\Subdump
#- mv MPF\bin\Debug\net6.0\subdump_fua_0x28.exe MPF\bin\Debug\net6.0\Programs\Subdump\subdump.exe
- cd MPF\bin\Debug
- 7z a MPF_net48.zip net48\*
#- 7z a MPF_netcoreapp3.1.zip netcoreapp3.1\*
#- 7z a MPF_net5.0.zip net5.0\*
#- 7z a MPF_net6.0.zip net6.0\*
- cd ..\..\..\MPF.Check\bin\Debug
- 7z a MPF-Check_net48.zip net48\*
#- 7z a MPF-Check_netcoreapp3.1.zip netcoreapp3.1\*
#- 7z a MPF-Check_net5.0.zip net5.0\*
#- 7z a MPF-Check_net6.0.zip net6.0\*
# success/failure tracking
on_success:
@@ -84,14 +80,10 @@ on_failure:
artifacts:
- path: MPF\bin\Debug\MPF_net48.zip
name: MPF (WPF UI) (.NET Framework 4.8)
#- path: MPF\bin\Debug\MPF_netcoreapp3.1.zip
# name: MPF (WPF UI) (.NET Core 3.1)
#- path: MPF\bin\Debug\MPF_net5.0.zip
# name: MPF (WPF UI) (.NET 5.0)
#- path: MPF\bin\Debug\MPF_net6.0.zip
# name: MPF (WPF UI) (.NET 6.0)
- path: MPF.Check\bin\Debug\MPF-Check_net48.zip
name: MPF Check (.NET Framework 4.8)
#- path: MPF.Check\bin\Debug\MPF-Check_netcoreapp3.1.zip
# name: MPF Check (.NET Core 3.1)
#- path: MPF.Check\bin\Debug\MPF-Check_net5.0.zip
# name: MPF Check (.NET 5.0)
#- path: MPF.Check\bin\Debug\MPF-Check_net6.0.zip
# name: MPF Check (.NET 6.0)