Compare commits

...

277 Commits
2.4 ... 2.6.4

Author SHA1 Message Date
Matt Nadareski
def499769f Bump version 2023-09-25 22:01:33 -04:00
Matt Nadareski
5e1e61a441 Fix tests that have been broken for a while 2023-09-25 21:53:34 -04:00
Matt Nadareski
4896a12ec5 Add .NET 7 as a build target (not AppVeyor) 2023-09-25 21:49:49 -04:00
Matt Nadareski
b28ad5d3cd Move MainWindow 2023-09-25 21:39:28 -04:00
Matt Nadareski
4cdbbcedf0 Move options into MainViewModel 2023-09-25 21:29:55 -04:00
Matt Nadareski
6ab63ba651 Make logger a local reference 2023-09-25 21:21:51 -04:00
Matt Nadareski
d36c5099f3 Set parent on MainViewModel init 2023-09-25 21:16:43 -04:00
Matt Nadareski
8ba8347a0c Make Options non-cloneable 2023-09-25 21:11:14 -04:00
Matt Nadareski
4c5184eac5 More decoupling App 2023-09-25 21:03:29 -04:00
Matt Nadareski
9914b94716 Fix build 2023-09-25 20:55:52 -04:00
Matt Nadareski
150e69eca5 Begin decoupling App 2023-09-25 16:58:14 -04:00
Matt Nadareski
f09923974b Move OptionsWindow 2023-09-25 15:54:18 -04:00
Matt Nadareski
d98bc28930 Move to csproj tag for internals 2023-09-25 15:47:27 -04:00
Matt Nadareski
ee4a7ab653 Move LogOutput 2023-09-25 15:43:01 -04:00
Matt Nadareski
6b20aee320 Remove log formatting code 2023-09-25 15:38:33 -04:00
Matt Nadareski
a5849325f3 Remove App references from LogViewModel 2023-09-25 15:36:50 -04:00
Matt Nadareski
86d2d83fbe Remove EnableLogFormatting 2023-09-25 15:27:25 -04:00
Matt Nadareski
308e0d6937 Remove EnableProgressProcessing 2023-09-25 15:25:45 -04:00
Matt Nadareski
3541bca2d0 Move WPFCustomMessageBox 2023-09-25 15:18:46 -04:00
Matt Nadareski
2496099532 Move Constants 2023-09-25 15:17:45 -04:00
Matt Nadareski
6001395181 Move RedumpSystemComboBoxItem 2023-09-25 15:12:05 -04:00
Matt Nadareski
fa2192a284 Not building against .NET Standard 2023-09-25 15:09:21 -04:00
Matt Nadareski
f09155936e Update changelog 2023-09-18 15:40:01 -04:00
Markus Persson
a07fca6a7b Fix timestamp (#551)
Timestamp used 12-hour clock. Fixed to 24-hour.
2023-09-18 12:36:29 -07:00
Matt Nadareski
5fe7a1dac8 Migrate to Nuget package for PIC 2023-09-13 16:50:13 -04:00
Matt Nadareski
16ed2f9595 Migrate to Nuget package for cuesheets 2023-09-13 16:36:51 -04:00
Matt Nadareski
9004d2bc7b Migrate to Nuget package for XMID 2023-09-13 16:16:47 -04:00
Matt Nadareski
cc98b38290 Remove dd for Windows (fixes #544) 2023-09-07 10:36:33 -04:00
Matt Nadareski
18ef0cddff Merge branch 'master' of https://github.com/SabreTools/MPF 2023-09-05 09:26:53 -04:00
Deterous
d9ca55d96c Fix serial parsing from #542 (#549)
Serial numbers begin at serial[5]
2023-09-05 06:26:31 -07:00
Matt Nadareski
816c94de58 Migrate to Nuget package for Redump 2023-09-05 00:08:09 -04:00
Matt Nadareski
c2ba58148a Retrofit README 2023-09-02 15:24:03 -04:00
Matt Nadareski
d9533f2448 Update changelog 2023-08-31 11:14:41 -04:00
Deterous
5126d1854c Fix PlayStation serial code region parsing (#542)
* Fix PlayStation serial code region parsing

- Do not assume S_J_ and S_P_ serials are Japanese
- Fix typo: PABX -> PBPX
- PCXC are Japanese
- PUBX are USA

* Remove S_J_ serial case

* Make S_P_ serial case comment clearer

* Retain PABX serial just in case

* Parse S_P_ serials to detect Asia/Korea

* C# style substring

* String literals
2023-08-31 08:10:42 -07:00
Matt Nadareski
6baaf132a7 Fix dumping path in DD 2023-08-29 21:07:01 -04:00
Matt Nadareski
98a30e6558 Swap order of operations for changing program 2023-08-28 12:16:53 -04:00
Matt Nadareski
0912b78568 Handle extension changing only 2023-08-28 11:24:58 -04:00
Matt Nadareski
d92a1d566d Add helper for changing dumping program from UI 2023-08-26 23:17:49 -04:00
Matt Nadareski
ff40d18ed3 Fix speed setting in Aaru (fixes #539) 2023-08-26 23:10:17 -04:00
Matt Nadareski
4d9cd85ba6 Add CD Projekt ID field (fixes #530) 2023-08-26 22:57:44 -04:00
Matt Nadareski
b6ae390cee Bump version to 2.6.3 2023-08-15 13:07:33 -04:00
Matt Nadareski
4692028cfb Update changelog 2023-08-15 12:18:52 -04:00
TheRogueArchivist
3ada7db916 Update SafeDisc Sanitization (#531)
* Update SafeDisc Sanitization

* Fix "Macrovision Protected Application" parsing
2023-08-15 09:18:01 -07:00
Matt Nadareski
aa4b2f415d Non-zero data start only for audio discs (fixes #536) 2023-08-15 12:07:29 -04:00
Matt Nadareski
56e91bf177 Add dumping date to log (fixes #533) 2023-08-15 00:47:32 -04:00
Matt Nadareski
228c752585 Make use flag required for MPF.Check (fixes #534) 2023-08-15 00:30:30 -04:00
Matt Nadareski
6ce7ccfa91 Remove _drive.txt from required UIC outputs (fixes #532) 2023-08-12 19:51:06 -04:00
Matt Nadareski
c8c98278b6 Add known .NET 6 limitations to README 2023-07-24 13:45:31 -04:00
Matt Nadareski
0fc57c58cf Update redumper to build 195 2023-07-24 13:23:15 -04:00
Matt Nadareski
3dcac28488 Bump version to 2.6.2 2023-07-24 09:40:20 -04:00
Matt Nadareski
4cd7073bf6 Clarify build instructions 2023-07-23 23:51:05 -04:00
Matt Nadareski
1af21e7aba Add build instructions to README 2023-07-23 23:48:04 -04:00
Matt Nadareski
52adcd0b46 Add *nix publish script 2023-07-23 23:44:03 -04:00
Matt Nadareski
8a91593e58 Add Windows publish script 2023-07-23 21:42:10 -04:00
Matt Nadareski
729f8273fc Fix universal hash URL generation 2023-07-23 19:16:57 -04:00
Matt Nadareski
78df6f6583 Fix .NET Framework 4.8 build 2023-07-23 16:48:44 -04:00
Matt Nadareski
dc8dae4df7 Attempt to match universal hash 2023-07-23 16:41:40 -04:00
Matt Nadareski
53db9dbf81 Modify the track count on checking 2023-07-23 15:09:46 -04:00
Matt Nadareski
22755a4af9 Skip extra tracks during checking (fixes #529) 2023-07-22 21:31:20 -04:00
Matt Nadareski
ba28b414ba Normalize old universal hash text (fixes #528) 2023-07-20 20:26:57 -04:00
Matt Nadareski
95d10ecb1e Always show extension for Redumper (fixes #525) 2023-07-20 11:35:15 -04:00
Matt Nadareski
c5ab2c747a Support LibCrypt data from Redumper 2023-07-19 17:07:59 -04:00
Matt Nadareski
476c494f4e Universal hash only for audio discs (fixes #524) 2023-07-19 13:33:14 -04:00
Matt Nadareski
758c49c1cc Ensure custom parameters properly set (fixes #523) 2023-07-19 12:51:22 -04:00
Matt Nadareski
d80ad3b3cf Bump version to 2.6.1 2023-07-19 09:36:34 -04:00
Matt Nadareski
3d06f80703 Fix comment field pulling again 2023-07-19 00:05:33 -04:00
Matt Nadareski
7344460409 Fix comment field pulling again (fixes #522) 2023-07-18 22:40:08 -04:00
Matt Nadareski
19f58d9dde Set extensions for Redumper in UI (fixes #521) 2023-07-18 20:56:08 -04:00
Matt Nadareski
ad9f39f832 Be more explicit about .NET 6 limitation 2023-07-17 13:23:00 -04:00
Matt Nadareski
d51117b058 Be more explicit about .NET 6 limitation (fixes #519) 2023-07-17 13:17:42 -04:00
Matt Nadareski
0d65d5114a Set best compression levels for log files (fixes #518) 2023-07-17 13:13:16 -04:00
Matt Nadareski
30fec3c3d0 Don't pull comment fields that auto-populate (fixes #517) 2023-07-17 10:28:37 -04:00
Matt Nadareski
8eb86fde90 Simplify Redumper error value extraction (fixes #515) 2023-07-14 16:42:47 -04:00
Matt Nadareski
7616c6b2ba Bump version to 2.6 2023-07-14 13:47:00 -04:00
Matt Nadareski
e0742cdfc7 Update Nuget packages to newest stable 2023-07-14 12:07:44 -04:00
Matt Nadareski
a19937d630 Omit pulling universal hash (fixes #513) 2023-07-12 17:29:35 -04:00
Matt Nadareski
fa92402bc5 Ensure we found the tags we're skipping (fixes #512) 2023-07-11 10:57:50 -04:00
Matt Nadareski
cdc3da5839 Reduce pulled information for Xbox and X360 (fixes #512) 2023-07-10 11:15:32 -04:00
Matt Nadareski
3430f8c1db New layerbreak takes precedence 2023-07-10 09:38:52 -04:00
Matt Nadareski
12fd55e76c Parse and format Redumper CD multisession data 2023-07-09 20:55:33 -04:00
Matt Nadareski
0013606d61 Update redumper to build 183 2023-07-09 14:43:47 -04:00
Matt Nadareski
4d520d7d63 Add ordering to disc or book type 2023-07-06 22:50:21 -04:00
Matt Nadareski
7bfe174680 Use HashSet for disc or book type 2023-07-06 15:21:37 -04:00
Matt Nadareski
4951e7bf42 Strip colons from Redumper disc key (fixes #508) 2023-06-27 15:44:55 -04:00
Matt Nadareski
d7d9c468ae UMDs are Sony discs (fixes #507) 2023-06-26 11:46:57 -04:00
Matt Nadareski
32faa33ad3 Update redumper to build 176 2023-06-26 01:06:46 -04:00
Matt Nadareski
8a5475380a Check Redumper dat section for completeness 2023-06-23 11:20:17 -04:00
Matt Nadareski
1dd5542390 Add missing Aaru error log to zip 2023-06-21 16:27:23 -04:00
Matt Nadareski
5156b89eca Normalize multi-instance site tags (fixes #505) 2023-06-19 21:21:20 -04:00
Matt Nadareski
83ea04c880 Normalize Redumper CSS outputs 2023-06-19 21:13:18 -04:00
Matt Nadareski
8695d2981e Fix non-reading loop 2023-06-18 23:18:50 -04:00
Matt Nadareski
e0c299e6f0 Adjust CSS title key parsing 2023-06-18 23:01:17 -04:00
Matt Nadareski
16ec54f389 Add support for redumper CD synonyms 2023-06-18 21:56:49 -04:00
Matt Nadareski
998bf5d5fa Update redumper to build 174 2023-06-18 21:55:57 -04:00
Matt Nadareski
07ec821e9a Hook up CSS output for testing 2023-06-18 21:15:50 -04:00
Matt Nadareski
56cf8f3574 Initial support for Redumper CSS outputs 2023-06-18 21:13:25 -04:00
Matt Nadareski
c6ebfcd6d9 Update redumper to build 173 2023-06-18 21:01:52 -04:00
Matt Nadareski
6ceffce63d Fix previous commit, clean up helpers 2023-06-12 13:42:13 -04:00
Matt Nadareski
f43aafc00d Update Redumper PS1 output parsing 2023-06-12 13:31:49 -04:00
Matt Nadareski
ab598d8377 Fix Redumper DAT/layer parsing 2023-06-11 00:57:42 -04:00
Matt Nadareski
dbd876a1c1 Fix VSCode build 2023-06-11 00:56:54 -04:00
Matt Nadareski
fd102cb56b Fix 2-layer DVD support in Redumper 2023-06-10 20:46:54 -04:00
Matt Nadareski
0afb49b657 Add TODO with notes to Redumper 2023-06-10 18:01:52 -04:00
Matt Nadareski
be980fe0c4 Add placeholder and TODOs for Redumper 2023-06-10 17:49:31 -04:00
Matt Nadareski
08b4e8d602 Support Redumper DVD layerbreak 2023-06-10 17:41:48 -04:00
Matt Nadareski
e483e1cdc6 Unblock Redumper DVD support 2023-06-10 12:03:14 -04:00
Matt Nadareski
5c2dce78e2 Update redumper to build 166 2023-06-09 23:00:20 -04:00
Matt Nadareski
26ea383775 Update to DIC 20230606 2023-06-06 11:13:29 -04:00
Matt Nadareski
64938fd7f1 Fix MCD region(s) parsing 2023-05-30 13:04:33 -04:00
Matt Nadareski
22318ee3c1 Add MCD/SS header support for Redumper (fixes #495)
This also does a replacement for all instances of `?? "";` with `?? string.Empty;`, which is more correct.
2023-05-30 12:58:37 -04:00
Matt Nadareski
b2b54a2706 Update changelog 2023-05-28 16:13:57 -04:00
fuzzball
40f04e0321 Change the fast-forward keyword (#493) 2023-05-28 13:06:49 -07:00
fuzzball
a7638b8063 Get write offset from redumper 119 (#484) 2023-05-28 13:06:20 -07:00
Matt Nadareski
2abcad2a0f Add more safety to DAT generation (fixes #492) 2023-05-28 16:05:00 -04:00
Matt Nadareski
e088b05de4 Update redumper to build 151 2023-05-19 13:10:39 -04:00
Matt Nadareski
a31d894b79 Add executable listing for XSX (fixes #491) 2023-05-10 09:26:50 -04:00
Matt Nadareski
34fae4572d Fix non-zero offset text (fixes #490) 2023-05-09 13:38:22 -04:00
Matt Nadareski
1ecf0ad1fa Fix other media type method 2023-04-26 16:00:20 -04:00
Matt Nadareski
7c7509020f Add PIC identifier to SubmissionInfo (fixes #488) 2023-04-26 10:24:30 -04:00
Matt Nadareski
7c6b118282 Add internal theme support with class (fixes #487) 2023-04-26 09:51:29 -04:00
Matt Nadareski
c154a844e3 Truncate PIC data for PS4/PS5 (fixes #486) 2023-04-26 08:35:36 -04:00
Matt Nadareski
088f1b8545 Add missing BD disc type identifier string 2023-04-26 08:29:56 -04:00
Matt Nadareski
fc3c636bdd Single file packing for .NET 6 again 2023-04-25 09:07:42 -04:00
Matt Nadareski
14c807c882 Fix subdump mkdir path in AV config 2023-04-24 21:52:52 -04:00
Matt Nadareski
d0e9c51786 Fix subdump output path in AV config 2023-04-24 21:42:20 -04:00
Matt Nadareski
b428bc0ba0 Be more specific with runtime identifiers 2023-04-24 21:31:39 -04:00
Matt Nadareski
6dbbb91438 De-indent ringcode data 2023-04-24 14:42:13 -04:00
Matt Nadareski
2066d36424 Ensure blank lines don't interfere 2023-04-24 00:01:07 -04:00
Matt Nadareski
29552cd39d Fix upcoming suppl DAT parsing 2023-04-23 23:59:44 -04:00
Matt Nadareski
640e7091cc Attempt to more accurately parse layerbreaks 2023-04-23 23:38:57 -04:00
Matt Nadareski
af12c18d2e Fix missing size for ISO data 2023-04-23 22:26:24 -04:00
Matt Nadareski
f28cf614c3 Fix info tool hash finding 2023-04-23 10:00:13 -04:00
Matt Nadareski
888cb8ec9f Support single digit subs 2023-04-23 09:45:56 -04:00
Matt Nadareski
04035ac524 Add suppl support to Xbox 2023-04-22 21:51:18 -04:00
Matt Nadareski
600374eb2d Prepare for future DIC changes
This also takes care of additional `sub` files generated for nonstandard tracks, such as Track 00 and Track AA (fixes #483)
2023-04-22 21:19:02 -04:00
Matt Nadareski
6ecb932a82 Start migrating to datafile serialization 2023-04-22 16:48:40 -04:00
Matt Nadareski
4cbc9ac109 Add datafile helper method 2023-04-22 16:25:18 -04:00
Matt Nadareski
5ee0b7345b Add datafile models 2023-04-22 15:11:14 -04:00
Matt Nadareski
cc55330fad Make TOC file optional for CD/GD 2023-04-21 13:19:27 -04:00
Matt Nadareski
6589380fdf Remove path from PS1/PS2 serial (fixes #481) 2023-04-19 11:36:13 -04:00
Matt Nadareski
bd45482bf7 Disable special SmartE handling for DIC 2023-04-17 14:06:27 -04:00
Matt Nadareski
e14c8a8f03 Ensure dumping program box can enable/disable 2023-04-13 12:34:51 -04:00
Matt Nadareski
4ac00e9a1a Comment out . handling for DIC 2023-04-13 12:33:14 -04:00
Matt Nadareski
edf983e304 Update to DIC 20230413 2023-04-13 12:27:46 -04:00
Matt Nadareski
01a69ef9b3 Add dumping program selection to main UI (#479)
* Add dumping program selection to main UI

* Fix program dropdown

* Fix lingering location

* Final changes
2023-04-11 08:15:53 -07:00
Matt Nadareski
a368afc14a UMDs always have "2 layers" (fixes #473) 2023-04-10 10:21:47 -04:00
Matt Nadareski
d83fed16f5 Handle PIC based on disc type 2023-04-10 10:14:44 -04:00
Matt Nadareski
949df08690 Add PIC models for BD (unused) 2023-04-10 10:13:58 -04:00
Matt Nadareski
ab3abb5b3e Re-enable BD33 and BD66 2023-04-10 08:57:51 -04:00
Matt Nadareski
d16e73a530 Add UltraCade 2023-04-09 21:24:47 -04:00
Matt Nadareski
3f8c55ca47 Clarify non-Redump systems 2023-04-09 21:10:08 -04:00
Matt Nadareski
2e9aaa50f9 Update redumper to build 118
This change also does the following:
- Performs some minor cleanup on `OptionsViewModel`
- Add options for enabling verbose and debug by default for Redumper (similar to Aaru)
2023-04-09 20:54:52 -04:00
Matt Nadareski
36951dc5da Resync with Redump 2023-04-08 21:32:20 -04:00
Matt Nadareski
b39c8dd738 Add DIC . notice to README 2023-04-01 21:51:02 -04:00
Matt Nadareski
a1155cf9b7 Update to DIC 20230401 2023-04-01 20:32:42 -04:00
Matt Nadareski
1d151d213e Update redumper to build 115 2023-03-31 10:30:19 -04:00
Matt Nadareski
6dc0c1438a Update changelog 2023-03-31 10:25:09 -04:00
fuzzball
8739569db6 Change the mark for fast-forwarding (#475) 2023-03-31 07:21:36 -07:00
Matt Nadareski
0dcba9ce71 Add Hasbro iON (fixes #471) 2023-03-20 15:59:16 -04:00
Matt Nadareski
8d37b85e12 Update README 2023-03-20 15:47:01 -04:00
Matt Nadareski
43cf8e1a45 Split MMI invocation in drive listing 2023-03-20 15:16:35 -04:00
Matt Nadareski
7317553483 Update redumper to build 113 2023-03-20 10:19:27 -04:00
Matt Nadareski
0b342e265c Add Windows 7 note to README 2023-03-20 10:11:39 -04:00
Matt Nadareski
08359dd45f Update changelog 2023-03-19 14:13:32 -04:00
fuzzball
a24415cae6 Increase the version of AppVeyor (#470) 2023-03-19 11:09:33 -07:00
fuzzball
59f8161308 Pull hardware info from redumper log (#469) 2023-03-19 11:09:12 -07:00
Matt Nadareski
51f955a14c Add warning to login tab 2023-03-13 14:23:54 -04:00
Matt Nadareski
ddebdef00c Update README 2023-03-13 13:25:32 -04:00
Matt Nadareski
6a4b6d613a Bump version to 2.5, update copyright year 2023-03-12 15:48:24 -04:00
Matt Nadareski
97aef5e29c Update changelog 2023-03-12 15:43:15 -04:00
fuzzball
9549178c3a Detect EOF during the search for error counts (#465) 2023-03-12 13:22:49 -04:00
Matt Nadareski
c531539c87 Readd accidentally deleted line (fixes #452) 2023-03-11 22:26:45 -05:00
Matt Nadareski
fa04461631 Return list of missing files for Redumper 2023-03-10 20:56:31 -05:00
Matt Nadareski
83d230dfe1 Handle quotes embedded 2023-03-10 17:05:18 -05:00
Matt Nadareski
ca18bbb72c Ensure drive and speed are set 2023-03-10 16:52:46 -05:00
Matt Nadareski
de0f2c1ad9 Update parameters with = handling 2023-03-10 16:43:17 -05:00
Matt Nadareski
2f0019282e Ensure min values are taken care of 2023-03-10 16:13:19 -05:00
Matt Nadareski
72ca479c9f Ensure Redumper parameters are set 2023-03-10 15:33:27 -05:00
Matt Nadareski
de3c4362e7 Fix incorrect image name setting 2023-03-10 15:10:24 -05:00
Matt Nadareski
987348fee8 Use and trim quotes for Redumper 2023-03-10 14:13:36 -05:00
Matt Nadareski
100e012fe6 Fix Redumper path generation 2023-03-10 10:27:09 -05:00
Matt Nadareski
af59ebe1ff Add TOC back as optional file 2023-03-09 13:43:10 -05:00
Matt Nadareski
0762c88655 Fix errant forward slashes 2023-03-09 10:18:23 -05:00
Matt Nadareski
bbe9c94545 Update to DIC 20230309 2023-03-09 10:07:06 -05:00
Matt Nadareski
c7efda7da8 Move drive finding inside of the try/catch 2023-03-09 09:10:29 -05:00
Matt Nadareski
5c78f9bc29 Add identifiers in more places 2023-03-08 22:49:01 -05:00
Matt Nadareski
e7c17c7b4b Remove unsupported identifiers 2023-03-08 22:30:05 -05:00
Matt Nadareski
7a2497f168 Add win7-x64 to identifier list 2023-03-08 22:25:21 -05:00
Matt Nadareski
5172f6f253 Attempt to support Windows 7 2023-03-08 22:11:32 -05:00
Matt Nadareski
9017472fa4 Set saner defaults for dumping speeds
I have to assume that new users will never look a the settings, so max is no longer a default that makes sense.
2023-03-08 11:43:05 -05:00
Matt Nadareski
6659c410c6 Minor cosmetic changes 2023-03-07 10:47:14 -05:00
fuzzball
8c7b66a2f5 Get error count from recent redumper log (#462)
* Get error count from recent redumper log

* Revert "Get error count from recent redumper log"

This reverts commit 50f5f71686.

* Fix to keep existing code format
2023-03-07 07:21:33 -08:00
Matt Nadareski
8f57d78200 Update to BurnOutSharp 2.7.0 2023-03-06 09:52:59 -05:00
Matt Nadareski
ad2ee9efa8 Update changelog 2023-03-04 22:43:44 -05:00
Wilson
192964f65a Originally intended behaviour of the Update Label button. (#459)
* Originally intended behaviour of the Update Label button. Sets up the environment and simply updates the output path and filename. No System, Media, Drive, or Speed changes. It's a very fast update meant for large lots of similar discs.

* Functionized the FastUpdateLabel code path.
Added new UI option to enable Fast Update Label feature.
Added missing function calls. Prevents UI misbehaviour.

* Fixed several glaring holes in the process. ^_^

* Removed some unecessary code.
2023-03-04 19:40:10 -08:00
Matt Nadareski
672f30af35 Handle undetected discs on refresh (fixes #461) 2023-03-03 08:40:33 -05:00
Matt Nadareski
a7a17298f2 Fix incorrect option slider display 2023-03-01 12:34:53 -05:00
Matt Nadareski
e17ad8e4a1 Handle missing extension gracefully (fixes #460) 2023-03-01 10:51:41 -05:00
Matt Nadareski
2a544676e6 Can't publish single file for UI 2023-02-25 10:52:12 -05:00
Matt Nadareski
6abfa9581d Update VSCode config files 2023-02-25 10:38:30 -05:00
Matt Nadareski
a6de548e5d Update changelog 2023-02-24 22:18:22 -05:00
fuzzball
9dc3e579b5 Get the version of redumper (#446)
* Get the version

* Correct the name of redumper (lowercase)
2023-02-24 19:15:58 -08:00
Matt Nadareski
c5be7d7f73 Trim PIC for PS3 (fixes #443) 2023-02-24 16:09:05 -05:00
Matt Nadareski
70c0da703b Use media size for type detection on .NET 6 2023-02-24 15:14:43 -05:00
Matt Nadareski
1cbe81fba6 Add Redumper non-zero data start 2023-02-24 13:40:14 -05:00
Matt Nadareski
420e356f34 Fix Redumper write offset support 2023-02-24 13:32:22 -05:00
Matt Nadareski
0e0ff0cb80 Add Redumper Universal Hash support 2023-02-24 13:00:38 -05:00
Matt Nadareski
9003d05ae2 Make .NET 6 slightly better 2023-02-24 00:17:53 -05:00
Matt Nadareski
c247225cac Remove System.Management 2023-02-23 23:36:19 -05:00
Matt Nadareski
4bccaa8ecf Introduce cross-platform MMI 2023-02-23 22:29:26 -05:00
Matt Nadareski
b579fec7ab Semi-unify drive finding 2023-02-23 21:37:36 -05:00
Matt Nadareski
37e4525c98 Fix typo in DICMultiSectorReadValue (fixes #447) 2023-02-23 21:04:56 -05:00
Matt Nadareski
2269537848 Fix other -windows places for .NET 6 2023-02-23 17:11:46 -05:00
Matt Nadareski
441fb91222 Packaging requires -windows for framework 2023-02-23 17:04:39 -05:00
Matt Nadareski
89e0473019 Handle no drives betterer 2023-02-23 16:59:37 -05:00
Matt Nadareski
3e13b35c84 Handle no drives better 2023-02-23 16:55:27 -05:00
Matt Nadareski
fd4910fc36 Attempt to handle no drives (fixes #444) 2023-02-23 16:50:00 -05:00
Matt Nadareski
964685770f Reformat CICM for old .NET versions 2023-02-23 16:43:36 -05:00
Matt Nadareski
634d64b5a1 Update Redumper to build_106 2023-02-23 16:36:11 -05:00
Matt Nadareski
19ec1c448f Enable .NET 6 Windows builds 2023-02-23 16:33:39 -05:00
Matt Nadareski
e3532d6f02 Fix Aaru removal 2023-02-23 16:26:16 -05:00
Matt Nadareski
aaf0aabb55 Remove Aaru as submodule 2023-02-23 16:11:29 -05:00
Matt Nadareski
575a5936ca Remove usage of Aaru 2023-02-23 15:47:01 -05:00
Matt Nadareski
391d265353 Use relative path output for DIC 2023-02-23 15:22:21 -05:00
Matt Nadareski
f90d19821c Update to DIC 20230201 (fixes #450) 2023-02-23 15:00:07 -05:00
Matt Nadareski
b79babf57e Add nicer failure message (fixes #453) 2023-02-23 14:32:46 -05:00
Matt Nadareski
a929bb0022 Fix relative paths for DIC (fixes #452) 2023-02-23 13:01:17 -05:00
Matt Nadareski
975eb97e27 Be smarter about old paths (fixes #455) 2023-02-23 12:31:27 -05:00
Matt Nadareski
49800cf0ed Go back to pre .NET 7 Aaru 2023-01-20 23:10:31 -08:00
Matt Nadareski
de18609e00 Add drive format (fs) to log 2023-01-20 22:55:13 -08:00
Matt Nadareski
9911446bf9 Revert submodule update 2023-01-20 22:46:02 -08:00
Matt Nadareski
a9223211ab Revert "Update internal Aaru to latest source"
This reverts commit a30ee3e6ff.
2023-01-20 22:45:28 -08:00
Matt Nadareski
54c4eeba03 Revert "Update internal Aaru to latest source"
This reverts commit a30ee3e6ff.
2023-01-20 22:39:21 -08:00
Matt Nadareski
a30ee3e6ff Update internal Aaru to latest source 2023-01-20 22:30:54 -08:00
Matt Nadareski
1fa19489a3 ReadAllText not ReadAllLines 2023-01-20 21:41:05 -08:00
John Veness
f6298dfe89 Make output file options consistent (#449)
Made "Output Protection File" to be consistent with "Output Submission JSON".
2023-01-16 10:32:52 -08:00
Matt Nadareski
79802a53f6 Update nuget packages 2023-01-06 13:54:24 -08:00
Matt Nadareski
54bf43fd6b Use msbuild for .NET Framework 4.8 2022-12-30 11:41:56 -08:00
Matt Nadareski
bb2b2f668b Use msbuild for .NET Framework 4.8 2022-12-30 11:37:14 -08:00
Matt Nadareski
ecca27e012 More strict when custom parameters editing 2022-12-30 11:24:51 -08:00
Matt Nadareski
fe0699ca48 Tweak AppVeyor, show Check 6.0 builds 2022-12-30 11:20:34 -08:00
Matt Nadareski
73b2f0921f Address some UI concerns 2022-12-30 10:59:32 -08:00
fuzzball
c56230c3af Skip during detection (#445) 2022-12-30 09:55:23 -08:00
Matt Nadareski
919b62822d Fix AppVeyor pathing 2022-12-29 22:15:27 -08:00
Matt Nadareski
21b0c9a08d Update options loader with sane defaults (fixes #442) 2022-12-24 12:59:58 -08:00
Matt Nadareski
63fafd05b3 Update to Aaru v5.3.2 LTS 2022-12-23 09:19:01 -08:00
Matt Nadareski
0a2493a953 Fix redumper error count parsing 2022-12-20 23:21:29 -08:00
Matt Nadareski
87ab750714 Fix typo in ToInternalProgram 2022-12-19 22:16:47 -08:00
Matt Nadareski
5cf3eca9eb Fix XGD media type outputs (fixes #441) 2022-12-19 12:38:18 -08:00
Matt Nadareski
b4a079b65f Remove x86 requirement for build 2022-12-19 09:41:59 -08:00
Matt Nadareski
7f2d501edf Output security sectors to info (fixes #440) 2022-12-15 22:14:31 -08:00
Matt Nadareski
c981f94092 Update AppVeyor version 2022-12-15 22:13:01 -08:00
Matt Nadareski
afba46b8b0 Fix a couple redumper things 2022-12-13 20:12:18 -08:00
Matt Nadareski
4e416df3c8 Fix incorrect SetParameters 2022-12-13 20:04:20 -08:00
Matt Nadareski
1b54e52351 Force a filename for redumper 2022-12-13 17:20:45 -08:00
Matt Nadareski
5e1568a148 Add missing redumper output file 2022-12-13 16:39:31 -08:00
Matt Nadareski
8f0ac56cf8 Reenable write offset for all CDs (fixes #439) 2022-12-13 16:16:42 -08:00
Matt Nadareski
37f0f9d4a4 Update README 2022-12-13 15:46:07 -08:00
Matt Nadareski
a48f75c704 Initial attempt at parsing redumper outputs 2022-12-13 15:35:12 -08:00
Matt Nadareski
58a683e3c9 Fix incorrect naming in Options window 2022-12-13 14:15:38 -08:00
Matt Nadareski
907637b128 Update redumper to build 81 2022-12-13 13:52:57 -08:00
Matt Nadareski
85f3e97a44 Add redumper to the UI 2022-12-13 13:47:15 -08:00
Matt Nadareski
beae9691fd Add new parameter and mode validation 2022-12-13 13:25:19 -08:00
Matt Nadareski
b7876d54cc Update redumper strings 2022-12-13 12:09:47 -08:00
Matt Nadareski
5b24223cb5 Merge branch 'master' of https://github.com/SabreTools/MPF 2022-12-13 11:57:06 -08:00
Matt Nadareski
88cadff9ef General UI Cleanup (#438)
* Update Nuget packages to newest stable

* Simplify path selection in UI

* Update changelog

* Fix broken normalization test

* Update drive info before dumping
2022-12-13 11:48:26 -08:00
Matt Nadareski
b92b39e7eb Update Nuget packages to newest stable 2022-12-12 14:45:07 -08:00
Matt Nadareski
8d29a29591 Update changelog 2022-12-04 00:08:53 -08:00
Terry Janas
daf516bf9c Populate internal serial and version from PS3 disc (#432) 2022-12-04 00:03:29 -08:00
Matt Nadareski
115d9857af Add HD-DVD to speed definitions (fixes #429) 2022-12-02 14:39:17 -08:00
Matt Nadareski
b322146e9e Add .NET 6.0 to tests, remove msbuild args 2022-11-19 23:03:26 -08:00
Matt Nadareski
b6e3c9da82 Remove .NET 6.0 from tests, add TODO 2022-11-19 22:39:42 -08:00
Matt Nadareski
6abdc632dc Remove windows from test target 2022-11-19 20:49:59 -08:00
Matt Nadareski
335ca6d5ac Add Xbox Series X short name to list 2022-11-17 18:45:16 -08:00
Matt Nadareski
8752426694 Update changelog 2022-11-17 16:24:45 -08:00
Matt Nadareski
5357ba5900 Add Xbox Series X and PS5 to list, fix Acorn 2022-11-17 16:16:05 -08:00
Matt Nadareski
f2686096bd Add _drive file to zip for UIC (fixes #425) 2022-11-07 09:03:45 -08:00
105 changed files with 13134 additions and 14271 deletions

3
.gitmodules vendored
View File

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

4
.vscode/launch.json vendored
View File

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

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"omnisharp.projectLoadTimeout": 480
}

15
.vscode/tasks.json vendored
View File

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

1
Aaru

Submodule Aaru deleted from b1ac16c551

View File

@@ -1,4 +1,273 @@
### 2.6.4 (2023-09-25)
- Add CD Projekt ID field
- Fix speed setting in Aaru
- Add helper for changing dumping program from UI
- Handle extension changing only
- Swap order of operations for changing program
- Fix dumping path in DD
- Fix PlayStation serial code region parsing (Deterous)
- Retrofit README
- Migrate to Nuget package for Redump
- Remove dd for Windows
- Migrate to Nuget package for XMID
- Migrate to Nuget package for cuesheets
- Migrate to Nuget package for PIC
- Fix timestamp (Ragowit)
- Not building against .NET Standard
- Move RedumpSystemComboBoxItem
- Move Constants
- Move WPFCustomMessageBox
- Remove EnableProgressProcessing
- Remove EnableLogFormatting
- Remove App references from LogViewModel
- Remove log formatting code
- Move LogOutput
- Move to csproj tag for internals
- Move OptionsWindow
- Begin decoupling App
- Fix build
- More decoupling App
- Make Options non-cloneable
- Set parent on MainViewModel init
- Make logger a local reference
- Move options into MainViewModel
- Move MainWindow
- Add .NET 7 as a build target (not AppVeyor)
- Fix tests that have been broken for a while
### 2.6.3 (2023-08-15)
- Update redumper to build 195
- Add known .NET 6 limitations to README
- Remove `_drive.txt` from required UIC outputs
- Make `use` flag required for MPF.Check
- Add dumping date to log
- Non-zero data start only for audio discs
- Update SafeDisc Sanitization (TheRogueArchivist)
### 2.6.2 (2023-07-25)
- Ensure custom parameters properly set
- Universal hash only for audio discs
- Support LibCrypt data from Redumper
- Always show extension for Redumper
- Normalize old universal hash text
- Skip extra tracks during checking
- Modify the track count on checking
- Attempt to match universal hash
- Fix .NET Framework 4.8 build
- Fix universal hash URL generation
- Add Windows publish script
- Add *nix publish script
- Add build instructions to README
- Clarify build instructions
### 2.6.1 (2023-07-19)
- Simplify Redumper error value extraction
- Don't pull comment fields that auto-populate
- Set best compression levels for log files
- Be more explicit about .NET 6 limitation
- Set extensions for Redumper in UI
- Fix comment field pulling again
### 2.6 (2023-07-14)
- Update README
- Add warning to login tab
- Pull hardware info from redumper log (fuzz6001)
- Increase the version of AppVeyor (fuzz6001)
- Add Windows 7 note to README
- Update redumper to build 113
- Split MMI invocation in drive listing
- Update README
- Add Hasbro iON
- Get the write offset from the latest redumper (fuzz6001)
- Update redumper to build 115
- Update to DIC 20230401
- Add DIC `.` notice to README
- Resync with Redump
- Update redumper to build 118
- Clarify non-Redump systems
- Add UltraCade
- Re-enable BD33 and BD66
- Add PIC models for BD (unused)
- Handle PIC based on disc type
- UMDs always have "2 layers"
- Add dumping program selection to main UI
- Update to DIC 20230413
- Comment out `.` handling for DIC
- Ensure dumping program box can enable/disable
- Disable special SmartE handling for DIC
- Remove path from PS1/PS2 serial
- Make TOC file optional for CD/GD
- Add datafile models
- Add datafile helper method
- Start migrating to datafile serialization
- Prepare for future DIC changes
- Add suppl support to Xbox
- Support single digit subs
- Fix info tool hash finding
- Fix missing size for ISO data
- Attempt to more accurately parse layerbreaks
- Fix upcoming suppl DAT parsing
- Ensure blank lines don't interfere
- De-indent ringcode data
- Be more specific with runtime identifiers
- Fix subdump output path in AV config
- Fix subdump mkdir path in AV config
- Single file packing for .NET 6 again
- Add missing BD disc type identifier string
- Truncate PIC data for PS4/PS5
- Add internal theme support with class
- Add PIC identifier to SubmissionInfo
- Fix other media type method
- Fix non-zero offset text
- Add executable listing for XSX
- Update redumper to build 151
- Add more safety to DAT generation
- Get write offset from redumper 119 (fuzz6001)
- Update hardware information pull from redumper (fuzz6001)
- Add MCD/SS header support for Redumper
- Fix MCD region(s) parsing
- Update to DIC 20230606
- Update redumper to build 166
- Unblock Redumper DVD support
- Support Redumper DVD layerbreak
- Add placeholder and TODOs for Redumper
- Add TODO with notes to Redumper
- Fix 2-layer DVD support in Redumper
- Fix VSCode build
- Fix Redumper DAT/layer parsing
- Update Redumper PS1 output parsing
- Fix previous commit, clean up helpers
- Update redumper to build 173
- Initial support for Redumper CSS outputs
- Hook up CSS output for testing
- Update redumper to build 174
- Add support for redumper CD synonyms
- Adjust CSS title key parsing
- Fix non-reading loop
- Normalize Redumper CSS outputs
- Normalize multi-instance site tags
- Add missing Aaru error log to zip
- Check Redumper dat section for completeness
- Update redumper to build 176
- UMDs are Sony discs
- Strip colons from Redumper disc key
- Use `HashSet` for disc or book type
- Add ordering to disc or book type
- Update redumper to build 183
- Parse and format Redumper CD multisession data
- New layerbreak takes precedence
- Reduce pulled information for Xbox and X360
- Ensure we found the tags we're skipping
- Omit pulling universal hash
- Update Nuget packages to newest stable
### 2.5 (2023-03-12)
- Add _drive file to zip for UIC
- Add Xbox Series X and PS5 to list, fix Acorn
- Add Xbox Series X short name to list
- Remove windows from test target
- Remove .NET 6.0 from tests, add TODO
- Add .NET 6.0 to tests, remove msbuild args
- Add HD-DVD to speed definitions
- Add PS3 internal serial and version parsing (tjanas)
- Update Nuget packages to newest stable
- Simplify path selection in UI
- Fix broken normalization test
- Update drive info before dumping
- Update redumper strings
- Add new parameter and mode validation
- Add redumper to the UI
- Update redumper to build 81
- Fix incorrect naming in Options window
- Initial attempt at parsing redumper outputs
- Update README
- Reenable write offset for all CDs
- Add missing redumper output file
- Force a filename for redumper
- Fix incorrect SetParameters
- Fix a couple redumper things
- Update AppVeyor version
- Output security sectors to info
- Remove x86 requirement for build
- Fix XGD media type outputs
- Fix typo in ToInternalProgram
- Fix redumper error count parsing
- Update to Aaru v5.3.2 LTS
- Update options loader with sane defaults
- Fix AppVeyor pathing
- Skip during detection
- Address some UI concerns
- Tweak AppVeyor, show Check 6.0 builds
- More strict when custom parameters editing
- Use msbuild for .NET Framework 4.8
- Update nuget packages
- ReadAllText not ReadAllLines
- Add drive format (fs) to log
- Go back to pre .NET 7 Aaru
- Be smarter about old paths
- Fix relative paths for DIC
- Add nicer failure message
- Update to DIC 20230201
- Use relative path output for DIC
- Remove usage of Aaru
- Remove Aaru as submodule
- Fix Aaru removal
- Enable .NET 6 Windows builds
- Update Redumper to build_106
- Reformat CICM for old .NET versions
- Attempt to handle no drives
- Handle no drives better
- Handle no drives betterer
- Packaging requires `-windows` for framework
- Fix other `-windows` places for .NET 6
- Fix typo in DICMultiSectorReadValue
- Semi-unify drive finding
- Introduce cross-platform MMI
- Remove System.Management
- Add Redumper Universal Hash support
- Fix Redumper write offset support
- Add Redumper non-zero data start
- Use media size for type detection on .NET 6
- Trim PIC for PS3
- Get the version of redumper (fuzz6001)
- Update VSCode config files
- Can't publish single file for UI
- Handle missing extension gracefully
- Fix incorrect option slider display
- Handle undetected discs on refresh
- Originally intended behaviour of the Update Label button (IcySon55)
- Update to BurnOutSharp 2.7.0
- Get error count from recent redumper log (fuzz6001)
- Minor cosmetic changes
- Set saner defaults for dumping speeds
- Attempt to support Windows 7
- Add `win7-x64` to identifier list
- Remove unsupported identifiers
- Add identifiers in more places
- Move drive finding inside of the try/catch
- Update to DIC 20230309
- Fix errant forward slashes
- Add TOC back as optional file
- Fix Redumper path generation
- Use and trim quotes for Redumper
- Fix incorrect image name setting
- Ensure Redumper parameters are set
- Ensure min values are taken care of
- Update parameters with `=` handling
- Ensure drive and speed are set
- Handle quotes embedded
- Return list of missing files for Redumper
- Readd accidentally deleted line
- Detect EOF during the search for error counts (fuzz6001)
### 2.4 (2022-10-26)
- Update to DIC 20211001
- Fix Redump disc title pulling
- Add /mr default flag options
@@ -178,6 +447,7 @@
- Fix offset formatting (fuzz6001)
### 2.3 (2022-02-05)
- Start overhauling Redump information pulling, again
- Add internal structure for special site codes
- Add new tabs for special site information
@@ -262,6 +532,7 @@
- Adjust paths for DIC just before dumping
### 2.2 (2021-12-30)
- Fix Saturn header finding
- Add Pocket PC support
- Add HD-DVD-Video support
@@ -313,6 +584,7 @@
- Add safety around volume labels
### 2.1 (2021-07-22)
- Enum, no more
- Sony works backward
- Add experimental dark mode
@@ -342,6 +614,7 @@
- Update to BurnOutSharp 1.7.0
### 2.0 (2021-04-23)
- Rename DICUI to Media Preservation Frontend (MPF)
- Add handling for BEh drive _mainInfo.txt changes
- Fix multiline regex fields during info pulling
@@ -396,6 +669,7 @@
- Update to BurnOutSharp 1.6.1
### 1.18 (2020-11-10)
- Add more information extraction and generation for Aaru
- Remove instances of CD Check from copy protection (again, sorry)
- Fix multiline submission info outputs
@@ -420,6 +694,7 @@
- Added HD-DVD-Video detection
### 1.17.1 (2020-09-14)
- Shuffled some shared, internal UI variables
- Synced WPF and Avalonia UI internals
- Made the disc information window less prone to bugs
@@ -427,6 +702,7 @@
- Added support for old(?) DIC flags: `/fix` and `/re`
### 1.17 (2020-09-12)
- Updated to Aaru version 5.1
- Updated to BurnOutSharp version 1.4.0
- Updated to DIC version 20200716
@@ -586,7 +862,7 @@
- edccchk now run on all CD-Roms
- Discs unsupported by Windows are now regonized
- Extra \ when accepting default save has been removed.
### 1.02b (2018-05-18)
- Added missing DLL
@@ -599,4 +875,4 @@
### 1.01d (2018-05-18)
-Combine IBM PC-CD options, misc fixes.
-Combine IBM PC-CD options, misc fixes.

View File

@@ -1,18 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<Title>MPF Check</Title>
<AssemblyName>MPF.Check</AssemblyName>
<Description>Validator for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2022</Copyright>
<Copyright>Copyright (c)2019-2023</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
@@ -28,9 +26,15 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.3.4" GeneratePathProperty="true">
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -41,9 +45,4 @@
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
</Project>

View File

@@ -5,8 +5,8 @@ using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.Library;
using RedumpLib.Data;
using RedumpLib.Web;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
namespace MPF.Check
{
@@ -24,7 +24,12 @@ namespace MPF.Check
return;
// Loop through and process options
(Options options, string path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
(Core.Data.Options options, string path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
if (options.InternalProgram == InternalProgram.NONE)
{
DisplayHelp("A program name needs to be provided");
return;
}
// Make new Progress objects
var resultProgress = new Progress<Result>();
@@ -33,7 +38,7 @@ namespace MPF.Check
protectionProgress.ProgressChanged += ProgressUpdated;
// Validate the supplied credentials
#if NET48 || NETSTANDARD2_1
#if NET48
(bool? _, string message) = RedumpWebClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword);
#else
(bool? _, string message) = RedumpHttpClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword).ConfigureAwait(false).GetAwaiter().GetResult();
@@ -59,7 +64,7 @@ namespace MPF.Check
if (!string.IsNullOrWhiteSpace(path))
drive = Drive.Create(null, path);
var env = new DumpEnvironment(options, "", filepath, drive, knownSystem, mediaType, null);
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, internalProgram: null, parameters: null);
// Finally, attempt to do the output dance
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();

View File

@@ -6,7 +6,7 @@ using System.Reflection;
using IMAPI2;
#endif
using MPF.Core.Data;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Converters
{
@@ -73,56 +73,6 @@ 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
@@ -148,8 +98,7 @@ namespace MPF.Core.Converters
if (!LongNameMethods.TryGetValue(sourceType, out MethodInfo method))
{
method = typeof(RedumpLib.Data.Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
method = typeof(SabreTools.RedumpLib.Data.Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
if (method == null)
method = typeof(EnumConverter).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
@@ -181,12 +130,10 @@ namespace MPF.Core.Converters
case InternalProgram.Aaru:
return "Aaru";
case InternalProgram.DD:
return "dd";
case InternalProgram.DiscImageCreator:
return "DiscImageCreator";
case InternalProgram.Redumper:
return "Redumper";
return "redumper";
#endregion
@@ -233,9 +180,7 @@ namespace MPF.Core.Converters
case "dicreator":
case "discimagecreator":
return InternalProgram.DiscImageCreator;
case "dd":
return InternalProgram.DD;
case "rd:":
case "rd":
case "redumper":
return InternalProgram.Redumper;

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
@@ -19,6 +19,7 @@ namespace MPF.Core.Data
// Private lists of known drive speed ranges
private static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
private static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
private static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
private static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
private static IReadOnlyList<int> Unknown { get; } = new List<int> { 1 };
@@ -35,10 +36,11 @@ namespace MPF.Core.Data
case MediaType.GDROM:
return CD;
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
return DVD;
case MediaType.HDDVD:
return HDDVD;
case MediaType.BluRay:
return BD;
default:
@@ -83,6 +85,7 @@ namespace MPF.Core.Data
// Automatic Information
public const string DumpingProgramField = "Dumping Program";
public const string DumpingDateField = "Date";
public const string DumpingDriveManufacturer = "Manufacturer";
public const string DumpingDriveModel = "Model";
public const string DumpingDriveFirmware = "Firmware";

View File

@@ -2,17 +2,13 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Generic;
using MPF.Core.Converters;
using MPF.Core.Utilities;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
#if NETFRAMEWORK
using System.Management;
using IMAPI2;
#else
using Aaru.CommonTypes.Enums;
using Aaru.Core.Media.Info;
using Aaru.Decoders.SCSI.MMC;
using Aaru.Devices;
#endif
namespace MPF.Core.Data
@@ -149,6 +145,12 @@ namespace MPF.Core.Data
this.TotalSize = driveInfo.TotalSize;
this.VolumeLabel = driveInfo.VolumeLabel;
}
else
{
this.DriveFormat = string.Empty;
this.TotalSize = default;
this.VolumeLabel = string.Empty;
}
}
#region Public Functionality
@@ -170,7 +172,83 @@ namespace MPF.Core.Data
/// </summary>
/// <returns></returns>
public (MediaType?, string) GetMediaType()
=> GetMediaType(this.Name, this.InternalDriveType);
{
// Take care of the non-optical stuff first
// TODO: See if any of these can be more granular, like Optical is
if (this.InternalDriveType == Data.InternalDriveType.Floppy)
return (MediaType.FloppyDisk, null);
else if (this.InternalDriveType == Data.InternalDriveType.HardDisk)
return (MediaType.HardDisk, null);
else if (this.InternalDriveType == Data.InternalDriveType.Removable)
return (MediaType.FlashDrive, null);
#if NET6_0_OR_GREATER
else
return GetMediaTypeFromSize();
#endif
// Get the current drive information
string deviceId = null;
bool loaded = false;
try
{
// Get the device ID first
CimSession session = CimSession.Create(null);
var collection = session.QueryInstances("root\\CIMV2", "WQL", $"SELECT * FROM Win32_CDROMDrive WHERE Id = '{this.Letter}:\'");
foreach (CimInstance instance in collection)
{
CimKeyedCollection<CimProperty> properties = instance.CimInstanceProperties;
deviceId = (string)properties["DeviceID"]?.Value;
loaded = (bool)properties["MediaLoaded"]?.Value;
}
// If we got no valid device, we don't care and just return
if (deviceId == null)
return (null, "Device could not be found");
else if (!loaded)
return (null, "Device is not reporting media loaded");
#if NETFRAMEWORK
MsftDiscMaster2 discMaster = new MsftDiscMaster2();
deviceId = deviceId.ToLower().Replace('\\', '#').Replace('/', '#');
string id = null;
foreach (var disc in discMaster)
{
if (disc.ToString().Contains(deviceId))
id = disc.ToString();
}
// If we couldn't find the drive, we don't care and return
if (id == null)
return (null, "Device ID could not be found");
// Create the required objects for reading from the drive
MsftDiscRecorder2 recorder = new MsftDiscRecorder2();
recorder.InitializeDiscRecorder(id);
MsftDiscFormat2Data dataWriter = new MsftDiscFormat2Data();
// If the recorder is not supported, just return
if (!dataWriter.IsRecorderSupported(recorder))
return (null, "IMAPI2 recorder not supported");
// Otherwise, set the recorder to get information from
dataWriter.Recorder = recorder;
var media = dataWriter.CurrentPhysicalMediaType;
return (media.IMAPIToMediaType(), null);
#else
return (null, "IMAPI2 recorder not supported");
#endif
}
catch (Exception ex)
{
return (null, ex.Message);
}
}
/// <summary>
/// Get the current system from drive
@@ -216,7 +294,7 @@ namespace MPF.Core.Data
{
return RedumpSystem.MattelFisherPriceiXL;
}
// Microsoft Xbox 360
try
{
@@ -379,7 +457,7 @@ namespace MPF.Core.Data
}
catch { }
#endregion
#endregion
// Default return
return defaultValue;
@@ -491,7 +569,19 @@ namespace MPF.Core.Data
#region Helpers
#if NETFRAMEWORK
/// <summary>
/// Get the media type for a device path based on size
/// </summary>
/// <returns>MediaType, null on error</returns>
private (MediaType?, string) GetMediaTypeFromSize()
{
if (this.TotalSize >= 0 && this.TotalSize < 800_000_000 && this.DriveFormat == "CDFS")
return (MediaType.CDROM, null);
else if (this.TotalSize >= 400_000_000 && this.TotalSize <= 8_540_000_000 && this.DriveFormat == "UDF")
return (MediaType.DVD, null);
else
return (MediaType.BluRay, null);
}
/// <summary>
/// Get all current attached Drives
@@ -511,26 +601,38 @@ namespace MPF.Core.Data
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();
// TODO: Reduce reliance on `DriveInfo`
// https://github.com/aaru-dps/Aaru/blob/5164a154e2145941472f2ee0aeb2eff3338ecbb3/Aaru.Devices/Windows/ListDevices.cs#L66
// Get the floppy drives and set the flag from removable
// Create an output drive list
List<Drive> drives = new List<Drive>();
// Get all standard supported drive types
try
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_LogicalDisk");
drives = DriveInfo.GetDrives()
.Where(d => desiredDriveTypes.Contains(d.DriveType))
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name))
.ToList();
}
catch
{
return drives;
}
var collection = searcher.Get();
foreach (ManagementObject queryObj in collection)
// Find and update all floppy drives
try
{
CimSession session = CimSession.Create(null);
var collection = session.QueryInstances("root\\CIMV2", "WQL", "SELECT * FROM Win32_LogicalDisk");
foreach (CimInstance instance in collection)
{
uint? mediaType = (uint?)queryObj["MediaType"];
CimKeyedCollection<CimProperty> properties = instance.CimInstanceProperties;
uint? mediaType = properties["MediaType"]?.Value as uint?;
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
{
char devId = queryObj["DeviceID"].ToString()[0];
char devId = (properties["Caption"].Value as string)[0];
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
}
}
@@ -543,317 +645,6 @@ namespace MPF.Core.Data
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

@@ -20,7 +20,6 @@
// Dumping support
Aaru,
DD,
DiscImageCreator,
Redumper,

View File

@@ -2,13 +2,13 @@
using System.Collections;
using System.Collections.Generic;
using MPF.Core.Converters;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
public class Options : IDictionary<string, string>, ICloneable
public class Options : IDictionary<string, string>
{
private readonly Dictionary<string, string> _settings;
private Dictionary<string, string> _settings;
#region Internal Program
@@ -30,21 +30,12 @@ namespace MPF.Core.Data
set { _settings["DiscImageCreatorPath"] = value; }
}
/// <summary>
/// Path to dd for Windows
/// </summary>
public string DDPath
{
get { return GetStringSetting(_settings, "DDPath", "Programs\\DD\\dd.exe"); }
set { _settings["DDPath"] = value; }
}
/// <summary>
/// Path to Redumper
/// </summary>
public string RedumperPath
{
get { return GetStringSetting(_settings, "RedumperPath", "Programs\\RedumperPath\\redumper.exe"); }
get { return GetStringSetting(_settings, "RedumperPath", "Programs\\Redumper\\redumper.exe"); }
set { _settings["RedumperPath"] = value; }
}
@@ -87,6 +78,15 @@ namespace MPF.Core.Data
set { _settings["CheckForUpdatesOnStartup"] = value.ToString(); }
}
/// <summary>
/// Fast update label - Skips disc checks and updates path only
/// </summary>
public bool FastUpdateLabel
{
get { return GetBooleanSetting(_settings, "FastUpdateLabel", false); }
set { _settings["FastUpdateLabel"] = value.ToString(); }
}
/// <summary>
/// Default output path for dumps
/// </summary>
@@ -132,7 +132,7 @@ namespace MPF.Core.Data
/// </summary>
public int PreferredDumpSpeedCD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedCD", 72); }
get { return GetInt32Setting(_settings, "PreferredDumpSpeedCD", 24); }
set { _settings["PreferredDumpSpeedCD"] = value.ToString(); }
}
@@ -141,16 +141,25 @@ namespace MPF.Core.Data
/// </summary>
public int PreferredDumpSpeedDVD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedDVD", 24); }
get { return GetInt32Setting(_settings, "PreferredDumpSpeedDVD", 16); }
set { _settings["PreferredDumpSpeedDVD"] = value.ToString(); }
}
/// <summary>
/// Default HD-DVD dumping speed
/// </summary>
public int PreferredDumpSpeedHDDVD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedHDDVD", 8); }
set { _settings["PreferredDumpSpeedHDDVD"] = value.ToString(); }
}
/// <summary>
/// Default BD dumping speed
/// </summary>
public int PreferredDumpSpeedBD
{
get { return GetInt32Setting(_settings, "PreferredDumpSpeedBD", 16); }
get { return GetInt32Setting(_settings, "PreferredDumpSpeedBD", 8); }
set { _settings["PreferredDumpSpeedBD"] = value.ToString(); }
}
@@ -222,7 +231,7 @@ namespace MPF.Core.Data
public int DICMultiSectorReadValue
{
get { return GetInt32Setting(_settings, "DICMultiSectorReadValue", 0); }
set { _settings["DICMultiSectorRead"] = value.ToString(); }
set { _settings["DICMultiSectorReadValue"] = value.ToString(); }
}
/// <summary>
@@ -286,6 +295,37 @@ namespace MPF.Core.Data
#endregion
#region Redumper
/// <summary>
/// Enable debug output while dumping by default
/// </summary>
public bool RedumperEnableDebug
{
get { return GetBooleanSetting(_settings, "RedumperEnableDebug", false); }
set { _settings["RedumperEnableDebug"] = value.ToString(); }
}
/// <summary>
/// Enable verbose output while dumping by default
/// </summary>
public bool RedumperEnableVerbose
{
get { return GetBooleanSetting(_settings, "RedumperEnableVerbose", false); }
set { _settings["RedumperEnableVerbose"] = value.ToString(); }
}
/// <summary>
/// Default number of rereads
/// </summary>
public int RedumperRereadCount
{
get { return GetInt32Setting(_settings, "RedumperRereadCount", 20); }
set { _settings["RedumperRereadCount"] = value.ToString(); }
}
#endregion
#region Extra Dumping Options
/// <summary>
@@ -480,30 +520,6 @@ namespace MPF.Core.Data
set { _settings["OpenLogWindowAtStartup"] = value.ToString(); }
}
/// <summary>
/// Enable fancy formatting of log statements
/// Disables EnableProgressProcessing if disabled
/// </summary>
/// <remarks>
/// This is mainly for outputting redirected console outputs. Not many
/// other bits of the logs include any specially handled outputs.
/// </remarks>
public bool EnableLogFormatting
{
get { return GetBooleanSetting(_settings, "EnableLogFormatting", false); }
set { _settings["EnableLogFormatting"] = value.ToString(); }
}
/// <summary>
/// Enable progress bar updating based on log text
/// Disabled if EnableLogFormatting is disabled
/// </summary>
public bool EnableProgressProcessing
{
get { return GetBooleanSetting(_settings, "EnableProgressProcessing", false); }
set { _settings["EnableProgressProcessing"] = value.ToString(); }
}
#endregion
#region Redump Login Information
@@ -538,11 +554,21 @@ namespace MPF.Core.Data
}
/// <summary>
/// Create a clone of the object
/// Constructor taking an existing Options object
/// </summary>
public object Clone()
/// <param name="source"></param>
public Options(Options source)
{
return new Options(new Dictionary<string, string>(_settings));
_settings = new Dictionary<string, string>(source._settings);
}
/// <summary>
/// Set all fields from an existing Options object
/// </summary>
/// <param name="source"></param>
public void SetFromExisting(Options source)
{
_settings = new Dictionary<string, string>(source._settings);
}
#region Helpers

View File

@@ -1,32 +1,10 @@
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
/// <summary>
/// Contains information specific to an XGD disc
/// </summary>
/// <remarks>
/// XGD1 XMID Format Information:
///
/// AABBBCCD
/// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details)
/// - BBB => Game ID
/// - CC => Version number
/// - D => Region identifier (see GetRegion for details)
///
/// XGD2/3 XeMID Format Information:
///
/// AABCCCDDEFFGHH(IIIIIIII)
/// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details)
/// - B => Platform identifier; 2 indicates Xbox 360.
/// - CCC => Game ID
/// - DD => SKU number (unique per SKU of a title)
/// - E => Region identifier (see GetRegion for details)
/// - FF => Base version; usually starts at 01 (can be 1 or 2 characters)
/// - G => Media type identifier (see GetMediaSubtype for details)
/// - HH => Disc number stored in [disc number][total discs] format
/// - IIIIIIII => 8-hex-digit certification submission identifier; usually on test discs only
/// </remarks>
public class XgdInfo
{
#region Fields
@@ -39,75 +17,17 @@ namespace MPF.Core.Data
/// <summary>
/// Raw XMID/XeMID string that all other information is derived from
/// </summary>
public string XMID { get; private set; }
public string RawXMID { get; private set; }
/// <summary>
/// 2-character publisher identifier
/// XGD1 XMID
/// </summary>
public string PublisherIdentifier { get; private set; }
public SabreTools.Models.Xbox.XMID XMID { get; private set; }
/// <summary>
/// Platform disc is made for, 2 indicates Xbox 360
/// XGD2/3 XeMID
/// </summary>
public char? PlatformIdentifier { get; private set; }
/// <summary>
/// Game ID
/// </summary>
public string GameID { get; private set; }
/// <summary>
/// For XGD1: Internal version number
/// For XGD2/3: Title-specific SKU
/// </summary>
public string SKU { get; private set; }
/// <summary>
/// Region identifier character
/// </summary>
public char RegionIdentifier { get; private set; }
/// <summary>
/// Base version of executables, usually starts at 01
/// </summary>
/// <remarks>
/// TODO: Check if this is always 2 characters for XGD2/3
/// </remarks>
public string BaseVersion { get; private set; }
/// <summary>
/// Media subtype identifier
/// </summary>
public char MediaSubtypeIdentifier { get; private set; }
/// <summary>
/// Disc number stored in [disc number][total discs] format
/// </summary>
public string DiscNumberIdentifier { get; private set; }
/// <summary>
/// 8-hex-digit certification submission identifier; usually on test discs only
/// </summary>
public string CertificationSubmissionIdentifier { get; private set; }
#endregion
#region Auto-Generated Information
/// <summary>
/// Human-readable name derived from the publisher identifier
/// </summary>
public string PublisherName => GetPublisher(this.PublisherIdentifier);
/// <summary>
/// Internally represented region
/// </summary>
public Region? InternalRegion => GetRegion(this.RegionIdentifier);
/// <summary>
/// Human-readable subtype derived from the media identifier
/// </summary>
public string MediaSubtype => GetMediaSubtype(this.MediaSubtypeIdentifier);
public SabreTools.Models.Xbox.XeMID XeMID { get; private set; }
#endregion
@@ -115,24 +35,23 @@ namespace MPF.Core.Data
/// Populate a set of XGD information from a Master ID (XMID/XeMID) string
/// </summary>
/// <param name="xmid">XMID/XeMID string representing the DMI information</param>
/// <param name="validate">True if value validation should be performed, false otherwise</param>
public XgdInfo(string xmid, bool validate = false)
public XgdInfo(string xmid)
{
this.Initialized = false;
if (string.IsNullOrWhiteSpace(xmid))
return;
this.XMID = xmid.TrimEnd('\0');
if (string.IsNullOrWhiteSpace(this.XMID))
this.RawXMID = xmid.TrimEnd('\0');
if (string.IsNullOrWhiteSpace(this.RawXMID))
return;
// XGD1 information is 8 characters
if (this.XMID.Length == 8)
this.Initialized = ParseXGD1XMID(this.XMID, validate);
if (this.RawXMID.Length == 8)
this.Initialized = ParseXGD1XMID(this.RawXMID);
// XGD2/3 information is semi-variable length
else if (this.XMID.Length == 13 || this.XMID.Length == 14 || this.XMID.Length == 21 || this.XMID.Length == 22)
this.Initialized = ParseXGD23XeMID(this.XMID, validate);
else if (this.RawXMID.Length == 13 || this.RawXMID.Length == 14 || this.RawXMID.Length == 21 || this.RawXMID.Length == 22)
this.Initialized = ParseXGD23XeMID(this.RawXMID);
}
/// <summary>
@@ -147,12 +66,12 @@ namespace MPF.Core.Data
try
{
// XGD1 doesn't use PlatformIdentifier
if (this.PlatformIdentifier == null)
return $"{this.PublisherIdentifier}-{this.GameID}";
if (XMID != null)
return $"{XMID.PublisherIdentifier}-{XMID.GameID}";
// XGD2/3 uses a specific identifier
else if (this.PlatformIdentifier == '2')
return $"{this.PublisherIdentifier}-{this.PlatformIdentifier}{this.GameID}";
else if (XeMID?.PlatformIdentifier == '2')
return $"{XeMID.PublisherIdentifier}-{XeMID.PlatformIdentifier}{XeMID.GameID}";
return null;
}
@@ -175,12 +94,12 @@ namespace MPF.Core.Data
try
{
// XGD1 doesn't use PlatformIdentifier
if (this.PlatformIdentifier == null)
return $"1.{this.SKU}";
if (XMID != null)
return $"1.{XMID.VersionNumber}";
// XGD2/3 uses a specific identifier
else if (this.PlatformIdentifier == '2')
return $"1.{this.SKU}";
else if (XeMID?.PlatformIdentifier == '2')
return $"1.{XeMID.SKU}";
return null;
}
@@ -193,217 +112,55 @@ namespace MPF.Core.Data
/// <summary>
/// Parse an XGD1 XMID string
/// </summary>
/// <param name="xmid">XMID string to attempt to parse</param>
/// <param name="validate">True if value validation should be performed, false otherwise</param>
/// <param name="rawXmid">XMID string to attempt to parse</param>
/// <returns>True if the XMID could be parsed, false otherwise</returns>
private bool ParseXGD1XMID(string xmid, bool validate)
private bool ParseXGD1XMID(string rawXmid)
{
if (xmid == null || xmid.Length != 8)
return false;
try
{
var xmid = new SabreTools.Serialization.Files.XMID().Deserialize(rawXmid);
if (xmid == null)
return false;
this.PublisherIdentifier = xmid.Substring(0, 2);
if (validate && string.IsNullOrEmpty(this.PublisherName))
this.XMID = xmid;
return true;
}
catch
{
return false;
this.GameID = xmid.Substring(2, 3);
this.SKU = xmid.Substring(5, 2);
this.RegionIdentifier = xmid[7];
if (validate && this.InternalRegion == null)
return false;
return true;
}
}
/// <summary>
/// Parse an XGD2/3 XeMID string
/// </summary>
/// <param name="xemid">XeMID string to attempt to parse</param>
/// <param name="validate">True if value validation should be performed, false otherwise</param>
/// <param name="rawXemid">XeMID string to attempt to parse</param>
/// <returns>True if the XeMID could be parsed, false otherwise</returns>
private bool ParseXGD23XeMID(string xemid, bool validate)
private bool ParseXGD23XeMID(string rawXemid)
{
if (xemid == null
|| (xemid.Length != 13 && xemid.Length != 14
&& xemid.Length != 21 && xemid.Length != 22))
return false;
this.PublisherIdentifier = xemid.Substring(0, 2);
if (validate && string.IsNullOrEmpty(this.PublisherName))
return false;
this.PlatformIdentifier = xemid[2];
if (validate && this.PlatformIdentifier != '2')
return false;
this.GameID = xemid.Substring(3, 3);
this.SKU = xemid.Substring(6, 2);
this.RegionIdentifier = xemid[8];
if (validate && this.InternalRegion == null)
return false;
if (xemid.Length == 13 || xemid.Length == 21)
try
{
this.BaseVersion = xemid.Substring(9, 1);
this.MediaSubtypeIdentifier = xemid[10];
if (validate && string.IsNullOrEmpty(this.MediaSubtype))
var xemid = new SabreTools.Serialization.Files.XeMID().Deserialize(rawXemid);
if (xemid == null)
return false;
this.DiscNumberIdentifier = xemid.Substring(11, 2);
this.XeMID = xemid;
return true;
}
else if (xemid.Length == 14 || xemid.Length == 22)
catch
{
this.BaseVersion = xemid.Substring(9, 2);
this.MediaSubtypeIdentifier = xemid[11];
if (validate && string.IsNullOrEmpty(this.MediaSubtype))
return false;
this.DiscNumberIdentifier = xemid.Substring(12, 2);
return false;
}
if (xemid.Length == 21)
this.CertificationSubmissionIdentifier = xemid.Substring(13);
else if (xemid.Length == 22)
this.CertificationSubmissionIdentifier = xemid.Substring(14);
return true;
}
#region Helpers
/// <summary>
/// Determine the XGD type based on the XGD2/3 media type identifier character
/// </summary>
/// <param name="mediaTypeIdentifier">Character denoting the media type</param>
/// <returns>Media subtype as a string, if possible</returns>
private static string GetMediaSubtype(char mediaTypeIdentifier)
{
switch (mediaTypeIdentifier)
{
case 'F': return "XGD3";
case 'X': return "XGD2";
case 'Z': return "Games on Demand / Marketplace Demo";
default: return null;
}
}
/// <summary>
/// Get the full name of the publisher from the 2-character identifier
/// </summary>
/// <param name="publisherIdentifier">Case-sensitive 2-character identifier</param>
/// <returns>Publisher name, if possible</returns>
/// <see cref="https://xboxdevwiki.net/Xbe#Title_ID"/>
private static string GetPublisher(string publisherIdentifier)
{
switch (publisherIdentifier)
{
case "AC": return "Acclaim Entertainment";
case "AH": return "ARUSH Entertainment";
case "AQ": return "Aqua System";
case "AS": return "ASK";
case "AT": return "Atlus";
case "AV": return "Activision";
case "AY": return "Aspyr Media";
case "BA": return "Bandai";
case "BL": return "Black Box";
case "BM": return "BAM! Entertainment";
case "BR": return "Broccoli Co.";
case "BS": return "Bethesda Softworks";
case "BU": return "Bunkasha Co.";
case "BV": return "Buena Vista Games";
case "BW": return "BBC Multimedia";
case "BZ": return "Blizzard";
case "CC": return "Capcom";
case "CK": return "Kemco Corporation"; // TODO: Confirm
case "CM": return "Codemasters";
case "CV": return "Crave Entertainment";
case "DC": return "DreamCatcher Interactive";
case "DX": return "Davilex";
case "EA": return "Electronic Arts (EA)";
case "EC": return "Encore inc";
case "EL": return "Enlight Software";
case "EM": return "Empire Interactive";
case "ES": return "Eidos Interactive";
case "FI": return "Fox Interactive";
case "FS": return "From Software";
case "GE": return "Genki Co.";
case "GV": return "Groove Games";
case "HE": return "Tru Blu (Entertainment division of Home Entertainment Suppliers)";
case "HP": return "Hip games";
case "HU": return "Hudson Soft";
case "HW": return "Highwaystar";
case "IA": return "Mad Catz Interactive";
case "IF": return "Idea Factory";
case "IG": return "Infogrames";
case "IL": return "Interlex Corporation";
case "IM": return "Imagine Media";
case "IO": return "Ignition Entertainment";
case "IP": return "Interplay Entertainment";
case "IX": return "InXile Entertainment"; // TODO: Confirm
case "JA": return "Jaleco";
case "JW": return "JoWooD";
case "KB": return "Kemco"; // TODO: Confirm
case "KI": return "Kids Station Inc."; // TODO: Confirm
case "KN": return "Konami";
case "KO": return "KOEI";
case "KU": return "Kobi and / or GAE (formerly Global A Entertainment)"; // TODO: Confirm
case "LA": return "LucasArts";
case "LS": return "Black Bean Games (publishing arm of Leader S.p.A.)";
case "MD": return "Metro3D";
case "ME": return "Medix";
case "MI": return "Microïds";
case "MJ": return "Majesco Entertainment";
case "MM": return "Myelin Media";
case "MP": return "MediaQuest"; // TODO: Confirm
case "MS": return "Microsoft Game Studios";
case "MW": return "Midway Games";
case "MX": return "Empire Interactive"; // TODO: Confirm
case "NK": return "NewKidCo";
case "NL": return "NovaLogic";
case "NM": return "Namco";
case "OX": return "Oxygen Interactive";
case "PC": return "Playlogic Entertainment";
case "PL": return "Phantagram Co., Ltd.";
case "RA": return "Rage";
case "SA": return "Sammy";
case "SC": return "SCi Games";
case "SE": return "SEGA";
case "SN": return "SNK";
case "SS": return "Simon & Schuster";
case "SU": return "Success Corporation";
case "SW": return "Swing! Deutschland";
case "TA": return "Takara";
case "TC": return "Tecmo";
case "TD": return "The 3DO Company (or just 3DO)";
case "TK": return "Takuyo";
case "TM": return "TDK Mediactive";
case "TQ": return "THQ";
case "TS": return "Titus Interactive";
case "TT": return "Take-Two Interactive Software";
case "US": return "Ubisoft";
case "VC": return "Victor Interactive Software";
case "VN": return "Vivendi Universal (just took Interplays publishing rights)"; // TODO: Confirm
case "VU": return "Vivendi Universal Games";
case "VV": return "Vivendi Universal Games"; // TODO: Confirm
case "WE": return "Wanadoo Edition";
case "WR": return "Warner Bros. Interactive Entertainment"; // TODO: Confirm
case "XI": return "XPEC Entertainment and Idea Factory";
case "XK": return "Xbox kiosk disk?"; // TODO: Confirm
case "XL": return "Xbox special bundled or live demo disk?"; // TODO: Confirm
case "XM": return "Evolved Games"; // TODO: Confirm
case "XP": return "XPEC Entertainment";
case "XR": return "Panorama";
case "YB": return "YBM Sisa (South-Korea)";
case "ZD": return "Zushi Games (formerly Zoo Digital Publishing)";
default: return null;
}
}
/// <summary>
/// Determine the region based on the XGD serial character
/// </summary>
/// <param name="region">Character denoting the region</param>
/// <returns>Region, if possible</returns>
private static Region? GetRegion(char region)
public static Region? GetRegion(char region)
{
switch (region)
{

View File

@@ -1,14 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2022</Copyright>
<Copyright>Copyright (c)2019-2023</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -42,23 +40,12 @@
</ItemGroup>
<ItemGroup>
<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.Configuration.ConfigurationManager" Version="6.0.1" />
<PackageReference Include="System.Management" Version="6.0.0" />
<PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.Models" Version="1.1.2" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="SabreTools.Serialization" Version="1.1.2" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic;
using MPF.Core.Converters;
using MPF.Core.Data;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Utilities
{
@@ -72,6 +72,7 @@ namespace MPF.Core.Utilities
case RedumpSystem.SonyPlayStation3:
case RedumpSystem.SonyPlayStation4:
//case RedumpSystem.SonyPlayStation5:
case RedumpSystem.SonyPlayStationPortable:
return true;
default:
return false;
@@ -94,11 +95,13 @@ namespace MPF.Core.Utilities
case RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem:
case RedumpSystem.AudioCD:
case RedumpSystem.DVDAudio:
case RedumpSystem.HasbroiONEducationalGamingSystem:
case RedumpSystem.HasbroVideoNow:
case RedumpSystem.HasbroVideoNowColor:
case RedumpSystem.HasbroVideoNowJr:
case RedumpSystem.HasbroVideoNowXP:
case RedumpSystem.PhilipsCDi:
case RedumpSystem.PlayStationGameSharkUpdates:
case RedumpSystem.SuperAudioCD:
return true;
default:

View File

@@ -14,8 +14,16 @@ namespace MPF.Core.Utilities
/// </summary>
public static (Options, string, int) LoadFromArguments(string[] args, int startIndex = 0)
{
// Create the output values
var options = new Options();
// Create the output values with defaults
var options = new Options()
{
RedumpUsername = null,
RedumpPassword = null,
InternalProgram = InternalProgram.NONE,
OutputSubmissionJSON = false,
CompressLogFiles = false,
};
string parsedPath = null;
// These values require multiple parts to be active
@@ -101,10 +109,6 @@ namespace MPF.Core.Utilities
}
}
// 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);
@@ -119,8 +123,8 @@ namespace MPF.Core.Utilities
{
var supportedArguments = new List<string>();
supportedArguments.Add("-u, --use <program> Dumping program output type [REQUIRED]");
supportedArguments.Add("-c, --credentials <user> <pw> Redump username and password");
supportedArguments.Add("-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)");

View File

@@ -2,7 +2,7 @@
using System.Reflection;
using MPF.Core.Data;
using Newtonsoft.Json.Linq;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Utilities
{
@@ -161,8 +161,15 @@ namespace MPF.Core.Utilities
/// </summary>
public static string GetCurrentVersion()
{
var assemblyVersion = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
return assemblyVersion.InformationalVersion;
try
{
var assemblyVersion = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
return assemblyVersion.InformationalVersion;
}
catch (Exception ex)
{
return ex.ToString();
}
}
/// <summary>

View File

@@ -1,238 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// The audio or data files filetype
/// </summary>
public enum CueFileType
{
/// <summary>
/// Intel binary file (least significant byte first). Use for data files.
/// </summary>
BINARY,
/// <summary>
/// Motorola binary file (most significant byte first). Use for data files.
/// </summary>
MOTOROLA,
/// <summary>
/// Audio AIFF file (44.1KHz 16-bit stereo)
/// </summary>
AIFF,
/// <summary>
/// Audio WAVE file (44.1KHz 16-bit stereo)
/// </summary>
WAVE,
/// <summary>
/// Audio MP3 file (44.1KHz 16-bit stereo)
/// </summary>
MP3,
}
/// <summary>
/// Represents a single FILE in a cuesheet
/// </summary>
public class CueFile
{
/// <summary>
/// filename
/// </summary>
public string FileName { get; set; }
/// <summary>
/// filetype
/// </summary>
public CueFileType FileType { get; set; }
/// <summary>
/// List of TRACK in FILE
/// </summary>
public List<CueTrack> Tracks { get; set; }
/// <summary>
/// Create an empty FILE
/// </summary>
public CueFile()
{
}
/// <summary>
/// Fill a FILE from an array of lines
/// </summary>
/// <param name="fileName">File name to set</param>
/// <param name="fileType">File type to set</param>
/// <param name="cueLines">Lines array to pull from</param>
/// <param name="i">Reference to index in array</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueFile(string fileName, string fileType, string[] cueLines, ref int i, bool throwOnError = false)
{
if (cueLines == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(cueLines));
return;
}
else if (i < 0 || i > cueLines.Length)
{
if (throwOnError)
throw new IndexOutOfRangeException();
return;
}
// Set the current fields
this.FileName = fileName.Trim('"');
this.FileType = GetFileType(fileType);
// Increment to start
i++;
for (; i < cueLines.Length; i++)
{
string line = cueLines[i].Trim();
string[] splitLine = line.Split(' ');
// If we have an empty line, we skip
if (string.IsNullOrWhiteSpace(line))
continue;
switch (splitLine[0])
{
// Read comments
case "REM":
// We ignore all comments for now
break;
// Read track information
case "TRACK":
if (splitLine.Length < 3)
{
if (throwOnError)
throw new FormatException($"TRACK line malformed: {line}");
continue;
}
if (this.Tracks == null)
this.Tracks = new List<CueTrack>();
var track = new CueTrack(splitLine[1], splitLine[2], cueLines, ref i);
if (track == default)
{
if (throwOnError)
throw new FormatException($"TRACK line malformed: {line}");
continue;
}
this.Tracks.Add(track);
break;
// Default means return
default:
i--;
return;
}
}
}
/// <summary>
/// Write the FILE out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public void Write(StreamWriter sw, bool throwOnError = false)
{
// If we don't have any tracks, it's invalid
if (this.Tracks == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(this.Tracks));
return;
}
else if (this.Tracks.Count == 0)
{
if (throwOnError)
throw new ArgumentException("No tracks provided to write");
return;
}
sw.WriteLine($"FILE \"{this.FileName}\" {FromFileType(this.FileType)}");
foreach (var track in Tracks)
{
track.Write(sw);
}
}
/// <summary>
/// Get the file type from a given string
/// </summary>
/// <param name="fileType">String to get value from</param>
/// <returns>CueFileType, if possible</returns>
private CueFileType GetFileType(string fileType)
{
switch (fileType.ToLowerInvariant())
{
case "binary":
return CueFileType.BINARY;
case "motorola":
return CueFileType.MOTOROLA;
case "aiff":
return CueFileType.AIFF;
case "wave":
return CueFileType.WAVE;
case "mp3":
return CueFileType.MP3;
default:
return CueFileType.BINARY;
}
}
/// <summary>
/// Get the string from a given file type
/// </summary>
/// <param name="fileType">CueFileType to get value from</param>
/// <returns>String, if possible (default BINARY)</returns>
private string FromFileType(CueFileType fileType)
{
switch (fileType)
{
case CueFileType.BINARY:
return "BINARY";
case CueFileType.MOTOROLA:
return "MOTOROLA";
case CueFileType.AIFF:
return "AIFF";
case CueFileType.WAVE:
return "WAVE";
case CueFileType.MP3:
return "MP3";
default:
return string.Empty;
}
}
}
}

View File

@@ -1,163 +0,0 @@
using System;
using System.IO;
using System.Linq;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// Represents a single INDEX in a TRACK
/// </summary>
public class CueIndex
{
/// <summary>
/// INDEX number, between 0 and 99
/// </summary>
public int Index { get; set; }
/// <summary>
/// Starting time of INDEX in minutes
/// </summary>
public int Minutes { get; set; }
/// <summary>
/// Starting time of INDEX in seconds
/// </summary>
/// <remarks>There are 60 seconds in a minute</remarks>
public int Seconds { get; set; }
/// <summary>
/// Starting time of INDEX in frames.
/// </summary>
/// <remarks>There are 75 frames per second</remarks>
public int Frames { get; set; }
/// <summary>
/// Create an empty INDEX
/// </summary>
public CueIndex()
{
}
/// <summary>
/// Fill a INDEX from an array of lines
/// </summary>
/// <param name="index">Index to set</param>
/// <param name="startTime">Start time to set</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueIndex(string index, string startTime, bool throwOnError = false)
{
// Set the current fields
if (!int.TryParse(index, out int parsedIndex))
{
if (throwOnError)
throw new ArgumentException($"Index was not a number: {index}");
return;
}
else if (parsedIndex < 0 || parsedIndex > 99)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Index must be between 0 and 99: {parsedIndex}");
return;
}
// Ignore empty lines
if (string.IsNullOrWhiteSpace(startTime))
{
if (throwOnError)
throw new ArgumentException("Start time was null or whitespace");
return;
}
// Ignore lines that don't contain the correct information
if (startTime.Length != 8 || startTime.Count(c => c == ':') != 2)
{
if (throwOnError)
throw new FormatException($"Start time was not in a recognized format: {startTime}");
return;
}
// Split the line
string[] splitTime = startTime.Split(':');
if (splitTime.Length != 3)
{
if (throwOnError)
throw new FormatException($"Start time was not in a recognized format: {startTime}");
return;
}
// Parse the lengths
int[] lengthSegments = new int[3];
// Minutes
if (!int.TryParse(splitTime[0], out lengthSegments[0]))
{
if (throwOnError)
throw new FormatException($"Minutes segment was not a number: {splitTime[0]}");
return;
}
else if (lengthSegments[0] < 0)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
return;
}
// Seconds
if (!int.TryParse(splitTime[1], out lengthSegments[1]))
{
if (throwOnError)
throw new FormatException($"Seconds segment was not a number: {splitTime[1]}");
return;
}
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
return;
}
// Frames
if (!int.TryParse(splitTime[2], out lengthSegments[2]))
{
if (throwOnError)
throw new FormatException($"Frames segment was not a number: {splitTime[2]}");
return;
}
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
return;
}
// Set the values
this.Index = parsedIndex;
this.Minutes = lengthSegments[0];
this.Seconds = lengthSegments[1];
this.Frames = lengthSegments[2];
}
/// <summary>
/// Write the INDEX out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
public void Write(StreamWriter sw)
{
sw.WriteLine($" INDEX {this.Index:D2} {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
}
}
}

View File

@@ -1,250 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// Represents a single cuesheet
/// </summary>
public class CueSheet
{
/// <summary>
/// CATALOG
/// </summary>
public string Catalog { get; set; }
/// <summary>
/// CDTEXTFILE
/// </summary>
public string CdTextFile { get; set; }
/// <summary>
/// PERFORMER
/// </summary>
public string Performer { get; set; }
/// <summary>
/// SONGWRITER
/// </summary>
public string Songwriter { get; set; }
/// <summary>
/// TITLE
/// </summary>
public string Title { get; set; }
/// <summary>
/// List of FILE in cuesheet
/// </summary>
public List<CueFile> Files { get; set; }
/// <summary>
/// Create an empty cuesheet
/// </summary>
public CueSheet()
{
}
/// <summary>
/// Create a cuesheet from a file, if possible
/// </summary>
/// <param name="filename"></param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueSheet(string filename, bool throwOnError = false)
{
// Check that the file exists
if (!File.Exists(filename))
return;
// Check the extension
string ext = Path.GetExtension(filename).TrimStart('.');
if (!string.Equals(ext, "cue", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(ext, "txt", StringComparison.OrdinalIgnoreCase))
{
return;
}
// Open the file and begin reading
string[] cueLines = File.ReadAllLines(filename);
for (int i = 0; i < cueLines.Length; i++)
{
string line = cueLines[i].Trim();
// http://stackoverflow.com/questions/554013/regular-expression-to-split-on-spaces-unless-in-quotes
string[] splitLine = Regex
.Matches(line, @"[^\s""]+|""[^""]*""")
.Cast<Match>()
.Select(m => m.Groups[0].Value)
.ToArray();
// If we have an empty line, we skip
if (string.IsNullOrWhiteSpace(line))
continue;
switch (splitLine[0])
{
// Read comments
case "REM":
// We ignore all comments for now
break;
// Read MCN
case "CATALOG":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"CATALOG line malformed: {line}");
continue;
}
this.Catalog = splitLine[1];
break;
// Read external CD-Text file path
case "CDTEXTFILE":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"CDTEXTFILE line malformed: {line}");
continue;
}
this.CdTextFile = splitLine[1];
break;
// Read CD-Text enhanced performer
case "PERFORMER":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"PERFORMER line malformed: {line}");
continue;
}
this.Performer = splitLine[1];
break;
// Read CD-Text enhanced songwriter
case "SONGWRITER":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"SONGWRITER line malformed: {line}");
continue;
}
this.Songwriter = splitLine[1];
break;
// Read CD-Text enhanced title
case "TITLE":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"TITLE line malformed: {line}");
continue;
}
this.Title = splitLine[1];
break;
// Read file information
case "FILE":
if (splitLine.Length < 3)
{
if (throwOnError)
throw new FormatException($"FILE line malformed: {line}");
continue;
}
if (this.Files == null)
this.Files = new List<CueFile>();
var file = new CueFile(splitLine[1], splitLine[2], cueLines, ref i);
if (file == default)
{
if (throwOnError)
throw new FormatException($"FILE line malformed: {line}");
continue;
}
this.Files.Add(file);
break;
}
}
}
/// <summary>
/// Write the cuesheet out to a file
/// </summary>
/// <param name="filename">File path to write to</param>
public void Write(string filename)
{
using (var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
Write(fs);
}
}
/// <summary>
/// Write the cuesheet out to a stream
/// </summary>
/// <param name="stream">Stream to write to</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public void Write(Stream stream, bool throwOnError = false)
{
// If we don't have any files, it's invalid
if (this.Files == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(this.Files));
return;
}
else if (this.Files.Count == 0)
{
if (throwOnError)
throw new ArgumentException("No files provided to write");
return;
}
using (var sw = new StreamWriter(stream, Encoding.ASCII, 1024, true))
{
if (!string.IsNullOrEmpty(this.Catalog))
sw.WriteLine($"CATALOG {this.Catalog}");
if (!string.IsNullOrEmpty(this.CdTextFile))
sw.WriteLine($"CDTEXTFILE {this.CdTextFile}");
if (!string.IsNullOrEmpty(this.Performer))
sw.WriteLine($"PERFORMER {this.Performer}");
if (!string.IsNullOrEmpty(this.Songwriter))
sw.WriteLine($"SONGWRITER {this.Songwriter}");
if (!string.IsNullOrEmpty(this.Title))
sw.WriteLine($"TITLE {this.Title}");
foreach (var file in Files)
{
file.Write(sw);
}
}
}
}
}

View File

@@ -1,554 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// Track datatype
/// </summary>
public enum CueTrackDataType
{
/// <summary>
/// AUDIO, Audio/Music (2352)
/// </summary>
AUDIO,
/// <summary>
/// CDG, Karaoke CD+G (2448)
/// </summary>
CDG,
/// <summary>
/// MODE1/2048, CD-ROM Mode1 Data (cooked)
/// </summary>
MODE1_2048,
/// <summary>
/// MODE1/2352 CD-ROM Mode1 Data (raw)
/// </summary>
MODE1_2352,
/// <summary>
/// MODE2/2336, CD-ROM XA Mode2 Data
/// </summary>
MODE2_2336,
/// <summary>
/// MODE2/2352, CD-ROM XA Mode2 Data
/// </summary>
MODE2_2352,
/// <summary>
/// CDI/2336, CD-I Mode2 Data
/// </summary>
CDI_2336,
/// <summary>
/// CDI/2352, CD-I Mode2 Data
/// </summary>
CDI_2352,
}
/// <summary>
/// Special subcode flags within a track
/// </summary>
[Flags]
public enum CueTrackFlag
{
/// <summary>
/// DCP, Digital copy permitted
/// </summary>
DCP = 1 << 0,
/// <summary>
/// 4CH, Four channel audio
/// </summary>
FourCH = 1 << 1,
/// <summary>
/// PRE, Pre-emphasis enabled (audio tracks only)
/// </summary>
PRE = 1 << 2,
/// <summary>
/// SCMS, Serial Copy Management System (not supported by all recorders)
/// </summary>
SCMS = 1 << 3,
/// <summary>
/// DATA, set for data files. This flag is set automatically based on the tracks filetype
/// </summary>
DATA = 1 << 4,
}
/// <summary>
/// Represents a single TRACK in a FILE
/// </summary>
public class CueTrack
{
/// <summary>
/// Track number. The range is 1 to 99.
/// </summary>
public int Number { get; set; }
/// <summary>
/// Track datatype
/// </summary>
public CueTrackDataType DataType { get; set; }
/// <summary>
/// FLAGS
/// </summary>
public CueTrackFlag Flags { get; set; }
/// <summary>
/// ISRC
/// </summary>
/// <remarks>12 characters in length</remarks>
public string ISRC { get; set; }
/// <summary>
/// PERFORMER
/// </summary>
public string Performer { get; set; }
/// <summary>
/// SONGWRITER
/// </summary>
public string Songwriter { get; set; }
/// <summary>
/// TITLE
/// </summary>
public string Title { get; set; }
/// <summary>
/// PREGAP
/// </summary>
public PreGap PreGap { get; set; }
/// <summary>
/// List of INDEX in TRACK
/// </summary>
/// <remarks>Must start with 0 or 1 and then sequential</remarks>
public List<CueIndex> Indices { get; set; }
/// <summary>
/// POSTGAP
/// </summary>
public PostGap PostGap { get; set; }
/// <summary>
/// Create an empty TRACK
/// </summary>
public CueTrack()
{
}
/// <summary>
/// Fill a TRACK from an array of lines
/// </summary>
/// <param name="number">Number to set</param>
/// <param name="dataType">Data type to set</param>
/// <param name="cueLines">Lines array to pull from</param>
/// <param name="i">Reference to index in array</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public CueTrack(string number, string dataType, string[] cueLines, ref int i, bool throwOnError = false)
{
if (cueLines == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(cueLines));
return;
}
else if (i < 0 || i > cueLines.Length)
{
if (throwOnError)
throw new IndexOutOfRangeException();
return;
}
// Set the current fields
if (!int.TryParse(number, out int parsedNumber))
{
if (throwOnError)
throw new ArgumentException($"Number was not a number: {number}");
return;
}
else if (parsedNumber < 1 || parsedNumber > 99)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Index must be between 1 and 99: {parsedNumber}");
return;
}
this.Number = parsedNumber;
this.DataType = GetDataType(dataType);
// Increment to start
i++;
for (; i < cueLines.Length; i++)
{
string line = cueLines[i].Trim();
string[] splitLine = line.Split(' ');
// If we have an empty line, we skip
if (string.IsNullOrWhiteSpace(line))
continue;
switch (splitLine[0])
{
// Read comments
case "REM":
// We ignore all comments for now
break;
// Read flag information
case "FLAGS":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"FLAGS line malformed: {line}");
continue;
}
this.Flags = GetFlags(splitLine);
break;
// Read International Standard Recording Code
case "ISRC":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"ISRC line malformed: {line}");
continue;
}
this.ISRC = splitLine[1];
break;
// Read CD-Text enhanced performer
case "PERFORMER":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"PERFORMER line malformed: {line}");
continue;
}
this.Performer = splitLine[1];
break;
// Read CD-Text enhanced songwriter
case "SONGWRITER":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"SONGWRITER line malformed: {line}");
continue;
}
this.Songwriter = splitLine[1];
break;
// Read CD-Text enhanced title
case "TITLE":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"TITLE line malformed: {line}");
continue;
}
this.Title = splitLine[1];
break;
// Read pregap information
case "PREGAP":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"PREGAP line malformed: {line}");
continue;
}
var pregap = new PreGap(splitLine[1]);
if (pregap == default)
{
if (throwOnError)
throw new FormatException($"PREGAP line malformed: {line}");
continue;
}
this.PreGap = pregap;
break;
// Read index information
case "INDEX":
if (splitLine.Length < 3)
{
if (throwOnError)
throw new FormatException($"INDEX line malformed: {line}");
continue;
}
if (this.Indices == null)
this.Indices = new List<CueIndex>();
var index = new CueIndex(splitLine[1], splitLine[2]);
if (index == default)
{
if (throwOnError)
throw new FormatException($"INDEX line malformed: {line}");
continue;
}
this.Indices.Add(index);
break;
// Read postgap information
case "POSTGAP":
if (splitLine.Length < 2)
{
if (throwOnError)
throw new FormatException($"POSTGAP line malformed: {line}");
continue;
}
var postgap = new PostGap(splitLine[1]);
if (postgap == default)
{
if (throwOnError)
throw new FormatException($"POSTGAP line malformed: {line}");
continue;
}
this.PostGap = postgap;
break;
// Default means return
default:
i--;
return;
}
}
}
/// <summary>
/// Write the TRACK out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public void Write(StreamWriter sw, bool throwOnError = false)
{
// If we don't have any indices, it's invalid
if (this.Indices == null)
{
if (throwOnError)
throw new ArgumentNullException(nameof(this.Indices));
return;
}
else if (this.Indices.Count == 0)
{
if (throwOnError)
throw new ArgumentException("No indices provided to write");
return;
}
sw.WriteLine($" TRACK {this.Number:D2} {FromDataType(this.DataType)}");
if (this.Flags != 0)
sw.WriteLine($" FLAGS {FromFlags(this.Flags)}");
if (!string.IsNullOrEmpty(this.ISRC))
sw.WriteLine($"ISRC {this.ISRC}");
if (!string.IsNullOrEmpty(this.Performer))
sw.WriteLine($"PERFORMER {this.Performer}");
if (!string.IsNullOrEmpty(this.Songwriter))
sw.WriteLine($"SONGWRITER {this.Songwriter}");
if (!string.IsNullOrEmpty(this.Title))
sw.WriteLine($"TITLE {this.Title}");
if (this.PreGap != null)
this.PreGap.Write(sw);
foreach (var index in Indices)
{
index.Write(sw);
}
if (this.PostGap != null)
this.PostGap.Write(sw);
}
/// <summary>
/// Get the data type from a given string
/// </summary>
/// <param name="dataType">String to get value from</param>
/// <returns>CueTrackDataType, if possible (default AUDIO)</returns>
private CueTrackDataType GetDataType(string dataType)
{
switch (dataType.ToLowerInvariant())
{
case "audio":
return CueTrackDataType.AUDIO;
case "cdg":
return CueTrackDataType.CDG;
case "mode1/2048":
return CueTrackDataType.MODE1_2048;
case "mode1/2352":
return CueTrackDataType.MODE1_2352;
case "mode2/2336":
return CueTrackDataType.MODE2_2336;
case "mode2/2352":
return CueTrackDataType.MODE2_2352;
case "cdi/2336":
return CueTrackDataType.CDI_2336;
case "cdi/2352":
return CueTrackDataType.CDI_2352;
default:
return CueTrackDataType.AUDIO;
}
}
/// <summary>
/// Get the string from a given data type
/// </summary>
/// <param name="dataType">CueTrackDataType to get value from</param>
/// <returns>string, if possible</returns>
private string FromDataType(CueTrackDataType dataType)
{
switch (dataType)
{
case CueTrackDataType.AUDIO:
return "AUDIO";
case CueTrackDataType.CDG:
return "CDG";
case CueTrackDataType.MODE1_2048:
return "MODE1/2048";
case CueTrackDataType.MODE1_2352:
return "MODE1/2352";
case CueTrackDataType.MODE2_2336:
return "MODE2/2336";
case CueTrackDataType.MODE2_2352:
return "MODE2/2352";
case CueTrackDataType.CDI_2336:
return "CDI/2336";
case CueTrackDataType.CDI_2352:
return "CDI/2352";
default:
return string.Empty;
}
}
/// <summary>
/// Get the flag value for an array of strings
/// </summary>
/// <param name="flagStrings">Possible flags as strings</param>
/// <returns>CueTrackFlag value representing the strings, if possible</returns>
private CueTrackFlag GetFlags(string[] flagStrings)
{
CueTrackFlag flag = 0;
foreach (string flagString in flagStrings)
{
switch (flagString.ToLowerInvariant())
{
case "flags":
// No-op since this is the start of the line
break;
case "dcp":
flag |= CueTrackFlag.DCP;
break;
case "4ch":
flag |= CueTrackFlag.FourCH;
break;
case "pre":
flag |= CueTrackFlag.PRE;
break;
case "scms":
flag |= CueTrackFlag.SCMS;
break;
case "data":
flag |= CueTrackFlag.DATA;
break;
}
}
return flag;
}
/// <summary>
/// Get the string value for a set of track flags
/// </summary>
/// <param name="flags">CueTrackFlag to get value from</param>
/// <returns>String value representing the CueTrackFlag, if possible</returns>
private string FromFlags(CueTrackFlag flags)
{
string outputFlagString = string.Empty;
if (flags.HasFlag(CueTrackFlag.DCP))
outputFlagString += "DCP ";
if (flags.HasFlag(CueTrackFlag.FourCH))
outputFlagString += "4CH ";
if (flags.HasFlag(CueTrackFlag.PRE))
outputFlagString += "PRE ";
if (flags.HasFlag(CueTrackFlag.SCMS))
outputFlagString += "SCMS ";
if (flags.HasFlag(CueTrackFlag.DATA))
outputFlagString += "DATA ";
return outputFlagString.Trim();
}
}
}

View File

@@ -1,31 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2022</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -1,139 +0,0 @@
using System;
using System.IO;
using System.Linq;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// Represents POSTGAP information of a track
/// </summary>
public class PostGap
{
/// <summary>
/// Length of POSTGAP in minutes
/// </summary>
public int Minutes { get; set; }
/// <summary>
/// Length of POSTGAP in seconds
/// </summary>
/// <remarks>There are 60 seconds in a minute</remarks>
public int Seconds { get; set; }
/// <summary>
/// Length of POSTGAP in frames.
/// </summary>
/// <remarks>There are 75 frames per second</remarks>
public int Frames { get; set; }
/// Create an empty POSTGAP
/// </summary>
public PostGap()
{
}
/// <summary>
/// Create a POSTGAP from a mm:ss:ff length
/// </summary>
/// <param name="length">String to get length information from</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public PostGap(string length, bool throwOnError = false)
{
// Ignore empty lines
if (string.IsNullOrWhiteSpace(length))
{
if (throwOnError)
throw new ArgumentException("Length was null or whitespace");
return;
}
// Ignore lines that don't contain the correct information
if (length.Length != 8 || length.Count(c => c == ':') != 2)
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Split the line
string[] splitLength = length.Split(':');
if (splitLength.Length != 3)
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Parse the lengths
int[] lengthSegments = new int[3];
// Minutes
if (!int.TryParse(splitLength[0], out lengthSegments[0]))
{
if (throwOnError)
throw new FormatException($"Minutes segment was not a number: {splitLength[0]}");
return;
}
else if (lengthSegments[0] < 0)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
return;
}
// Seconds
if (!int.TryParse(splitLength[1], out lengthSegments[1]))
{
if (throwOnError)
throw new FormatException($"Seconds segment was not a number: {splitLength[1]}");
return;
}
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
return;
}
// Frames
if (!int.TryParse(splitLength[2], out lengthSegments[2]))
{
if (throwOnError)
throw new FormatException($"Frames segment was not a number: {splitLength[2]}");
return;
}
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
return;
}
// Set the values
this.Minutes = lengthSegments[0];
this.Seconds = lengthSegments[1];
this.Frames = lengthSegments[2];
}
/// <summary>
/// Write the POSTGAP out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
public void Write(StreamWriter sw)
{
sw.WriteLine($" POSTGAP {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
}
}
}

View File

@@ -1,140 +0,0 @@
using System;
using System.IO;
using System.Linq;
/// <remarks>
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
/// </remarks>
namespace MPF.CueSheets
{
/// <summary>
/// Represents PREGAP information of a track
/// </summary>
public class PreGap
{
/// <summary>
/// Length of PREGAP in minutes
/// </summary>
public int Minutes { get; set; }
/// <summary>
/// Length of PREGAP in seconds
/// </summary>
/// <remarks>There are 60 seconds in a minute</remarks>
public int Seconds { get; set; }
/// <summary>
/// Length of PREGAP in frames.
/// </summary>
/// <remarks>There are 75 frames per second</remarks>
public int Frames { get; set; }
/// <summary>
/// Create an empty PREGAP
/// </summary>
public PreGap()
{
}
/// <summary>
/// Create a PREGAP from a mm:ss:ff length
/// </summary>
/// <param name="length">String to get length information from</param>
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
public PreGap(string length, bool throwOnError = false)
{
// Ignore empty lines
if (string.IsNullOrWhiteSpace(length))
{
if (throwOnError)
throw new ArgumentException("Length was null or whitespace");
return;
}
// Ignore lines that don't contain the correct information
if (length.Length != 8 || length.Count(c => c == ':') != 2)
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Split the line
string[] splitLength = length.Split(':');
if (splitLength.Length != 3)
{
if (throwOnError)
throw new FormatException($"Length was not in a recognized format: {length}");
return;
}
// Parse the lengths
int[] lengthSegments = new int[3];
// Minutes
if (!int.TryParse(splitLength[0], out lengthSegments[0]))
{
if (throwOnError)
throw new FormatException($"Minutes segment was not a number: {splitLength[0]}");
return;
}
else if (lengthSegments[0] < 0)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
return;
}
// Seconds
if (!int.TryParse(splitLength[1], out lengthSegments[1]))
{
if (throwOnError)
throw new FormatException($"Seconds segment was not a number: {splitLength[1]}");
return;
}
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
return;
}
// Frames
if (!int.TryParse(splitLength[2], out lengthSegments[2]))
{
if (throwOnError)
throw new FormatException($"Frames segment was not a number: {splitLength[2]}");
return;
}
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
{
if (throwOnError)
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
return;
}
// Set the values
this.Minutes = lengthSegments[0];
this.Seconds = lengthSegments[1];
this.Frames = lengthSegments[2];
}
/// <summary>
/// Write the PREGAP out to a stream
/// </summary>
/// <param name="sw">StreamWriter to write to</param>
public void Write(StreamWriter sw)
{
sw.WriteLine($" PREGAP {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
}
}
}

View File

@@ -7,7 +7,7 @@ using BurnOutSharp;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.Modules;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Library
{
@@ -19,14 +19,9 @@ namespace MPF.Library
#region Output paths
/// <summary>
/// Base output directory to write files to
/// Base output file path to write files to
/// </summary>
public string OutputDirectory { get; private set; }
/// <summary>
/// Base output filename for output
/// </summary>
public string OutputFilename { get; private set; }
public string OutputPath { get; private set; }
#endregion
@@ -47,10 +42,15 @@ namespace MPF.Library
/// </summary>
public MediaType? Type { get; private set; }
/// <summary>
/// Currently selected dumping program
/// </summary>
public InternalProgram InternalProgram { get; private set; }
/// <summary>
/// Options object representing user-defined options
/// </summary>
public Options Options { get; private set; }
public Core.Data.Options Options { get; private set; }
/// <summary>
/// Parameters object representing what to send to the internal program
@@ -87,30 +87,31 @@ namespace MPF.Library
/// Constructor for a full DumpEnvironment object from user information
/// </summary>
/// <param name="options"></param>
/// <param name="outputDirectory"></param>
/// <param name="outputFilename"></param>
/// <param name="outputPath"></param>
/// <param name="drive"></param>
/// <param name="system"></param>
/// <param name="type"></param>
/// <param name="internalProgram"></param>
/// <param name="parameters"></param>
public DumpEnvironment(Options options,
string outputDirectory,
string outputFilename,
public DumpEnvironment(Core.Data.Options options,
string outputPath,
Drive drive,
RedumpSystem? system,
MediaType? type,
InternalProgram? internalProgram,
string parameters)
{
// Set options object
this.Options = options;
// Output paths
(this.OutputDirectory, this.OutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename);
this.OutputPath = InfoTool.NormalizeOutputPaths(outputPath);
// UI information
this.Drive = drive;
this.System = system ?? options.DefaultSystem;
this.Type = type ?? MediaType.NONE;
this.InternalProgram = internalProgram ?? options.InternalProgram;
// Dumping program
SetParameters(parameters);
@@ -127,20 +128,32 @@ namespace MPF.Library
if (this.Parameters.InternalProgram != InternalProgram.DiscImageCreator)
return;
// Replace all instances in the output directory
this.OutputDirectory = this.OutputDirectory.Replace('.', '_');
try
{
// Normalize the output path
string outputPath = InfoTool.NormalizeOutputPaths(this.OutputPath);
// Currently, only periods in directories matter
// Leave the following code commented in case filename handling breaks again
// Replace all instances in the output directory
string outputDirectory = Path.GetDirectoryName(outputPath);
outputDirectory = outputDirectory.Replace(".", "_");
// Replace all instances in the output filename, except the extension
//string tempFilename = Path.GetFileNameWithoutExtension(this.OutputFilename)
// .Replace('.', '_');
//string tempExtension = Path.GetExtension(this.OutputFilename)?.TrimStart('.');
//this.OutputFilename = $"{tempFilename}.{tempExtension}";
// Replace all instances in the output filename
string outputFilename = Path.GetFileNameWithoutExtension(outputPath);
outputFilename = outputFilename.Replace(".", "_");
// Assign the path to the filename as well for dumping
((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = Path.Combine(this.OutputDirectory, this.OutputFilename);
// Get the extension for recreating the path
string outputExtension = Path.GetExtension(outputPath).TrimStart('.');
// Rebuild the output path
if (!string.IsNullOrWhiteSpace(outputExtension))
this.OutputPath = Path.Combine(outputDirectory, $"{outputFilename}.{outputExtension}");
else
this.OutputPath = Path.Combine(outputDirectory, outputFilename);
// Assign the path to the filename as well for dumping
((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = this.OutputPath;
}
catch { }
}
/// <summary>
@@ -149,23 +162,19 @@ namespace MPF.Library
/// <param name="parameters">String representation of the parameters</param>
public void SetParameters(string parameters)
{
switch (Options.InternalProgram)
switch (this.InternalProgram)
{
// Dumping support
case InternalProgram.Aaru:
this.Parameters = new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath };
break;
case InternalProgram.DD:
this.Parameters = new Modules.DD.Parameters(parameters) { ExecutablePath = Options.DDPath };
break;
case InternalProgram.DiscImageCreator:
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
break;
case InternalProgram.Redumper:
this.Parameters = new Modules.DD.Parameters(parameters) { ExecutablePath = Options.RedumperPath };
this.Parameters = new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath };
break;
// Verification support only
@@ -207,28 +216,23 @@ namespace MPF.Library
return null;
// Set the proper parameters
string filename = OutputDirectory + Path.DirectorySeparatorChar + OutputFilename;
switch (Options.InternalProgram)
switch (this.InternalProgram)
{
case InternalProgram.Aaru:
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
break;
case InternalProgram.DD:
Parameters = new Modules.DD.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
break;
case InternalProgram.DiscImageCreator:
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
break;
case InternalProgram.Redumper:
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, this.OutputPath, 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);
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
break;
}
@@ -279,10 +283,10 @@ namespace MPF.Library
}
// Execute internal tool
progress?.Report(Result.Success($"Executing {Options.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
Directory.CreateDirectory(OutputDirectory);
progress?.Report(Result.Success($"Executing {this.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
progress?.Report(Result.Success($"{Options.InternalProgram} has finished!"));
progress?.Report(Result.Success($"{this.InternalProgram} has finished!"));
// Execute additional tools
progress?.Report(Result.Success("Running any additional tools... see log for output!"));
@@ -313,8 +317,12 @@ namespace MPF.Library
{
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
// Get the output directory and filename separately
string outputDirectory = Path.GetDirectoryName(this.OutputPath);
string outputFilename = Path.GetFileName(this.OutputPath);
// Check to make sure that the output had all the correct files
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(this.OutputDirectory, this.OutputFilename, this.Parameters, false);
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, this.Parameters, false);
if (!foundFiles)
{
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
@@ -324,8 +332,7 @@ namespace MPF.Library
// Extract the information from the output files
resultProgress?.Report(Result.Success("Extracting output information from output files..."));
SubmissionInfo submissionInfo = await InfoTool.ExtractOutputInformation(
this.OutputDirectory,
this.OutputFilename,
this.OutputPath,
this.Drive,
this.System,
this.Type,
@@ -343,7 +350,7 @@ namespace MPF.Library
}
// Reset the drive automatically if configured to
if (Options.InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
if (this.InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
{
resultProgress?.Report(Result.Success($"Resetting drive {Drive.Letter}"));
await ResetDrive();
@@ -378,7 +385,7 @@ namespace MPF.Library
// Write the text output
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(this.OutputDirectory, formattedValues);
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, formattedValues);
if (txtSuccess)
resultProgress?.Report(Result.Success(txtResult));
else
@@ -388,7 +395,7 @@ namespace MPF.Library
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
{
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
bool scanSuccess = InfoTool.WriteProtectionData(this.OutputDirectory, submissionInfo);
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, submissionInfo);
if (scanSuccess)
resultProgress?.Report(Result.Success("Writing complete!"));
else
@@ -399,7 +406,7 @@ namespace MPF.Library
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);
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, submissionInfo, Options.IncludeArtifacts);
if (jsonSuccess)
resultProgress?.Report(Result.Success("Writing complete!"));
else
@@ -410,7 +417,7 @@ namespace MPF.Library
if (Options.CompressLogFiles)
{
resultProgress?.Report(Result.Success("Compressing log files..."));
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(this.OutputDirectory, this.OutputFilename, this.Parameters);
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, outputFilename, this.Parameters);
if (compressSuccess)
resultProgress?.Report(Result.Success(compressResult));
else
@@ -491,12 +498,11 @@ namespace MPF.Library
return Result.Failure("Error! Current configuration is not supported!");
// Fix the output paths, just in case
(OutputDirectory, OutputFilename) = InfoTool.NormalizeOutputPaths(OutputDirectory, OutputFilename);
this.OutputPath = InfoTool.NormalizeOutputPaths(this.OutputPath);
// Validate that the output path isn't on the dumping drive
string fullOutputPath = Path.GetFullPath(Path.Combine(OutputDirectory, OutputFilename));
if (fullOutputPath[0] == Drive.Letter)
return Result.Failure($"Error! Cannot output to same drive that is being dumped!");
if (this.OutputPath[0] == Drive.Letter)
return Result.Failure("Error! Cannot output to same drive that is being dumped!");
// Validate that the required program exists
if (!File.Exists(Parameters.ExecutablePath))
@@ -505,7 +511,7 @@ namespace MPF.Library
// Validate that the dumping drive doesn't contain the executable
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath);
if (fullExecutablePath[0] == Drive.Letter)
return Result.Failure("$Error! Cannot dump same drive that executable resides on!");
return Result.Failure("Error! Cannot dump same drive that executable resides on!");
// Validate that the current configuration is supported
return Tools.GetSupportStatus(System, Type);

File diff suppressed because it is too large Load Diff

View File

@@ -1,15 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<AssemblyName>MPF.Library</AssemblyName>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2022</Copyright>
<Copyright>Copyright (c)2019-2023</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -24,14 +22,14 @@
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.3.4" GeneratePathProperty="true">
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />

View File

@@ -4,9 +4,8 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using BinaryObjectScanner.Protection;
using BurnOutSharp;
using BurnOutSharp.ProtectionType;
using MPF.Core.Data;
using psxt001z;
namespace MPF.Library
@@ -20,7 +19,7 @@ namespace MPF.Library
/// <param name="options">Options object that determines what to scan</param>
/// <param name="progress">Optional progress callback</param>
/// <returns>Set of all detected copy protections with an optional error string</returns>
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Options options, IProgress<ProtectionProgress> progress = null)
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Core.Data.Options options, IProgress<ProtectionProgress> progress = null)
{
try
{
@@ -28,7 +27,10 @@ namespace MPF.Library
{
var scanner = new Scanner(
options.ScanArchivesForProtection,
scanContents: true, // Hardcoded value to avoid issues
scanGameEngines: false, // Hardcoded value to avoid issues
options.ScanPackersForProtection,
scanPaths: true, // Hardcoded value to avoid issues
options.IncludeDebugProtectionInformation,
progress);
@@ -212,80 +214,57 @@ namespace MPF.Library
}
// 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}")))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => p != "SafeDisc")
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}")))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc 2")
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
.Where(p => !p.StartsWith("SafeDisc (dplayerx.dll)"))
.Where(p => !p.StartsWith("SafeDisc (drvmgt.dll)"))
.Where(p => !p.StartsWith("SafeDisc (secdrv.sys)"))
.Where(p => p != "SafeDisc Lite");
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => p.StartsWith("SafeDisc (drvmgt.dll)")))
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}")))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => p != "SafeDisc")
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc 2")
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
.Where(p => !p.StartsWith("SafeDisc (dplayerx.dll)"))
.Where(p => !p.StartsWith("SafeDisc (secdrv.sys)"))
.Where(p => p != "SafeDisc Lite");
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => p.StartsWith("SafeDisc (secdrv.sys)")))
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc 2")
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
.Where(p => !p.StartsWith("SafeDisc (dplayerx.dll)"))
.Where(p => p != "SafeDisc Lite");
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => p.StartsWith("SafeDisc (dplayerx.dll)")))
else if (foundProtections.Any(p => p.StartsWith("Macrovision Security Driver")))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc 2")
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
.Where(p => p != "SafeDisc Lite");
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => p == "SafeDisc 3.20-4.xx (version removed)"))
else if (foundProtections.Any(p => p == "SafeDisc 2+"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc 2")
.Where(p => p != "SafeDisc Lite");
}
else if (foundProtections.Any(p => p == "SafeDisc 2"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc Lite");
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => p != "SafeDisc");
}
else if (foundProtections.Any(p => p == "SafeDisc 1/Lite"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1-3")
.Where(p => p != "SafeDisc Lite");
}
else if (foundProtections.Any(p => p == "SafeDisc Lite"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1-3");
}
else if (foundProtections.Any(p => p == "SafeDisc 1-3"))
{
foundProtections = foundProtections.Where(p => p != "SafeDisc");
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => p != "SafeDisc");
}
}

View File

@@ -1,4 +1,4 @@
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Modules.Aaru
{

View File

@@ -9,8 +9,8 @@ using System.Xml.Schema;
using System.Xml.Serialization;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.CueSheets;
using RedumpLib.Data;
using SabreTools.Models.CueSheets;
using SabreTools.RedumpLib.Data;
using Schemas;
namespace MPF.Modules.Aaru
@@ -199,6 +199,7 @@ namespace MPF.Modules.Aaru
// TODO: Determine if there's an Aaru version anywhere
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + ".cicm.xml")?.ToString("yyyy-MM-dd HH:mm:ss");
// Deserialize the sidecar, if possible
var sidecar = GenerateSidecar(basePath + ".cicm.xml");
@@ -225,14 +226,16 @@ namespace MPF.Modules.Aaru
info.DumpingInfo.ReportedDiscType = fullDiscType;
}
// Get the Datafile information
Datafile datafile = GenerateDatafile(sidecar, basePath);
// Fill in the hash data
info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(sidecar, basePath);
info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(datafile);
switch (this.Type)
{
// TODO: Can this do GD-ROM?
case MediaType.CDROM:
// TODO: Re-enable once PVD generation / finding is fixed
// Generate / obtain the PVD
//info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
@@ -243,9 +246,9 @@ namespace MPF.Modules.Aaru
info.CommonDiscInfo.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
info.TracksAndWriteOffsets.Cuesheet = GenerateCuesheet(sidecar, basePath) ?? "";
info.TracksAndWriteOffsets.Cuesheet = GenerateCuesheet(sidecar, basePath) ?? string.Empty;
string cdWriteOffset = GetWriteOffset(sidecar) ?? "";
string cdWriteOffset = GetWriteOffset(sidecar) ?? string.Empty;
info.CommonDiscInfo.RingWriteOffset = cdWriteOffset;
info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset;
break;
@@ -254,7 +257,7 @@ namespace MPF.Modules.Aaru
case MediaType.HDDVD:
case MediaType.BluRay:
// Get the individual hash data, as per internal
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out string crc32, out string md5, out string sha1))
if (GetISOHashValues(datafile, out long size, out string crc32, out string md5, out string sha1))
{
info.SizeAndChecksums.Size = size;
info.SizeAndChecksums.CRC32 = crc32;
@@ -269,7 +272,7 @@ namespace MPF.Modules.Aaru
// Deal with the layerbreak
string layerbreak = null;
if (this.Type == MediaType.DVD)
layerbreak = GetLayerbreak(sidecar) ?? "";
layerbreak = GetLayerbreak(sidecar) ?? string.Empty;
else if (this.Type == MediaType.BluRay)
layerbreak = info.SizeAndChecksums.Size > 25_025_314_816 ? "25025314816" : null;
@@ -301,7 +304,7 @@ namespace MPF.Modules.Aaru
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
info.CopyProtection.Protection = GetDVDProtection(sidecar) ?? "";
info.CopyProtection.Protection = GetDVDProtection(sidecar) ?? string.Empty;
break;
case RedumpSystem.KonamiPython2:
@@ -313,7 +316,7 @@ namespace MPF.Modules.Aaru
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
}
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.MicrosoftXbox:
@@ -323,13 +326,13 @@ namespace MPF.Modules.Aaru
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer;
info.Extras.SecuritySectorRanges = ss ?? "";
info.Extras.SecuritySectorRanges = ss ?? string.Empty;
}
if (GetXboxDMIInfo(sidecar, out string serial, out string version, out Region? region))
{
info.CommonDiscInfo.Serial = serial ?? "";
info.VersionAndEditions.Version = version ?? "";
info.CommonDiscInfo.Serial = serial ?? string.Empty;
info.VersionAndEditions.Version = version ?? string.Empty;
info.CommonDiscInfo.Region = region;
}
@@ -342,13 +345,13 @@ namespace MPF.Modules.Aaru
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer;
info.Extras.SecuritySectorRanges = ss360 ?? "";
info.Extras.SecuritySectorRanges = ss360 ?? string.Empty;
}
if (GetXbox360DMIInfo(sidecar, out string serial360, out string version360, out Region? region360))
{
info.CommonDiscInfo.Serial = serial360 ?? "";
info.VersionAndEditions.Version = version360 ?? "";
info.CommonDiscInfo.Serial = serial360 ?? string.Empty;
info.VersionAndEditions.Version = version360 ?? string.Empty;
info.CommonDiscInfo.Region = region360;
}
break;
@@ -373,17 +376,22 @@ namespace MPF.Modules.Aaru
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
}
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation3:
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation4:
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? "";
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
break;
case RedumpSystem.SonyPlayStation5:
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? "";
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
break;
}
@@ -1430,6 +1438,8 @@ namespace MPF.Modules.Aaru
case MediaType.CDROM:
if (File.Exists($"{basePath}.cicm.xml"))
logFiles.Add($"{basePath}.cicm.xml");
if (File.Exists($"{basePath}.error.log"))
logFiles.Add($"{basePath}.error.log");
if (File.Exists($"{basePath}.ibg"))
logFiles.Add($"{basePath}.ibg");
if (File.Exists($"{basePath}.log"))
@@ -1448,6 +1458,8 @@ namespace MPF.Modules.Aaru
case MediaType.BluRay:
if (File.Exists($"{basePath}.cicm.xml"))
logFiles.Add($"{basePath}.cicm.xml");
if (File.Exists($"{basePath}.error.log"))
logFiles.Add($"{basePath}.error.log");
if (File.Exists($"{basePath}.ibg"))
logFiles.Add($"{basePath}.ibg");
if (File.Exists($"{basePath}.log"))
@@ -1816,7 +1828,7 @@ namespace MPF.Modules.Aaru
// Speed
byteValue = ProcessInt8Parameter(parts, null, FlagStrings.SpeedLong, ref i);
if (byteValue == null && byteValue != SByte.MinValue)
if (byteValue != null && byteValue != SByte.MinValue)
SpeedValue = byteValue;
#endregion
@@ -2429,10 +2441,10 @@ namespace MPF.Modules.Aaru
// Required variables
uint totalTracks = 0;
CueSheet cueSheet = new CueSheet
var cueFiles = new List<CueFile>();
var cueSheet = new CueSheet
{
Performer = string.Join(", ", cicmSidecar.Performer ?? new string[0]),
Files = new List<CueFile>(),
};
// Only care about OpticalDisc types
@@ -2470,13 +2482,13 @@ namespace MPF.Modules.Aaru
{
FileName = GenerateTrackName(basePath, (int)totalTracks, cueTrack.Number, opticalDisc.DiscType),
FileType = CueFileType.BINARY,
Tracks = new List<CueTrack>(),
};
// Add index data
var cueTracks = new List<CueTrack>();
if (track.Indexes != null && track.Indexes.Length > 0)
{
cueTrack.Indices = new List<CueIndex>();
var cueIndicies = new List<CueIndex>();
// Loop through each index
foreach (TrackIndexType trackIndex in track.Indexes)
@@ -2490,36 +2502,60 @@ namespace MPF.Modules.Aaru
// Pregap information
if (trackIndex.Value < 0)
cueTrack.PreGap = new PreGap(timeString);
{
string[] timeStringSplit = timeString.Split(':');
cueTrack.PreGap = new PreGap
{
Minutes = int.Parse(timeStringSplit[0]),
Seconds = int.Parse(timeStringSplit[1]),
Frames = int.Parse(timeStringSplit[2]),
};
}
// Individual indexes
else
cueTrack.Indices.Add(new CueIndex(trackIndex.index.ToString(), timeString));
{
string[] timeStringSplit = timeString.Split(':');
cueIndicies.Add(new CueIndex
{
Index = trackIndex.index,
Minutes = int.Parse(timeStringSplit[0]),
Seconds = int.Parse(timeStringSplit[1]),
Frames = int.Parse(timeStringSplit[2]),
});
}
}
cueTrack.Indices = cueIndicies.ToArray();
}
else
{
// Default if index data missing from sidecar
cueTrack.Indices = new List<CueIndex>()
cueTrack.Indices = new CueIndex[]
{
new CueIndex("01", "00:00:00"),
new CueIndex
{
Index = 1,
Minutes = 0,
Seconds = 0,
Frames = 0,
},
};
}
// Add the track to the file
cueFile.Tracks.Add(cueTrack);
cueTracks.Add(cueTrack);
// Add the file to the cuesheet
cueSheet.Files.Add(cueFile);
cueFiles.Add(cueFile);
}
}
// If we have a cuesheet to write out, do so
cueSheet.Files = cueFiles.ToArray();
if (cueSheet != null && cueSheet != default)
{
MemoryStream ms = new MemoryStream();
cueSheet.Write(ms);
ms.Position = 0;
var ms = new SabreTools.Serialization.Streams.CueSheet().Serialize(cueSheet);
using (var sr = new StreamReader(ms))
{
return sr.ReadToEnd();
@@ -2637,6 +2673,119 @@ namespace MPF.Modules.Aaru
return datfile;
}
/// <summary>
/// Generate a CMP XML datfile string based on CICM sidecar data
/// </summary>
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
/// <param name="basePath">Base path for determining file names</param>
/// <returns>Datafile containing the hash information, null on error</returns>
private static Datafile GenerateDatafile(CICMMetadataType cicmSidecar, string basePath)
{
// If the object is null, we can't get information from it
if (cicmSidecar == null)
return null;
// Required variables
Datafile datafile = new Datafile();
List<Rom> roms = new List<Rom>();
// Process OpticalDisc, if possible
if (cicmSidecar.OpticalDisc != null && cicmSidecar.OpticalDisc.Length > 0)
{
// Loop through each OpticalDisc in the metadata
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
{
// Only capture the first total track count
uint totalTracks = 0;
if (opticalDisc.Tracks != null && opticalDisc.Tracks.Length > 0)
totalTracks = opticalDisc.Tracks[0];
// If there are no tracks, we can't get a datfile
if (opticalDisc.Track == null || opticalDisc.Track.Length == 0)
continue;
// Loop through each track
foreach (TrackType track in opticalDisc.Track)
{
uint trackNumber = track.Sequence?.TrackNumber ?? 0;
ulong size = track.Size;
string crc32 = string.Empty;
string md5 = string.Empty;
string sha1 = string.Empty;
// If we don't have any checksums, we can't get a DAT for this track
if (track.Checksums == null || track.Checksums.Length == 0)
continue;
// Extract only relevant checksums
foreach (ChecksumType checksum in track.Checksums)
{
switch (checksum.type)
{
case ChecksumTypeType.crc32:
crc32 = checksum.Value;
break;
case ChecksumTypeType.md5:
md5 = checksum.Value;
break;
case ChecksumTypeType.sha1:
sha1 = checksum.Value;
break;
}
}
// Build the track datfile data and append
string trackName = GenerateTrackName(basePath, (int)totalTracks, (int)trackNumber, opticalDisc.DiscType);
roms.Add(new Rom { Name = trackName, Size = size.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1 });
}
}
}
// Process BlockMedia, if possible
if (cicmSidecar.BlockMedia != null && cicmSidecar.BlockMedia.Length > 0)
{
// Loop through each BlockMedia in the metadata
foreach (BlockMediaType blockMedia in cicmSidecar.BlockMedia)
{
ulong size = blockMedia.Size;
string crc32 = string.Empty;
string md5 = string.Empty;
string sha1 = string.Empty;
// If we don't have any checksums, we can't get a DAT for this track
if (blockMedia.Checksums == null || blockMedia.Checksums.Length == 0)
continue;
// Extract only relevant checksums
foreach (ChecksumType checksum in blockMedia.Checksums)
{
switch (checksum.type)
{
case ChecksumTypeType.crc32:
crc32 = checksum.Value;
break;
case ChecksumTypeType.md5:
md5 = checksum.Value;
break;
case ChecksumTypeType.sha1:
sha1 = checksum.Value;
break;
}
}
// Build the track datfile data and append
string trackName = $"{basePath}.bin";
roms.Add(new Rom { Name = trackName, Size = size.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1 });
}
}
// Assign the roms to a new game
datafile.Games = new Game[1];
datafile.Games[0] = new Game { Roms = roms.ToArray() };
return datafile;
}
/// <summary>
/// Generate a track name based on current path and tracks
/// </summary>

7391
MPF.Modules/Aaru/cicm.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,10 +6,14 @@ using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml.Schema;
using System.Xml;
using System.Xml.Serialization;
using MPF.Core.Data;
using MPF.Core.Hashing;
using MPF.Core.Utilities;
using RedumpLib.Data;
using SabreTools.Models.PIC;
using SabreTools.RedumpLib.Data;
namespace MPF.Modules
{
@@ -242,7 +246,7 @@ namespace MPF.Modules
/// </summary>
/// <param name="parameters">String possibly representing parameters</param>
/// <returns>True if the parameters were set correctly, false otherwise</returns>
protected virtual bool ValidateAndSetParameters(string parameters) => true;
protected virtual bool ValidateAndSetParameters(string parameters) => !string.IsNullOrWhiteSpace(parameters);
#endregion
@@ -344,7 +348,7 @@ namespace MPF.Modules
return BitConverter.ToString(bytes).Replace("-", string.Empty);
}
return string.Join("\n", File.ReadAllLines(filename));
return File.ReadAllText(filename);
}
/// <summary>
@@ -622,6 +626,7 @@ namespace MPF.Modules
string valuePart = commandParts[1];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
return (sbyte)(sbyte.Parse(value) * factor);
}
@@ -698,6 +703,7 @@ namespace MPF.Modules
string valuePart = commandParts[1];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
return (short)(short.Parse(value) * factor);
}
@@ -774,6 +780,7 @@ namespace MPF.Modules
string valuePart = commandParts[1];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
return (int)(int.Parse(value) * factor);
}
@@ -850,6 +857,7 @@ namespace MPF.Modules
string valuePart = commandParts[1];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
return long.Parse(value) * factor;
}
@@ -1002,6 +1010,7 @@ namespace MPF.Modules
string valuePart = commandParts[1];
this[longFlagString] = true;
(string value, long factor) = ExtractFactorFromValue(valuePart);
return (byte)(byte.Parse(value) * factor);
}
@@ -1075,6 +1084,102 @@ namespace MPF.Modules
#region Common Information Extraction
/// <summary>
/// Generate the proper datfile from the input Datafile, if possible
/// </summary>
/// <param name="datafile">.dat file location</param>
/// <returns>Relevant pieces of the datfile, null on error</returns>
protected static string GenerateDatfile(Datafile datafile)
{
// If we don't have a valid datafile, we can't do anything
if (datafile?.Games == null || datafile.Games.Length == 0 || datafile.Games[0]?.Roms == null || datafile.Games[0].Roms.Length == 0)
return null;
// Otherwise, reconstruct the hash data with only the required info
try
{
var roms = datafile.Games[0].Roms;
string datString = string.Empty;
for (int i = 0; i < roms.Length; i++)
{
var rom = roms[i];
datString += $"<rom name=\"{rom.Name}\" size=\"{rom.Size}\" crc=\"{rom.Crc}\" md5=\"{rom.Md5}\" sha1=\"{rom.Sha1}\" />\n";
}
datString.TrimEnd('\n');
return datString;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get Datafile from a standard DAT
/// </summary>
/// <param name="dat">Path to the DAT file to parse</param>
/// <returns>Filled Datafile on success, null on error</returns>
protected static Datafile GetDatafile(string dat)
{
// If there's no path, we can't read the file
if (string.IsNullOrWhiteSpace(dat))
return null;
// If the file doesn't exist, we can't read it
if (!File.Exists(dat))
return null;
try
{
// Open and read in the XML file
XmlReader xtr = XmlReader.Create(dat, new XmlReaderSettings
{
CheckCharacters = false,
DtdProcessing = DtdProcessing.Ignore,
IgnoreComments = true,
IgnoreWhitespace = true,
ValidationFlags = XmlSchemaValidationFlags.None,
ValidationType = ValidationType.None,
});
// If the reader is null for some reason, we can't do anything
if (xtr == null)
return null;
XmlSerializer serializer = new XmlSerializer(typeof(Datafile));
Datafile obj = serializer.Deserialize(xtr) as Datafile;
return obj;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Gets disc information from a PIC file
/// </summary>
/// <param name="pic">Path to a PIC.bin file</param>
/// <returns>Filled DiscInformation on success, null on error</returns>
/// <remarks>This omits the emergency brake information, if it exists</remarks>
protected static DiscInformation GetDiscInformation(string pic)
{
try
{
return new SabreTools.Serialization.Files.PIC().Deserialize(pic);
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get hashes from an input file path
/// </summary>
@@ -1182,6 +1287,22 @@ namespace MPF.Modules
return false;
}
/// <summary>
/// Get the last modified date from a file path, if possible
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <returns>Filled DateTime on success, null on failure</returns>
protected static DateTime? GetFileModifiedDate(string filename, bool fallback = false)
{
if (string.IsNullOrWhiteSpace(filename))
return fallback ? (DateTime?)DateTime.UtcNow : null;
else if (!File.Exists(filename))
return fallback ? (DateTime?)DateTime.UtcNow : null;
var fi = new FileInfo(filename);
return fi.LastWriteTimeUtc;
}
/// <summary>
/// Get the split values for ISO-based media
/// </summary>
@@ -1194,6 +1315,8 @@ namespace MPF.Modules
if (string.IsNullOrWhiteSpace(hashData))
return false;
// TODO: Use deserialization to Rom instead of Regex
Regex hashreg = new Regex(@"<rom name="".*?"" size=""(.*?)"" crc=""(.*?)"" md5=""(.*?)"" sha1=""(.*?)""");
Match m = hashreg.Match(hashData);
if (m.Success)
@@ -1210,6 +1333,92 @@ namespace MPF.Modules
}
}
/// <summary>
/// Get the split values for ISO-based media
/// </summary>
/// <param name="datafile">Datafile represenging the hash data</param>
/// <returns>True if extraction was successful, false otherwise</returns>
protected static bool GetISOHashValues(Datafile datafile, out long size, out string crc32, out string md5, out string sha1)
{
size = -1; crc32 = null; md5 = null; sha1 = null;
if (datafile?.Games == null || datafile.Games.Length == 0 || datafile.Games[0].Roms.Length == 0)
return false;
var rom = datafile.Games[0].Roms[0];
Int64.TryParse(rom.Size, out size);
crc32 = rom.Crc;
md5 = rom.Md5;
sha1 = rom.Sha1;
return true;
}
/// <summary>
/// Get the layerbreak info associated from the disc information
/// </summary>
/// <param name="di">Disc information containing unformatted data</param>
/// <returns>True if layerbreak info was set, false otherwise</returns>
protected static bool GetLayerbreaks(DiscInformation di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)
{
// Set the default values
layerbreak1 = null; layerbreak2 = null; layerbreak3 = null;
// If we don't have valid disc information, we can't do anything
if (di?.Units == null || di.Units.Length <= 1)
return false;
int ReadFromArrayBigEndian(byte[] bytes, int offset)
{
var span = new ReadOnlySpan<byte>(bytes, offset, 0x04);
byte[] rev = span.ToArray();
Array.Reverse(rev);
return BitConverter.ToInt32(rev, 0);
}
// Layerbreak 1 (2+ layers)
if (di.Units.Length >= 2)
{
long offset = ReadFromArrayBigEndian(di.Units[0].Body.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[0].Body.FormatDependentContents, 0x10);
layerbreak1 = value - offset + 2;
}
// Layerbreak 2 (3+ layers)
if (di.Units.Length >= 3)
{
long offset = ReadFromArrayBigEndian(di.Units[1].Body.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[1].Body.FormatDependentContents, 0x10);
layerbreak2 = layerbreak1 + value - offset + 2;
}
// Layerbreak 3 (4 layers)
if (di.Units.Length >= 4)
{
long offset = ReadFromArrayBigEndian(di.Units[2].Body.FormatDependentContents, 0x0C);
long value = ReadFromArrayBigEndian(di.Units[2].Body.FormatDependentContents, 0x10);
layerbreak3 = layerbreak2 + value - offset + 2;
}
return true;
}
/// <summary>
/// Get the PIC identifier from the first disc information unit, if possible
/// </summary>
/// <param name="di">Disc information containing the data</param>
/// <returns>String representing the PIC identifier, null on error</returns>
protected static string GetPICIdentifier(DiscInformation di)
{
// If we don't have valid disc information, we can't do anything
if (di?.Units == null || di.Units.Length <= 1)
return null;
// We assume the identifier is consistent across all units
return di.Units[0].Body.DiscTypeIdentifier;
}
/// <summary>
/// Get the EXE date from a PlayStation disc, if possible
/// </summary>
@@ -1266,6 +1475,9 @@ namespace MPF.Modules
serial = exeName
.Replace('_', '-')
.Replace(".", string.Empty);
// Some games may have the EXE in a subfolder
serial = Path.GetFileName(serial);
}
}
@@ -1322,6 +1534,80 @@ namespace MPF.Modules
return null;
}
/// <summary>
/// Get the internal serial from a PlayStation 3 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 GetPlayStation3Serial(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 3 disc
string paramSfoPath = Path.Combine(drivePath, "PS3_GAME", "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(-0x18, 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 3 disc, if possible
/// </summary>
/// <param name="driveLetter">Drive letter to use to check</param>
/// <returns>Game version if possible, null on error</returns>
protected static string GetPlayStation3Version(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 3 disc
string paramSfoPath = Path.Combine(drivePath, "PS3_GAME", "PARAM.SFO");
if (!File.Exists(paramSfoPath))
return null;
// Let's try reading PARAM.SFO to find the version at the end of the file
try
{
using (BinaryReader br = new BinaryReader(File.OpenRead(paramSfoPath)))
{
br.BaseStream.Seek(-0x08, SeekOrigin.End);
return new string(br.ReadChars(5));
}
}
catch
{
// We don't care what the error was
return null;
}
}
/// <summary>
/// Get the internal serial from a PlayStation 4 disc, if possible
/// </summary>
@@ -1511,10 +1797,33 @@ namespace MPF.Modules
case 'A': return Region.Asia;
case 'C': return Region.China;
case 'E': return Region.Europe;
case 'J': return Region.JapanKorea;
case 'K': return Region.SouthKorea;
case 'P': return Region.Japan;
case 'U': return Region.UnitedStatesOfAmerica;
case 'P':
// Region of S_P_ serials may be Japan, Asia, or SouthKorea
switch (serial[3])
{
case 'S':
// Check first two digits of S_PS serial
switch (serial.Substring(5, 2))
{
case "46": return Region.SouthKorea;
case "56": return Region.SouthKorea;
case "51": return Region.Asia;
case "55": return Region.Asia;
default: return Region.Japan;
}
case 'M':
// Check first three digits of S_PM serial
switch (serial.Substring(5, 3))
{
case "645": return Region.SouthKorea;
case "675": return Region.SouthKorea;
case "885": return Region.SouthKorea;
default: return Region.Japan; // Remaining S_PM serials may be Japan or Asia
}
default: return Region.Japan;
}
}
}
@@ -1526,10 +1835,18 @@ namespace MPF.Modules
else if (serial.StartsWith("PABX"))
return null;
// Region appears entirely random
else if (serial.StartsWith("PBPX"))
return null;
// Japan-only special serial
else if (serial.StartsWith("PCBX"))
return Region.Japan;
// Japan-only special serial
else if (serial.StartsWith("PCXC"))
return Region.Japan;
// Single disc known, Japan
else if (serial.StartsWith("PDBX"))
return Region.Japan;
@@ -1538,6 +1855,10 @@ namespace MPF.Modules
else if (serial.StartsWith("PEBX"))
return Region.Europe;
// Single disc known, USA
else if (serial.StartsWith("PUBX"))
return Region.UnitedStatesOfAmerica;
return null;
}

View File

@@ -1,10 +1,11 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Modules.CleanRip
{
@@ -63,10 +64,12 @@ namespace MPF.Modules.CleanRip
{
// 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");
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
Datafile datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
// Get the individual hash data, as per internal
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out string crc32, out string md5, out string sha1))
if (GetISOHashValues(datafile, out long size, out string crc32, out string md5, out string sha1))
{
info.SizeAndChecksums.Size = size;
info.SizeAndChecksums.CRC32 = crc32;
@@ -131,6 +134,65 @@ namespace MPF.Modules.CleanRip
#region Information Extraction Methods
/// <summary>
/// Get a formatted datfile from the cleanrip output, if possible
/// </summary>
/// <param name="iso">Path to ISO file</param>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <returns></returns>
private static Datafile GenerateCleanripDatafile(string iso, string dumpinfo)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dumpinfo))
return null;
using (StreamReader sr = File.OpenText(dumpinfo))
{
long size = new FileInfo(iso).Length;
string crc = string.Empty;
string md5 = string.Empty;
string sha1 = string.Empty;
try
{
// Make sure this file is a dumpinfo
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
return null;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
string line = sr.ReadLine().Trim();
if (line.StartsWith("CRC32"))
crc = line.Substring(7).ToLowerInvariant();
else if (line.StartsWith("MD5"))
md5 = line.Substring(5);
else if (line.StartsWith("SHA-1"))
sha1 = line.Substring(7);
}
return new Datafile
{
Games = new Game[]
{
new Game
{
Roms = new Rom[]
{
new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1 },
}
}
}
};
}
catch
{
// We don't care what the exception is right now
return null;
}
}
}
/// <summary>
/// Get the hex contents of the BCA file
/// </summary>

View File

@@ -1,32 +0,0 @@
namespace MPF.Modules.DD
{
/// <summary>
/// Top-level commands for DD
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
public const string List = "--list";
}
/// <summary>
/// Dumping flags for DD
/// </summary>
public static class FlagStrings
{
// Boolean flags
public const string Progress = "--progress";
public const string Size = "--size";
// Int64 flags
public const string BlockSize = "bs";
public const string Count = "count";
public const string Seek = "seek";
public const string Skip = "skip";
// String flags
public const string Filter = "--filter";
public const string InputFile = "if";
public const string OutputFile = "of";
}
}

View File

@@ -1,22 +0,0 @@
using RedumpLib.Data;
namespace MPF.Modules.DD
{
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)
{
// DD has a single, unified output format by default
return ".bin";
}
#endregion
}
}

View File

@@ -1,410 +0,0 @@
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.DD
{
/// <summary>
/// Represents a generic set of DD parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Generic Dumping Information
/// <inheritdoc/>
public override string InputPath => InputFileValue?.TrimStart('\\', '?');
/// <inheritdoc/>
public override string OutputPath => OutputFileValue;
/// <inheritdoc/>
/// <inheritdoc/>
public override int? Speed
{
get { return 1; }
set { }
}
#endregion
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.DD;
#endregion
#region Flag Values
public long? BlockSizeValue { get; set; }
public long? CountValue { get; set; }
// fixed, removable, disk, partition
public string FilterValue { get; set; }
public string InputFileValue { get; set; }
public string OutputFileValue { get; set; }
public long? SeekValue { get; set; }
public long? SkipValue { 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: Figure out what sort of output files are expected... just `.bin`?
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 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
}
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.IsNullOrEmpty(BaseCommand))
parameters.Add(BaseCommand);
#region Boolean flags
// Progress
if (IsFlagSupported(FlagStrings.Progress))
{
if (this[FlagStrings.Progress] == true)
parameters.Add($"{FlagStrings.Progress}");
}
// Size
if (IsFlagSupported(FlagStrings.Size))
{
if (this[FlagStrings.Size] == true)
parameters.Add($"{FlagStrings.Size}");
}
#endregion
#region Int64 flags
// Block Size
if (IsFlagSupported(FlagStrings.BlockSize))
{
if (this[FlagStrings.BlockSize] == true && BlockSizeValue != null)
parameters.Add($"{FlagStrings.BlockSize}={BlockSizeValue}");
}
// Count
if (IsFlagSupported(FlagStrings.Count))
{
if (this[FlagStrings.Count] == true && CountValue != null)
parameters.Add($"{FlagStrings.Count}={CountValue}");
}
// Seek
if (IsFlagSupported(FlagStrings.Seek))
{
if (this[FlagStrings.Seek] == true && SeekValue != null)
parameters.Add($"{FlagStrings.Seek}={SeekValue}");
}
// Skip
if (IsFlagSupported(FlagStrings.Skip))
{
if (this[FlagStrings.Skip] == true && SkipValue != null)
parameters.Add($"{FlagStrings.Skip}={SkipValue}");
}
#endregion
#region String flags
// Filter
if (IsFlagSupported(FlagStrings.Filter))
{
if (this[FlagStrings.Filter] == true && FilterValue != null)
parameters.Add($"{FlagStrings.Filter}={FilterValue}");
}
// Input File
if (IsFlagSupported(FlagStrings.InputFile))
{
if (this[FlagStrings.InputFile] == true && InputFileValue != null)
parameters.Add($"{FlagStrings.InputFile}=\"{InputFileValue}\"");
else
return null;
}
// Output File
if (IsFlagSupported(FlagStrings.OutputFile))
{
if (this[FlagStrings.OutputFile] == true && OutputFileValue != null)
parameters.Add($"{FlagStrings.OutputFile}=\"{OutputFileValue}\"");
else
return null;
}
#endregion
return string.Join(" ", parameters);
}
/// <inheritdoc/>
public override Dictionary<string, List<string>> GetCommandSupport()
{
return new Dictionary<string, List<string>>()
{
[CommandStrings.NONE] = new List<string>()
{
FlagStrings.BlockSize,
FlagStrings.Count,
FlagStrings.Filter,
FlagStrings.InputFile,
FlagStrings.OutputFile,
FlagStrings.Progress,
FlagStrings.Seek,
FlagStrings.Size,
FlagStrings.Skip,
},
[CommandStrings.List] = new List<string>()
{
},
};
}
/// <inheritdoc/>
public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
/// <inheritdoc/>
public override bool IsDumpingCommand()
{
switch (this.BaseCommand)
{
case CommandStrings.List:
return false;
default:
return true;
}
}
/// <inheritdoc/>
protected override void ResetValues()
{
BaseCommand = CommandStrings.NONE;
flags = new Dictionary<string, bool?>();
BlockSizeValue = null;
CountValue = null;
InputFileValue = null;
OutputFileValue = null;
SeekValue = null;
SkipValue = null;
}
/// <inheritdoc/>
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
{
BaseCommand = CommandStrings.NONE;
this[FlagStrings.InputFile] = true;
InputFileValue = $"\\\\?\\{driveLetter}:";
this[FlagStrings.OutputFile] = true;
OutputFileValue = filename;
// TODO: Add more common block sizes
this[FlagStrings.BlockSize] = true;
switch (this.Type)
{
case MediaType.FloppyDisk:
BlockSizeValue = 1440 * 1024;
break;
default:
BlockSizeValue = 1024 * 1024 * 1024;
break;
}
this[FlagStrings.Progress] = true;
this[FlagStrings.Size] = true;
}
/// <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
int start = 0;
if (parts[0] == CommandStrings.List)
{
BaseCommand = parts[0];
start = 1;
}
// Loop through all auxilary flags, if necessary
for (int i = start; i < parts.Count; i++)
{
// Flag read-out values
long? longValue = null;
string stringValue = null;
// Keep a count of keys to determine if we should break out to filename handling or not
int keyCount = Keys.Count();
#region Boolean flags
// Progress
ProcessFlagParameter(parts, FlagStrings.Progress, ref i);
// Size
ProcessFlagParameter(parts, FlagStrings.Size, ref i);
#endregion
#region Int64 flags
// Block Size
longValue = ProcessInt64Parameter(parts, FlagStrings.BlockSize, ref i);
if (longValue != null)
BlockSizeValue = longValue;
// Count
longValue = ProcessInt64Parameter(parts, FlagStrings.Count, ref i);
if (longValue != null)
CountValue = longValue;
// Seek
longValue = ProcessInt64Parameter(parts, FlagStrings.Seek, ref i);
if (longValue != null)
SeekValue = longValue;
// Skip
longValue = ProcessInt64Parameter(parts, FlagStrings.Skip, ref i);
if (longValue != null)
SkipValue = longValue;
#endregion
#region String flags
// Filter (fixed, removable, disk, partition)
stringValue = ProcessStringParameter(parts, FlagStrings.Filter, ref i);
if (!string.IsNullOrEmpty(stringValue))
FilterValue = stringValue;
// Input File
stringValue = ProcessStringParameter(parts, FlagStrings.InputFile, ref i);
if (!string.IsNullOrEmpty(stringValue))
InputFileValue = stringValue;
// Output File
stringValue = ProcessStringParameter(parts, FlagStrings.OutputFile, ref i);
if (!string.IsNullOrEmpty(stringValue))
OutputFileValue = stringValue;
#endregion
}
return true;
}
#endregion
}
}

73
MPF.Modules/Datafile.cs Normal file
View File

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

View File

@@ -26,6 +26,7 @@ namespace MPF.Modules.DiscImageCreator
public const string Sub = "sub";
public const string Swap = "swap";
public const string Tape = "tape";
public const string Version = "/v";
public const string XBOX = "xbox";
public const string XBOXSwap = "xboxswap";
public const string XGD2Swap = "xgd2swap";
@@ -44,13 +45,13 @@ namespace MPF.Modules.DiscImageCreator
public const string C2Opcode = "/c2";
public const string CopyrightManagementInformation = "/c";
public const string D8Opcode = "/d8";
public const string DatExpand = "/d";
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";
public const string MultiSectorRead = "/mr";
public const string MultiSession = "/ms";
public const string NoFixSubP = "/np";
public const string NoFixSubQ = "/nq";
public const string NoFixSubQLibCrypt = "/nl";
@@ -58,6 +59,7 @@ namespace MPF.Modules.DiscImageCreator
public const string NoFixSubQSecuROM = "/ns";
public const string NoSkipSS = "/nss";
public const string PadSector = "/ps";
public const string Range = "/ra";
public const string Raw = "/raw";
public const string Resume = "/re";
public const string Reverse = "/r";
@@ -68,7 +70,6 @@ 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

@@ -1,4 +1,4 @@
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Modules.DiscImageCreator
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2022</Copyright>
<Copyright>Copyright (c)2019-2023</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
@@ -22,11 +20,12 @@
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.CueSheets\MPF.CueSheets.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.Models" Version="1.1.2" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="SabreTools.Serialization" Version="1.1.2" />
<PackageReference Include="System.Memory" Version="4.5.5" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">

View File

@@ -7,11 +7,17 @@ namespace MPF.Modules.Redumper
{
public const string NONE = "";
public const string CD = "cd";
public const string DVD = "dvd"; // Synonym for CD
public const string BluRay = "bd"; // Synonym for CD
public const string SACD = "sacd"; // Synonym for CD
public const string Dump = "dump";
public const string Info = "info";
public const string Protection = "protection";
public const string Refine = "refine";
public const string Split = "split";
public const string Verify = "verify";
public const string DVDKey = "dvdkey";
public const string DVDIsoKey = "dvdisokey";
}
/// <summary>
@@ -19,32 +25,49 @@ namespace MPF.Modules.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";
// General
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";
public const string Debug = "--debug";
public const string Drive = "--drive";
public const string Speed = "--speed";
public const string Retries = "--retries";
public const string ImagePath = "--image-path";
public const string ImageName = "--image-name";
public const string Overwrite = "--overwrite";
// Drive Configuration
public const string DriveType = "--drive-type";
public const string DriveReadOffset = "--drive-read-offset";
public const string DriveC2Shift = "--drive-c2-shift";
public const string DrivePregapStart = "--drive-pregap-start";
public const string DriveReadMethod = "--drive-read-method";
public const string DriveSectorOrder = "--drive-sector-order";
// Drive Specific
public const string PlextorLeadinSkip = "--plextor-leadin-skip";
public const string PlextorLeadinRetries = "--plextor-leadin-retries";
public const string AsusSkipLeadout = "--asus-skip-leadout";
// Offset
public const string ForceOffset = "--force-offset";
public const string AudioSilenceThreshold = "--audio-silence-threshold";
public const string CorrectOffsetShift = "--correct-offset-shift";
public const string OffsetShiftRelocate = "--offset-shift-relocate";
// Split
public const string ForceSplit = "--force-split";
public const string LeaveUnchanged = "--leave-unchanged";
public const string ForceQTOC = "--force-qtoc";
public const string SkipFill = "--skip-fill";
public const string ISO9660Trim = "--iso9660-trim";
// Miscellaneous
public const string LBAStart = "--lba-start";
public const string LBAEnd = "--lba-end";
public const string RefineSubchannel = "--refine-subchannel";
public const string Skip = "--skip";
public const string DumpReadSize = "--dump-read-size";
}
}

View File

@@ -1,4 +1,4 @@
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Modules.Redumper
{
@@ -13,8 +13,18 @@ namespace MPF.Modules.Redumper
/// <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";
switch (type)
{
case MediaType.CDROM:
return ".bin";
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
return ".iso";
case MediaType.NONE:
default:
return null;
}
}
#endregion

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,7 @@ using System.IO;
using System.Linq;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Utilities;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Modules.UmdImageCreator
{
@@ -66,12 +65,13 @@ namespace MPF.Modules.UmdImageCreator
{
// TODO: Determine if there's a UMDImageCreator version anywhere
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.UMD:
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? "";
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
if (GetFileHashes(basePath + ".iso", out long filesize, out string crc32, out string md5, out string sha1))
{
@@ -83,9 +83,9 @@ namespace MPF.Modules.UmdImageCreator
if (GetUMDAuxInfo(basePath + "_disc.txt", out string title, out DiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize))
{
info.CommonDiscInfo.Title = title ?? "";
info.CommonDiscInfo.Title = title ?? string.Empty;
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
info.VersionAndEditions.Version = umdversion ?? "";
info.VersionAndEditions.Version = umdversion ?? string.Empty;
info.SizeAndChecksums.Size = umdsize;
if (!string.IsNullOrWhiteSpace(umdlayer))
@@ -100,6 +100,8 @@ namespace MPF.Modules.UmdImageCreator
{
if (File.Exists(basePath + "_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt"));
if (File.Exists(basePath + "_drive.txt"))
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt"));
if (File.Exists(basePath + "_mainError.txt"))
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt"));
if (File.Exists(basePath + "_mainInfo.txt"))
@@ -118,6 +120,8 @@ namespace MPF.Modules.UmdImageCreator
case MediaType.UMD:
if (File.Exists($"{basePath}_disc.txt"))
logFiles.Add($"{basePath}_disc.txt");
if (File.Exists($"{basePath}_drive.txt"))
logFiles.Add($"{basePath}_drive.txt");
if (File.Exists($"{basePath}_mainError.txt"))
logFiles.Add($"{basePath}_mainError.txt");
if (File.Exists($"{basePath}_mainInfo.txt"))

View File

@@ -22,13 +22,13 @@ namespace MPF.Test.Core.Data
[InlineData("AV00100W\0", "AV", "001", "00", 'W')]
public void XGD1ValidTests(string validString, string publisher, string gameId, string version, char regionIdentifier)
{
XgdInfo xgdInfo = new XgdInfo(validString, validate: true);
XgdInfo xgdInfo = new XgdInfo(validString);
Assert.True(xgdInfo.Initialized);
Assert.Equal(publisher, xgdInfo.PublisherIdentifier);
Assert.Equal(gameId, xgdInfo.GameID);
Assert.Equal(version, xgdInfo.SKU);
Assert.Equal(regionIdentifier, xgdInfo.RegionIdentifier);
Assert.Equal(publisher, xgdInfo.XMID.PublisherIdentifier);
Assert.Equal(gameId, xgdInfo.XMID.GameID);
Assert.Equal(version, xgdInfo.XMID.VersionNumber);
Assert.Equal(regionIdentifier, xgdInfo.XMID.RegionIdentifier);
}
[Theory]
@@ -40,7 +40,7 @@ namespace MPF.Test.Core.Data
[InlineData("AV00000Z\0")]
public void XGD1InvalidTests(string invalidString)
{
XgdInfo xgdInfo = new XgdInfo(invalidString, validate: true);
XgdInfo xgdInfo = new XgdInfo(invalidString);
Assert.False(xgdInfo.Initialized);
}
@@ -55,18 +55,18 @@ namespace MPF.Test.Core.Data
[InlineData("AV200100W01F11DEADBEEF\0", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
public void XGD23ValidTests(string validString, string publisher, string gameId, string sku, char regionIdentifier, string baseVersion, char mediaSubtype, string discNumber, string cert)
{
XgdInfo xgdInfo = new XgdInfo(validString, validate: true);
XgdInfo xgdInfo = new XgdInfo(validString);
Assert.True(xgdInfo.Initialized);
Assert.Equal(publisher, xgdInfo.PublisherIdentifier);
Assert.Equal('2', xgdInfo.PlatformIdentifier);
Assert.Equal(gameId, xgdInfo.GameID);
Assert.Equal(sku, xgdInfo.SKU);
Assert.Equal(regionIdentifier, xgdInfo.RegionIdentifier);
Assert.Equal(baseVersion, xgdInfo.BaseVersion);
Assert.Equal(mediaSubtype, xgdInfo.MediaSubtypeIdentifier);
Assert.Equal(discNumber, xgdInfo.DiscNumberIdentifier);
Assert.Equal(cert, xgdInfo.CertificationSubmissionIdentifier);
Assert.Equal(publisher, xgdInfo.XeMID.PublisherIdentifier);
Assert.Equal('2', xgdInfo.XeMID.PlatformIdentifier);
Assert.Equal(gameId, xgdInfo.XeMID.GameID);
Assert.Equal(sku, xgdInfo.XeMID.SKU);
Assert.Equal(regionIdentifier, xgdInfo.XeMID.RegionIdentifier);
Assert.Equal(baseVersion, xgdInfo.XeMID.BaseVersion);
Assert.Equal(mediaSubtype, xgdInfo.XeMID.MediaSubtypeIdentifier);
Assert.Equal(discNumber, xgdInfo.XeMID.DiscNumberIdentifier);
Assert.Equal(cert, xgdInfo.XeMID.CertificationSubmissionIdentifier);
}
[Theory]
@@ -108,7 +108,7 @@ namespace MPF.Test.Core.Data
[InlineData("AV200000W00A0000000000\0")]
public void XGD23InvalidTests(string invalidString)
{
XgdInfo xgdInfo = new XgdInfo(invalidString, validate: true);
XgdInfo xgdInfo = new XgdInfo(invalidString);
Assert.False(xgdInfo.Initialized);
}
}

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using MPF.Core.Utilities;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Core.Utilities
@@ -31,10 +31,12 @@ namespace MPF.Test.Core.Utilities
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem,
RedumpSystem.AudioCD,
RedumpSystem.DVDAudio,
RedumpSystem.HasbroiONEducationalGamingSystem,
RedumpSystem.HasbroVideoNow,
RedumpSystem.HasbroVideoNowColor,
RedumpSystem.HasbroVideoNowJr,
RedumpSystem.HasbroVideoNowXP,
RedumpSystem.PlayStationGameSharkUpdates,
RedumpSystem.PhilipsCDi,
RedumpSystem.SuperAudioCD,
};
@@ -58,6 +60,7 @@ namespace MPF.Test.Core.Utilities
RedumpSystem.SonyPlayStation2,
RedumpSystem.SonyPlayStation3,
RedumpSystem.SonyPlayStation4,
RedumpSystem.SonyPlayStationPortable,
};
/// <summary>

View File

@@ -1,7 +1,7 @@
using System.IO;
using MPF.Core.Data;
using MPF.Library;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Library
@@ -24,7 +24,7 @@ namespace MPF.Test.Library
? 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);
var env = new DumpEnvironment(options, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, null, parameters);
bool actual = env.ParametersValid();
Assert.Equal(expected, actual);

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.IO;
using MPF.Library;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Library
@@ -25,9 +26,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")]
@@ -43,24 +44,27 @@ namespace MPF.Test.Library
long layerbreak3,
string expected)
{
string actual = InfoTool.GetFixedMediaType(mediaType, size, layerbreak, layerbreak2, layerbreak3);
// TODO: Add tests around BDU
string actual = InfoTool.GetFixedMediaType(mediaType, null, size, layerbreak, layerbreak2, layerbreak3);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(null, null, null, null)]
[InlineData(" ", "", " ", "")]
[InlineData("super", "blah.bin", "super", "blah.bin")]
[InlineData("super\\hero", "blah.bin", "super\\hero", "blah.bin")]
[InlineData("super.hero", "blah.bin", "super.hero", "blah.bin")]
[InlineData("superhero", "blah.rev.bin", "superhero", "blah.rev.bin")]
[InlineData("super&hero", "blah.bin", "super&hero", "blah.bin")]
[InlineData("superhero", "blah&foo.bin", "superhero", "blah&foo.bin")]
public void NormalizeOutputPathsTest(string outputDirectory, string outputFilename, string expectedOutputDirectory, string expectedOutputFilename)
[InlineData(null, null)]
[InlineData(" ", " ")]
[InlineData("super\\blah.bin", "super\\blah.bin")]
[InlineData("super\\hero\\blah.bin", "super\\hero\\blah.bin")]
[InlineData("super.hero\\blah.bin", "super.hero\\blah.bin")]
[InlineData("superhero\\blah.rev.bin", "superhero\\blah.rev.bin")]
[InlineData("super&hero\\blah.bin", "super&hero\\blah.bin")]
[InlineData("superhero\\blah&foo.bin", "superhero\\blah&foo.bin")]
public void NormalizeOutputPathsTest(string outputPath, string expectedPath)
{
(string actualOutputDirectory, string actualOutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename);
Assert.Equal(expectedOutputDirectory, actualOutputDirectory);
Assert.Equal(expectedOutputFilename, actualOutputFilename);
if (!string.IsNullOrWhiteSpace(expectedPath))
expectedPath = Path.GetFullPath(expectedPath);
string actualPath = InfoTool.NormalizeOutputPaths(outputPath);
Assert.Equal(expectedPath, actualPath);
}
[Fact]
@@ -72,13 +76,21 @@ namespace MPF.Test.Library
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = "This is a comments line\n[T:ISBN] ISBN Value",
#if NET48
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
CommentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
},
Contents = "This is a contents line\n[T:GF] Game Footage",
#if NET48
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
ContentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.Patches] = "1.04 patch",
},
@@ -128,13 +140,21 @@ namespace MPF.Test.Library
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = null,
#if NET48
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
CommentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
},
Contents = null,
#if NET48
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
ContentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.Patches] = "1.04 patch",
},

View File

@@ -178,43 +178,6 @@ namespace MPF.Test.Library
Assert.Equal("Anything Else Protection", sanitized);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
[InlineData(4)]
[InlineData(5)]
[InlineData(6)]
[InlineData(7)]
[InlineData(8)]
public void SanitizeFoundProtectionsSafeDiscTest(int skip)
{
List<string> protections = new List<string>()
{
"SafeDisc 1.20.000",
"SafeDisc (drvmgt.dll) 1.2.0",
"SafeDisc (secdrv.sys) 1.2.0",
"SafeDisc (dplayerx.dll) 1.2.0",
"SafeDisc 3.20-4.xx (version removed)",
"SafeDisc 2",
"SafeDisc 1/Lite",
"SafeDisc Lite",
"SafeDisc 1-3",
"SafeDisc",
};
// Safeguard for the future
if (skip >= protections.Count)
throw new ArgumentException("Invalid skip value", nameof(skip));
// The list is in order of preference
protections = protections.Skip(skip).ToList();
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal(protections[0], sanitized);
}
[Theory]
[InlineData(0)]
[InlineData(1)]

View File

@@ -1,8 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup>
@@ -31,25 +30,25 @@
<ProjectReference Include="..\MPF\MPF.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup>
<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.2" />
<PackageReference Include="Microsoft.CodeCoverage" Version="17.6.3" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="xunit" Version="2.5.0" />
<PackageReference Include="xunit.abstractions" Version="2.0.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">
<PackageReference Include="xunit.analyzers" Version="1.2.0" />
<PackageReference Include="xunit.assert" Version="2.5.0" />
<PackageReference Include="xunit.core" Version="2.5.0" />
<PackageReference Include="xunit.extensibility.core" Version="2.5.0" />
<PackageReference Include="xunit.extensibility.execution" Version="2.5.0" />
<PackageReference Include="xunit.runner.console" Version="2.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic;
using MPF.Core.Data;
using MPF.Modules.DiscImageCreator;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Modules

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.RedumpLib
@@ -18,9 +18,9 @@ namespace MPF.Test.RedumpLib
private static readonly DiscType?[] _mappableDiscTypes = new DiscType?[]
{
DiscType.BD25,
//DiscType.BD33,
DiscType.BD33,
DiscType.BD50,
//DiscType.BD66,
DiscType.BD66,
DiscType.BD100,
DiscType.BD128,
DiscType.CD,

View File

@@ -1,7 +1,8 @@
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using RedumpLib.Data;
using psxt001z;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.RedumpLib
@@ -81,12 +82,20 @@ namespace MPF.Test.RedumpLib
EXEDateBuildDate = "19xx-xx-xx",
ErrorsCount = "0",
Comments = "Comment data line 1\r\nComment data line 2",
#if NET48
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
CommentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.ISBN] = "ISBN",
},
Contents = "Special contents 1\r\nSpecial contents 2",
#if NET48
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
#else
ContentsSpecialFields = new Dictionary<SiteCode, string>()
#endif
{
[SiteCode.PlayableDemos] = "Game Demo 1",
},
@@ -160,6 +169,7 @@ namespace MPF.Test.RedumpLib
DumpingInfo = new DumpingInfoSection()
{
DumpingProgram = "DiscImageCreator 20500101",
DumpingDate = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
Manufacturer = "ATAPI",
Model = "Optical Drive",
Firmware = "1.23",

View File

@@ -1,6 +1,6 @@
using System.Linq;
using MPF.Core.Data;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Data

View File

@@ -3,9 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using MPF.Core.Utilities;
using MPF.UI.Core.ComboBoxItems;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF
namespace MPF.UI.Core.ComboBoxItems
{
/// <summary>
/// Represents a single item in the System combo box

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
namespace MPF
namespace MPF.UI.Core
{
/// <summary>
/// Variables for UI elements
@@ -13,11 +13,13 @@ namespace MPF
// Private lists of known drive speed ranges
private static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
private static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
private static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
private static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
// Create collections for UI based on known drive speeds
public static DoubleCollection SpeedsForCDAsCollection { get; } = GetDoubleCollectionFromIntList(CD);
public static DoubleCollection SpeedsForDVDAsCollection { get; } = GetDoubleCollectionFromIntList(DVD);
public static DoubleCollection SpeedsForHDDVDAsCollection { get; } = GetDoubleCollectionFromIntList(HDDVD);
public static DoubleCollection SpeedsForBDAsCollection { get; } = GetDoubleCollectionFromIntList(BD);
private static DoubleCollection GetDoubleCollectionFromIntList(IReadOnlyList<int> list)
=> new DoubleCollection(list.Select(i => Convert.ToDouble(i)).ToList());

View File

@@ -1,17 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64</RuntimeIdentifiers>
<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>
<Copyright>Copyright (c)2019-2023</Copyright>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
</PropertyGroup>
@@ -20,15 +18,9 @@
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
<NrtShowRevision>false</NrtShowRevision>
<UserSecretsId>27abb4ca-bf7a-431e-932f-49153303d5ff</UserSecretsId>
</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" />
@@ -36,13 +28,21 @@
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationFramework.Aero" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Images\" />
</ItemGroup>

400
MPF.UI.Core/Theme.cs Normal file
View File

@@ -0,0 +1,400 @@
using System.Windows;
using System.Windows.Media;
namespace MPF.UI.Core
{
/// <summary>
/// Represents all required mapping values for the UI
/// </summary>
public class Theme
{
#region Application-Wide
/// <summary>
/// SolidColorBrush used to paint the active window's border.
/// </summary>
public SolidColorBrush ActiveBorderBrush { get; set; }
/// <summary>
/// SolidColorBrush that paints the face of a three-dimensional display element.
/// </summary>
public SolidColorBrush ControlBrush { get; set; }
/// <summary>
/// SolidColorBrush that paints text in a three-dimensional display element.
/// </summary>
public SolidColorBrush ControlTextBrush { get; set; }
/// <summary>
/// SolidColorBrush that paints disabled text.
/// </summary>
public SolidColorBrush GrayTextBrush { get; set; }
/// <summary>
/// SolidColorBrush that paints the background of a window's client area.
/// </summary>
public SolidColorBrush WindowBrush { get; set; }
/// <summary>
/// SolidColorBrush that paints the text in the client area of a window.
/// </summary>
public SolidColorBrush WindowTextBrush { get; set; }
#endregion
#region Button
/// <summary>
/// Brush for the Button.Disabled.Background resource
/// </summary>
public Brush Button_Disabled_Background { get; set; }
/// <summary>
/// Brush for the Button.MouseOver.Background resource
/// </summary>
public Brush Button_MouseOver_Background { get; set; }
/// <summary>
/// Brush for the Button.Pressed.Background resource
/// </summary>
public Brush Button_Pressed_Background { get; set; }
/// <summary>
/// Brush for the Button.Static.Background resource
/// </summary>
public Brush Button_Static_Background { get; set; }
#endregion
#region ComboBox
/// <summary>
/// Brush for the ComboBox.Disabled.Background resource
/// </summary>
public Brush ComboBox_Disabled_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.Disabled.Editable.Background resource
/// </summary>
public Brush ComboBox_Disabled_Editable_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.Disabled.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_Disabled_Editable_Button_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.MouseOver.Background resource
/// </summary>
public Brush ComboBox_MouseOver_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.MouseOver.Editable.Background resource
/// </summary>
public Brush ComboBox_MouseOver_Editable_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.MouseOver.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_MouseOver_Editable_Button_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.Pressed.Background resource
/// </summary>
public Brush ComboBox_Pressed_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.Pressed.Editable.Background resource
/// </summary>
public Brush ComboBox_Pressed_Editable_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.Pressed.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_Pressed_Editable_Button_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.Static.Background resource
/// </summary>
public Brush ComboBox_Static_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.Static.Editable.Background resource
/// </summary>
public Brush ComboBox_Static_Editable_Background { get; set; }
/// <summary>
/// Brush for the ComboBox.Static.Editable.Button.Background resource
/// </summary>
public Brush ComboBox_Static_Editable_Button_Background { get; set; }
#endregion
#region CustomMessageBox
/// <summary>
/// Brush for the CustomMessageBox.Static.Background resource
/// </summary>
public Brush CustomMessageBox_Static_Background { get; set; }
#endregion
#region MenuItem
/// <summary>
/// Brush for the MenuItem.SubMenu.Background resource
/// </summary>
public Brush MenuItem_SubMenu_Background { get; set; }
/// <summary>
/// Brush for the MenuItem.SubMenu.Border resource
/// </summary>
public Brush MenuItem_SubMenu_Border { get; set; }
#endregion
#region ProgressBar
/// <summary>
/// Brush for the ProgressBar.Background resource
/// </summary>
public Brush ProgressBar_Background { get; set; }
#endregion
#region ScrollViewer
/// <summary>
/// Brush for the ScrollViewer.ScrollBar.Background resource
/// </summary>
public Brush ScrollViewer_ScrollBar_Background { get; set; }
#endregion
#region TabItem
/// <summary>
/// Brush for the TabItem.Selected.Background resource
/// </summary>
public Brush TabItem_Selected_Background { get; set; }
/// <summary>
/// Brush for the TabItem.Static.Background resource
/// </summary>
public Brush TabItem_Static_Background { get; set; }
/// <summary>
/// Brush for the TabItem.Static.Border resource
/// </summary>
public Brush TabItem_Static_Border { get; set; }
#endregion
#region TextBox
/// <summary>
/// Brush for the TextBox.Static.Background resource
/// </summary>
public Brush TextBox_Static_Background { get; set; }
#endregion
/// <summary>
/// Apply the theme to the current application
/// </summary>
public void Apply()
{
// Handle application-wide resources
Application.Current.Resources[SystemColors.ActiveBorderBrushKey] = this.ActiveBorderBrush;
Application.Current.Resources[SystemColors.ControlBrushKey] = this.ControlBrush;
Application.Current.Resources[SystemColors.ControlTextBrushKey] = this.ControlTextBrush;
Application.Current.Resources[SystemColors.GrayTextBrushKey] = this.GrayTextBrush;
Application.Current.Resources[SystemColors.WindowBrushKey] = this.WindowBrush;
Application.Current.Resources[SystemColors.WindowTextBrushKey] = this.WindowTextBrush;
// Handle Button-specific resources
Application.Current.Resources["Button.Disabled.Background"] = this.Button_Disabled_Background;
Application.Current.Resources["Button.MouseOver.Background"] = this.Button_MouseOver_Background;
Application.Current.Resources["Button.Pressed.Background"] = this.Button_Pressed_Background;
Application.Current.Resources["Button.Static.Background"] = this.Button_Static_Background;
// Handle ComboBox-specific resources
Application.Current.Resources["ComboBox.Disabled.Background"] = this.ComboBox_Disabled_Background;
Application.Current.Resources["ComboBox.Disabled.Editable.Background"] = this.ComboBox_Disabled_Editable_Background;
Application.Current.Resources["ComboBox.Disabled.Editable.Button.Background"] = this.ComboBox_Disabled_Editable_Button_Background;
Application.Current.Resources["ComboBox.MouseOver.Background"] = this.ComboBox_MouseOver_Background;
Application.Current.Resources["ComboBox.MouseOver.Editable.Background"] = this.ComboBox_MouseOver_Editable_Background;
Application.Current.Resources["ComboBox.MouseOver.Editable.Button.Background"] = this.ComboBox_MouseOver_Editable_Button_Background;
Application.Current.Resources["ComboBox.Pressed.Background"] = this.ComboBox_Pressed_Background;
Application.Current.Resources["ComboBox.Pressed.Editable.Background"] = this.ComboBox_Pressed_Editable_Background;
Application.Current.Resources["ComboBox.Pressed.Editable.Button.Background"] = this.ComboBox_Pressed_Editable_Button_Background;
Application.Current.Resources["ComboBox.Static.Background"] = this.ComboBox_Static_Background;
Application.Current.Resources["ComboBox.Static.Editable.Background"] = this.ComboBox_Static_Editable_Background;
Application.Current.Resources["ComboBox.Static.Editable.Button.Background"] = this.ComboBox_Static_Editable_Button_Background;
// Handle CustomMessageBox-specific resources
Application.Current.Resources["CustomMessageBox.Static.Background"] = this.CustomMessageBox_Static_Background;
// Handle MenuItem-specific resources
Application.Current.Resources["MenuItem.SubMenu.Background"] = this.MenuItem_SubMenu_Background;
Application.Current.Resources["MenuItem.SubMenu.Border"] = this.MenuItem_SubMenu_Border;
// Handle ProgressBar-specific resources
Application.Current.Resources["ProgressBar.Background"] = this.ProgressBar_Background;
// Handle ScrollViewer-specific resources
Application.Current.Resources["ScrollViewer.ScrollBar.Background"] = this.ScrollViewer_ScrollBar_Background;
// Handle TabItem-specific resources
Application.Current.Resources["TabItem.Selected.Background"] = this.TabItem_Selected_Background;
Application.Current.Resources["TabItem.Static.Background"] = this.TabItem_Static_Background;
Application.Current.Resources["TabItem.Static.Border"] = this.TabItem_Static_Border;
// Handle TextBox-specific resources
Application.Current.Resources["TextBox.Static.Background"] = this.TextBox_Static_Background;
}
}
/// <summary>
/// Default light-mode theme
/// </summary>
public class LightModeTheme : Theme
{
public LightModeTheme()
{
// Handle application-wide resources
this.ActiveBorderBrush = null;
this.ControlBrush = null;
this.ControlTextBrush = null;
this.GrayTextBrush = null;
this.WindowBrush = null;
this.WindowTextBrush = null;
// Handle Button-specific resources
this.Button_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF4));
this.Button_MouseOver_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xBE, 0xE6, 0xFD));
this.Button_Pressed_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xC4, 0xE5, 0xF6));
this.Button_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xDD, 0xDD, 0xDD));
// Handle ComboBox-specific resources
this.ComboBox_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
this.ComboBox_Disabled_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
this.ComboBox_Disabled_Editable_Button_Background = Brushes.Transparent;
this.ComboBox_MouseOver_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xEC, 0xF4, 0xFC),
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
new Point(0, 0),
new Point(0, 1));
this.ComboBox_MouseOver_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
this.ComboBox_MouseOver_Editable_Button_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xEB, 0xF4, 0xFC),
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
new Point(0, 0),
new Point(0, 1));
this.ComboBox_Pressed_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xDA, 0xEC, 0xFC),
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
new Point(0, 0),
new Point(0, 1));
this.ComboBox_Pressed_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
this.ComboBox_Pressed_Editable_Button_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xDA, 0xEB, 0xFC),
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
new Point(0, 0),
new Point(0, 1));
this.ComboBox_Static_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
new Point(0, 0),
new Point(0, 1));
this.ComboBox_Static_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
this.ComboBox_Static_Editable_Button_Background = Brushes.Transparent;
// Handle CustomMessageBox-specific resources
this.CustomMessageBox_Static_Background = null;
// Handle MenuItem-specific resources
this.MenuItem_SubMenu_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
// Handle ProgressBar-specific resources
this.ProgressBar_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xE6, 0xE6, 0xE6));
// Handle ScrollViewer-specific resources
this.ScrollViewer_ScrollBar_Background = Brushes.LightGray;
// Handle TabItem-specific resources
this.TabItem_Selected_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
this.TabItem_Static_Background = new LinearGradientBrush(
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
new Point(0, 0),
new Point(0, 1));
this.TabItem_Static_Border = Brushes.DarkGray;
// Handle TextBox-specific resources
this.TextBox_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
}
}
/// <summary>
/// Default dark-mode theme
/// </summary>
public class DarkModeTheme : Theme
{
public DarkModeTheme()
{
// Setup needed brushes
var darkModeBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, 0x20, 0x20, 0x20) };
// Handle application-wide resources
this.ActiveBorderBrush = Brushes.Black;
this.ControlBrush = darkModeBrush;
this.ControlTextBrush = Brushes.White;
this.GrayTextBrush = Brushes.DarkGray;
this.WindowBrush = darkModeBrush;
this.WindowTextBrush = Brushes.White;
// Handle Button-specific resources
this.Button_Disabled_Background = darkModeBrush;
this.Button_MouseOver_Background = darkModeBrush;
this.Button_Pressed_Background = darkModeBrush;
this.Button_Static_Background = darkModeBrush;
// Handle ComboBox-specific resources
this.ComboBox_Disabled_Background = darkModeBrush;
this.ComboBox_Disabled_Editable_Background = darkModeBrush;
this.ComboBox_Disabled_Editable_Button_Background = darkModeBrush;
this.ComboBox_MouseOver_Background = darkModeBrush;
this.ComboBox_MouseOver_Editable_Background = darkModeBrush;
this.ComboBox_MouseOver_Editable_Button_Background = darkModeBrush;
this.ComboBox_Pressed_Background = darkModeBrush;
this.ComboBox_Pressed_Editable_Background = darkModeBrush;
this.ComboBox_Pressed_Editable_Button_Background = darkModeBrush;
this.ComboBox_Static_Background = darkModeBrush;
this.ComboBox_Static_Editable_Background = darkModeBrush;
this.ComboBox_Static_Editable_Button_Background = darkModeBrush;
// Handle CustomMessageBox-specific resources
this.CustomMessageBox_Static_Background = darkModeBrush;
// Handle MenuItem-specific resources
this.MenuItem_SubMenu_Background = darkModeBrush;
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
// Handle ProgressBar-specific resources
this.ProgressBar_Background = darkModeBrush;
// Handle ScrollViewer-specific resources
this.ScrollViewer_ScrollBar_Background = darkModeBrush;
// Handle TabItem-specific resources
this.TabItem_Selected_Background = darkModeBrush;
this.TabItem_Static_Background = darkModeBrush;
this.TabItem_Static_Border = Brushes.DarkGray;
// Handle TextBox-specific resources
this.TextBox_Static_Background = darkModeBrush;
}
}
}

View File

@@ -1,9 +1,8 @@
<UserControl x:Class="MPF.UserControls.LogOutput"
<UserControl x:Class="MPF.UI.Core.UserControls.LogOutput"
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"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MPF.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>

View File

@@ -1,7 +1,7 @@
using System.Windows.Controls;
using MPF.UI.ViewModels;
using MPF.UI.Core.ViewModels;
namespace MPF.UserControls
namespace MPF.UI.Core.UserControls
{
public partial class LogOutput : UserControl
{

View File

@@ -5,7 +5,7 @@ using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.UI.Core.ComboBoxItems;
using MPF.UI.Core.Windows;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.UI.Core.ViewModels
{
@@ -322,6 +322,8 @@ namespace MPF.UI.Core.ViewModels
Parent.PVD.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Multisession) != true)
Parent.Multisession.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.RingNonZeroDataStart) != true)
Parent.RingNonZeroDataStart.Visibility = Visibility.Collapsed;
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.SecuROMData))
Parent.SecuROMData.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSHash) != true)
@@ -330,6 +332,8 @@ namespace MPF.UI.Core.ViewModels
Parent.SecuritySectorRanges.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSVersion) != true)
Parent.SSVersion.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.UniversalHash) != true)
Parent.UniversalHash.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.VolumeLabel) != true)
Parent.VolumeLabel.Visibility = Visibility.Collapsed;
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.XeMID) != true)
@@ -391,6 +395,8 @@ namespace MPF.UI.Core.ViewModels
// Physical Identifiers
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.BBFCRegistrationNumber))
Parent.BBFCRegistrationNumberTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BBFCRegistrationNumber];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.CDProjektID))
Parent.CDProjektIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.CDProjektID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DiscHologramID))
Parent.DiscHologramIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DiscHologramID];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DNASDiscID))
@@ -465,10 +471,14 @@ namespace MPF.UI.Core.ViewModels
Parent.Multisession.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PFIHash))
Parent.PFIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.RingNonZeroDataStart))
Parent.RingNonZeroDataStart.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSHash))
Parent.SSHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSVersion))
Parent.SSVersion.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.UniversalHash))
Parent.UniversalHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VolumeLabel))
Parent.VolumeLabel.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel];
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.XeMID))
@@ -526,9 +536,17 @@ namespace MPF.UI.Core.ViewModels
// Initialize the dictionaries, if needed
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields == null)
#if NET48
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields == null)
#if NET48
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields = new Dictionary<SiteCode?, string>();
#else
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields = new Dictionary<SiteCode, string>();
#endif
#region Comment Fields
@@ -542,6 +560,7 @@ namespace MPF.UI.Core.ViewModels
// Physical Identifiers
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BBFCRegistrationNumber] = Parent.BBFCRegistrationNumberTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.CDProjektID] = Parent.CDProjektIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DiscHologramID] = Parent.DiscHologramIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DNASDiscID] = Parent.DNASDiscIDTextBox.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISBN] = Parent.ISBNTextBox.Text;
@@ -581,8 +600,10 @@ namespace MPF.UI.Core.ViewModels
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = Parent.InternalSerialName.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession] = Parent.Multisession.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = Parent.PFIHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart] = Parent.RingNonZeroDataStart.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = Parent.SSHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = Parent.SSVersion.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash] = Parent.UniversalHash.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel] = Parent.VolumeLabel.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = Parent.XeMID.Text;
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = Parent.XMID.Text;
@@ -661,9 +682,9 @@ namespace MPF.UI.Core.ViewModels
case DiscType.HDDVDSL:
case DiscType.HDDVDDL:
case DiscType.BD25:
//case DiscType.BD33:
case DiscType.BD33:
case DiscType.BD50:
//case DiscType.BD66:
case DiscType.BD66:
case DiscType.BD100:
case DiscType.BD128:
case DiscType.NintendoGameCubeGameDisc:
@@ -788,7 +809,7 @@ namespace MPF.UI.Core.ViewModels
}
}
#endregion
#endregion
#region Event Handlers

View File

@@ -0,0 +1,293 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using MPF.Core.Data;
using MPF.UI.Core.UserControls;
namespace MPF.UI.Core.ViewModels
{
public class LogViewModel
{
#region Fields
/// <summary>
/// Parent OptionsWindow object
/// </summary>
public LogOutput Parent { get; private set; }
#endregion
#region Private State Variables
/// <summary>
/// Paragraph backing the log
/// </summary>
private readonly Paragraph _paragraph;
/// <summary>
/// Cached value of the last line written
/// </summary>
private Run lastLine = null;
/// <summary>
/// Queue of items that need to be logged
/// </summary>
private readonly ProcessingQueue<LogLine> logQueue;
#endregion
/// <summary>
/// Constructor
/// </summary>
public LogViewModel(LogOutput parent)
{
Parent = parent;
// Add handlers
Parent.OutputViewer.SizeChanged += OutputViewerSizeChanged;
Parent.Output.TextChanged += OnTextChanged;
Parent.ClearButton.Click += OnClearButton;
Parent.SaveButton.Click += OnSaveButton;
// Update the internal state
var document = new FlowDocument()
{
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
};
_paragraph = new Paragraph();
document.Blocks.Add(_paragraph);
Parent.Output.Document = document;
logQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
}
#region Logging
/// <summary>
/// Log level for output
/// </summary>
public enum LogLevel
{
USER,
VERBOSE,
ERROR,
SECRET,
}
/// <summary>
/// Log line wrapper
/// </summary>
private struct LogLine
{
public readonly string Text;
public readonly LogLevel LogLevel;
public LogLine(string text, LogLevel logLevel)
{
this.Text = text;
this.LogLevel = logLevel;
}
/// <summary>
/// Get the foreground Brush for the current LogLevel
/// </summary>
/// <returns>Brush representing the color</returns>
public Brush GetForegroundColor()
{
switch (this.LogLevel)
{
case LogLevel.SECRET:
return Brushes.Blue;
case LogLevel.ERROR:
return Brushes.Red;
case LogLevel.VERBOSE:
return Brushes.Yellow;
case LogLevel.USER:
default:
return Brushes.White;
}
}
/// <summary>
/// Generate a Run object from the current LogLine
/// </summary>
/// <returns>Run object based on internal values</returns>
public Run GenerateRun()
{
return new Run { Text = this.Text, Foreground = GetForegroundColor() };
}
}
/// <summary>
/// Enqueue text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void Log(string text) => LogInternal(text, LogLevel.USER);
/// <summary>
/// Enqueue text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void LogLn(string text) => Log(text + "\n");
/// <summary>
/// Enqueue error text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void ErrorLog(string text) => LogInternal(text, LogLevel.ERROR);
/// <summary>
/// Enqueue error text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void ErrorLogLn(string text) => ErrorLog(text + "\n");
/// <summary>
/// Enqueue secret text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void SecretLog(string text) => LogInternal(text, LogLevel.SECRET);
/// <summary>
/// Enqueue secret text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void SecretLogLn(string text) => SecretLog(text + "\n");
/// <summary>
/// Enqueue verbose text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void VerboseLog(string text) => LogInternal(text, LogLevel.VERBOSE);
/// <summary>
/// Enqueue verbose text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void VerboseLogLn(string text) => VerboseLog(text + "\n");
/// <summary>
/// Reset the progress bar state
/// </summary>
public void ResetProgressBar()
{
Parent.Dispatcher.Invoke(() =>
{
Parent.ProgressBar.Value = 0;
Parent.ProgressLabel.Text = string.Empty;
});
}
/// <summary>
/// Enqueue text to the log with formatting
/// </summary>
/// <param name="text">Text to write to the log</param>
/// <param name="logLevel">LogLevel for the log</param>
private void LogInternal(string text, LogLevel logLevel)
{
// Null text gets ignored
if (text == null)
return;
// Enqueue the text
logQueue.Enqueue(new LogLine(text, logLevel));
}
/// <summary>
/// Process the log lines in the queue
/// </summary>
/// <param name="nextLogLine">LogLine item to process</param>
private void ProcessLogLine(LogLine nextLogLine)
{
// Null text gets ignored
string nextText = Parent.Dispatcher.Invoke(() => nextLogLine.Text);
if (nextText == null)
return;
try
{
if (nextText.StartsWith("\r"))
ReplaceLastLine(nextLogLine);
else
AppendToTextBox(nextLogLine);
}
catch (Exception ex)
{
// In the event that something fails horribly, we want to log
AppendToTextBox(new LogLine(ex.ToString(), LogLevel.ERROR));
}
}
/// <summary>
/// Append log line to the log text box
/// </summary>
/// <param name="logLine">LogLine value to append</param>
private void AppendToTextBox(LogLine logLine)
{
Parent.Dispatcher.Invoke(() =>
{
var run = logLine.GenerateRun();
_paragraph.Inlines.Add(run);
lastLine = run;
});
}
/// <summary>
/// Replace the last line written to the log text box
/// </summary>
/// <param name="logLine">LogLine value to append</param>
private void ReplaceLastLine(LogLine logLine)
{
Parent.Dispatcher.Invoke(() =>
{
lastLine.Text = logLine.Text;
lastLine.Foreground = logLine.GetForegroundColor();
});
}
#endregion
#region Helpers
/// <summary>
/// Scroll the current view to the bottom
/// </summary>
public void ScrollToBottom()
{
Parent.OutputViewer.ScrollToBottom();
}
#endregion
#region EventHandlers
private void OnClearButton(object sender, EventArgs e)
{
_paragraph.Inlines.Clear();
ResetProgressBar();
}
private void OnSaveButton(object sender, EventArgs e)
{
using (StreamWriter tw = new StreamWriter(File.OpenWrite("console.log")))
{
foreach (var inline in _paragraph.Inlines)
{
if (inline is Run run)
tw.Write(run.Text);
}
}
}
private void OnTextChanged(object sender, TextChangedEventArgs e) =>
ScrollToBottom();
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e) =>
ScrollToBottom();
#endregion
}
}

View File

@@ -7,11 +7,11 @@ using System.Windows;
using System.Windows.Forms;
using MPF.Core.Data;
using MPF.UI.Core.ComboBoxItems;
using MPF.Windows;
using RedumpLib.Web;
using MPF.UI.Core.Windows;
using SabreTools.RedumpLib.Web;
using WPFCustomMessageBox;
namespace MPF.UI.ViewModels
namespace MPF.UI.Core.ViewModels
{
public class OptionsViewModel
{
@@ -51,15 +51,14 @@ namespace MPF.UI.ViewModels
/// <summary>
/// Constructor
/// </summary>
public OptionsViewModel(OptionsWindow parent)
public OptionsViewModel(OptionsWindow parent, Options baseOptions)
{
Parent = parent;
Options = App.Options.Clone() as Options;
Options = new Options(baseOptions);
// Add handlers
Parent.AaruPathButton.Click += BrowseForPathClick;
Parent.DiscImageCreatorPathButton.Click += BrowseForPathClick;
Parent.DDPathButton.Click += BrowseForPathClick;
Parent.DefaultOutputPathButton.Click += BrowseForPathClick;
Parent.AcceptButton.Click += OnAcceptClick;
@@ -105,7 +104,7 @@ namespace MPF.UI.ViewModels
/// </summary>
private static List<Element<InternalProgram>> PopulateInternalPrograms()
{
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, /*InternalProgram.Redumper,*/ InternalProgram.DD };
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.Redumper };
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
}
@@ -188,13 +187,13 @@ namespace MPF.UI.ViewModels
/// <summary>
/// Test Redump login credentials
/// </summary>
#if NET48 || NETSTANDARD2_1
#if NET48
private bool? TestRedumpLogin()
#else
private async Task<bool?> TestRedumpLogin()
#endif
{
#if NET48 || NETSTANDARD2_1
#if NET48
(bool? success, string message) = RedumpWebClient.ValidateCredentials(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
#else
(bool? success, string message) = await RedumpHttpClient.ValidateCredentials(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
@@ -209,9 +208,9 @@ namespace MPF.UI.ViewModels
return success;
}
#endregion
#endregion
#region UI Functionality
#region UI Functionality
/// <summary>
/// Create an open folder dialog box
@@ -240,9 +239,9 @@ namespace MPF.UI.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
@@ -265,7 +264,7 @@ namespace MPF.UI.ViewModels
/// <summary>
/// Test Redump credentials for validity
/// </summary>
#if NET48 || NETSTANDARD2_1
#if NET48
private void OnRedumpTestClick(object sender, EventArgs e) =>
TestRedumpLogin();
#else
@@ -273,6 +272,6 @@ namespace MPF.UI.ViewModels
_ = await TestRedumpLogin();
#endif
#endregion
#endregion
}
}

View File

@@ -194,6 +194,7 @@
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="BBFCRegistrationNumberTextBox" Label="BBFC Reg. No."/>
<controls:UserInput x:Name="CDProjektIDTextBox" Label="CD Projekt ID"/>
<controls:UserInput x:Name="DiscHologramIDTextBox" Label="Disc Hologram ID"/>
<controls:UserInput x:Name="DNASDiscIDTextBox" Label="DNAS Disc ID"/>
<controls:UserInput x:Name="ISBNTextBox" Label="ISBN"/>
@@ -369,6 +370,7 @@
<controls:UserInput x:Name="PVD" Label="PVD" IsReadOnly="True"
Text="{Binding SubmissionInfo.Extras.PVD, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="RingNonZeroDataStart" Label="Ring Non-Zero Data Start" IsReadOnly="True"/>
<controls:UserInput x:Name="SecuROMData" Label="SecuROM Data" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.SecuROMData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
@@ -377,6 +379,7 @@
Text="{Binding SubmissionInfo.Extras.SecuritySectorRanges, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="SSVersion" Label="Security Sector Version" IsReadOnly="True"/>
<controls:UserInput x:Name="UniversalHash" Label="Universal Hash (SHA-1)" IsReadOnly="True"/>
<controls:UserInput x:Name="VolumeLabel" Label="Volume Label" IsReadOnly="True"/>
<controls:UserInput x:Name="XeMID" Label="XeMID" IsReadOnly="True"/>
<controls:UserInput x:Name="XMID" Label="XMID" IsReadOnly="True"/>

View File

@@ -1,6 +1,6 @@
using MPF.Core.Data;
using MPF.UI.Core.ViewModels;
using RedumpLib.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.UI.Core.Windows
{

View File

@@ -1,11 +1,11 @@
<coreWindows:WindowBase x:Class="MPF.Windows.MainWindow"
<coreWindows:WindowBase x:Class="MPF.UI.Core.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"
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
xmlns:viewModels="clr-namespace:MPF.UI.Core.ViewModels"
mc:Ignorable="d"
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
@@ -136,16 +136,13 @@
</ComboBox>
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right" Style="{DynamicResource CustomComboBoxStyle}" />
<Label x:Name="OutputFilenameLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="Output Filename"/>
<TextBox x:Name="OutputFilenameTextBox" Grid.Row="1" Grid.Column="1" Height="22" VerticalContentAlignment="Center" />
<Label x:Name="OutputDirectoryLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Output Directory"/>
<TextBox x:Name="OutputDirectoryTextBox" Grid.Row="2" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center" />
<Button x:Name="OutputDirectoryBrowseButton" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
<Label x:Name="OutputPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="Output Path"/>
<TextBox x:Name="OutputPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center" />
<Button x:Name="OutputPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="DriveLetterLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="Drive Letter"/>
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}">
<Label x:Name="DriveLetterLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Drive Letter"/>
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="2" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Letter}" />
@@ -153,9 +150,12 @@
</ComboBox.ItemTemplate>
</ComboBox>
<Label x:Name="DriveSpeedLabel" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Content="Drive Speed"/>
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
<Label x:Name="DriveSpeedLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="Drive Speed"/>
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
<Label x:Name="DumpingProgramLabel" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Content="Dumping Program"/>
<ComboBox x:Name="DumpingProgramComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
<Label x:Name="ParametersLabel" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" Content="Parameters"/>
<TextBox x:Name="ParametersTextBox" Grid.Row="5" Grid.Column="1" Height="22" Width="370" HorizontalAlignment="Left" IsEnabled="False" VerticalContentAlignment="Center" />
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right" IsChecked="False" />

View File

@@ -1,8 +1,7 @@
using System;
using MPF.UI.Core.Windows;
using MPF.UI.ViewModels;
using MPF.UI.Core.ViewModels;
namespace MPF.Windows
namespace MPF.UI.Core.Windows
{
public partial class MainWindow : WindowBase
{
@@ -22,7 +21,7 @@ namespace MPF.Windows
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
MainViewModel.Init();
MainViewModel.Init(this);
}
}
}

View File

@@ -1,10 +1,10 @@
<coreWindows:WindowBase x:Class="MPF.Windows.OptionsWindow"
<coreWindows:WindowBase x:Class="MPF.UI.Core.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"
xmlns:core="clr-namespace:MPF.UI.Core"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
mc:Ignorable="d"
Title="Options" Width="515.132" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
@@ -70,12 +70,15 @@
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
IsChecked="{Binding Options.EnableDarkMode}"
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
/>
/>
<CheckBox VerticalAlignment="Center" Content="Check for Updates on Startup"
IsChecked="{Binding Options.CheckForUpdatesOnStartup}"
ToolTip="Check for updates when the application starts" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Fast Update Label"
IsChecked="{Binding Options.FastUpdateLabel}"
ToolTip="Bypasses disc checks to quickly update the output path. Use with caution!" Margin="0,4"
/>
</UniformGrid>
</TabItem>
@@ -92,6 +95,7 @@
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Aaru Path" />
@@ -104,12 +108,12 @@
<Button x:Name="DiscImageCreatorPathButton" Grid.Row="1" Grid.Column="2" Height="22" Width="22" Content="..."
Style="{DynamicResource CustomButtonStyle}" />
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DD Path" />
<TextBox x:Name="DDPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DDPath}" VerticalContentAlignment="Center" />
<Button x:Name="DDPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..."
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Redumper Path" />
<TextBox x:Name="RedumperPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.RedumperPath}" VerticalContentAlignment="Center" />
<Button x:Name="RedumperPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..."
Style="{DynamicResource CustomButtonStyle}" />
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Dumping Program" />
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Dumping Program" />
<ComboBox x:Name="InternalProgramComboBox" Grid.Row="3" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
ItemsSource="{Binding InternalPrograms}" Style="{DynamicResource CustomComboBoxStyle}" />
@@ -124,7 +128,7 @@
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
<UniformGrid Columns="2" Rows="9">
<CheckBox VerticalAlignment="Center" Content="Skip Type Detect"
<CheckBox x:Name="SkipMediaTypeDetectionCheckBox" 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,7 +166,7 @@
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
/>
<CheckBox VerticalAlignment="Center" Content="Protection File"
<CheckBox VerticalAlignment="Center" Content="Output Protection File"
IsChecked="{Binding Options.OutputSeparateProtectionFile}" IsEnabled="{Binding Options.ScanForProtection}"
ToolTip="Output protection information to a separate file" Margin="0,4,0,0"
/>
@@ -226,27 +230,35 @@
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="CD-ROM" />
<Label Grid.Row="0" Grid.Column="0" Content="CD" />
<Slider x:Name="DumpSpeedCDSlider" Grid.Row="0" Grid.Column="1" Minimum="1" Maximum="72" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static local:Constants.SpeedsForCDAsCollection}}"
Ticks="{Binding Source={x:Static core:Constants.SpeedsForCDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedCD}" />
<TextBox x:Name="DumpSpeedCDTextBox" Grid.Row="0" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedCDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
<Label Grid.Row="1" Grid.Column="0" Content="DVD-ROM" />
<Label Grid.Row="1" Grid.Column="0" Content="DVD" />
<Slider x:Name="DumpSpeedDVDSlider" Grid.Row="1" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static local:Constants.SpeedsForDVDAsCollection}}"
Ticks="{Binding Source={x:Static core:Constants.SpeedsForDVDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedDVD}" />
<TextBox x:Name="DumpSpeedDVDTextBox" Grid.Row="1" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
<Label Grid.Row="2" Grid.Column="0" Content="BD-ROM" />
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static local:Constants.SpeedsForBDAsCollection}}"
<Label Grid.Row="2" Grid.Column="0" Content="HD-DVD" />
<Slider x:Name="DumpSpeedHDDVDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static core:Constants.SpeedsForHDDVDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedHDDVD}" />
<TextBox x:Name="DumpSpeedHDDVDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedHDDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
<Label Grid.Row="3" Grid.Column="0" Content="BD" />
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="3" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
Ticks="{Binding Source={x:Static core:Constants.SpeedsForBDAsCollection}}"
Value="{Binding Options.PreferredDumpSpeedBD}" />
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="3" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
Text="{Binding ElementName=DumpSpeedBDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
</Grid>
</GroupBox>
@@ -352,6 +364,26 @@
</UniformGrid>
</TabItem>
<TabItem Header="Redumper" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2" Rows="2">
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
IsChecked="{Binding Options.RedumperEnableDebug}"
ToolTip="Enable debug output in logs" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
IsChecked="{Binding Options.RedumperEnableVerbose}"
ToolTip="Enable verbose output in logs" Margin="0,4"
/>
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
Text="{Binding Options.RedumperRereadCount}"
ToolTip="Specifies how many rereads are attempted on read error"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Logging" Style="{DynamicResource CustomTabItemStyle}">
<UniformGrid Columns="2">
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
@@ -363,23 +395,13 @@
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
ToolTip="Open the log panel when the program launches" Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Log Formatting"
IsChecked="{Binding Options.EnableLogFormatting}"
ToolTip="Format log lines written to the log, including overwriting previous lines on match. Disable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
/>
<CheckBox VerticalAlignment="Center" Content="Enable Progress Processing"
IsChecked="{Binding Options.EnableProgressProcessing}" IsEnabled="{Binding Options.EnableLogFormatting}"
ToolTip="Process lines written to the log to update the progress bar. Disable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
/>
</UniformGrid>
</TabItem>
<TabItem Header="Login Info" Style="{DynamicResource CustomTabItemStyle}">
<StackPanel>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump Credentials">
<UniformGrid Columns="5" Rows="1">
<UniformGrid Columns="5">
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Username" />
<TextBox x:Name="RedumpUsernameTextBox" Height="22" HorizontalAlignment="Stretch"
Text="{Binding Options.RedumpUsername}" />
@@ -391,6 +413,12 @@
Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
<Label>
<Label.Content>
<TextBlock TextWrapping="Wrap"><Bold Foreground="Red">WARNING:</Bold> If you choose to enable validation and information retrieval, you are responsible for ensuring that all data populated matches your actual media. Some information may be marked to check for validity as a reminder, but all information should be subject to the same scrutiny.</TextBlock>
</Label.Content>
</Label>
</StackPanel>
</TabItem>
</TabControl>

View File

@@ -1,7 +1,7 @@
using MPF.UI.Core.Windows;
using MPF.UI.ViewModels;
using MPF.Core.Data;
using MPF.UI.Core.ViewModels;
namespace MPF.Windows
namespace MPF.UI.Core.Windows
{
/// <summary>
/// Interaction logic for OptionsWindow.xaml
@@ -16,10 +16,16 @@ namespace MPF.Windows
/// <summary>
/// Constructor
/// </summary>
public OptionsWindow()
public OptionsWindow(Options options)
{
InitializeComponent();
DataContext = new OptionsViewModel(this);
DataContext = new OptionsViewModel(this, options);
#if NET6_0_OR_GREATER
this.SkipMediaTypeDetectionCheckBox.IsEnabled = false;
this.SkipMediaTypeDetectionCheckBox.IsChecked = false;
this.SkipMediaTypeDetectionCheckBox.ToolTip = "This feature is not enabled for .NET 6";
#endif
}
}
}

143
MPF.sln
View File

@@ -18,57 +18,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.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
publish-nix.sh = publish-nix.sh
README.md = README.md
publish-win.bat = publish-win.bat
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RedumpLib", "RedumpLib\RedumpLib.csproj", "{13574913-A426-4644-9955-F49AD0876E5F}"
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}") = "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
@@ -93,14 +53,6 @@ Global
{8CFDE289-E171-4D49-A40D-5293265C1253}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CFDE289-E171-4D49-A40D-5293265C1253}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CFDE289-E171-4D49-A40D-5293265C1253}.Release|Any CPU.Build.0 = Release|Any CPU
{13574913-A426-4644-9955-F49AD0876E5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13574913-A426-4644-9955-F49AD0876E5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13574913-A426-4644-9955-F49AD0876E5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13574913-A426-4644-9955-F49AD0876E5F}.Release|Any CPU.Build.0 = Release|Any CPU
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{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
{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
@@ -113,78 +65,6 @@ Global
{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
@@ -194,28 +74,9 @@ Global
{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}

View File

@@ -2,7 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
xmlns:windows="clr-namespace:MPF.Windows"
xmlns:windows="clr-namespace:MPF.UI.Core.Windows;assembly=MPF.UI.Core"
x:Class="MPF.App">
<Application.MainWindow>
<windows:MainWindow Visibility="Visible"/>

View File

@@ -1,8 +1,4 @@
using System.Windows;
using MPF.Core.Data;
using MPF.Core.Utilities;
using MPF.UI.ViewModels;
using MPF.Windows;
namespace MPF
{
@@ -16,46 +12,5 @@ namespace MPF
/// </remarks>
public partial class App : Application
{
/// <summary>
/// Static application instance for reference
/// </summary>
private static App _appInstance;
/// <summary>
/// Read-only access to the current main window
/// </summary>
public static MainWindow Instance => _appInstance.MainWindow as MainWindow;
/// <summary>
/// Read-only access to the current log window
/// </summary>
public static LogViewModel Logger => Instance.LogOutput.LogViewModel;
/// <summary>
/// Access to the current options
/// </summary>
public static Options Options
{
get => _options;
set
{
_options = value;
OptionsLoader.SaveToConfig(_options);
}
}
/// <summary>
/// Internal reference to Options
/// </summary>
private static Options _options;
/// <summary>
/// Constructor
/// </summary>
public App()
{
_appInstance = this;
_options = OptionsLoader.LoadFromConfig();
}
}
}

View File

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

View File

@@ -1,8 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
<PlatformTarget>x86</PlatformTarget>
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64</RuntimeIdentifiers>
<OutputType>WinExe</OutputType>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
@@ -11,13 +11,12 @@
<AssemblyName>MPF</AssemblyName>
<Description>Frontend for various dumping programs</Description>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Copyright>Copyright (c)2019-2022</Copyright>
<Copyright>Copyright (c)2019-2023</Copyright>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<Version>2.4</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
<FileVersion>$(Version)</FileVersion>
<Version>2.6.4</Version>
<IncludeSource>true</IncludeSource>
<IncludeSymbols>true</IncludeSymbols>
<InternalsVisibleTo>MPF.Test</InternalsVisibleTo>
</PropertyGroup>
<PropertyGroup>
@@ -27,10 +26,25 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.3.4" GeneratePathProperty="true">
<Resource Include="Images\Icon.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
<ProjectReference Include="..\MPF.UI.Core\MPF.UI.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationFramework.Aero" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -41,19 +55,4 @@
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
<ItemGroup>
<Resource Include="Images\Icon.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
<ProjectReference Include="..\MPF.UI.Core\MPF.UI.Core.csproj" />
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
</ItemGroup>
<ItemGroup>
<Reference Include="PresentationFramework.Aero" />
</ItemGroup>
</Project>

View File

@@ -1,577 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using MPF.Core.Data;
using MPF.UserControls;
namespace MPF.UI.ViewModels
{
public class LogViewModel
{
#region Fields
/// <summary>
/// Parent OptionsWindow object
/// </summary>
public LogOutput Parent { get; private set; }
#endregion
#region Private State Variables
/// <summary>
/// Paragraph backing the log
/// </summary>
private readonly Paragraph _paragraph;
/// <summary>
/// Cached value of the last line written
/// </summary>
private Run lastLine = null;
/// <summary>
/// Queue of items that need to be logged
/// </summary>
private readonly ProcessingQueue<LogLine> logQueue;
/// <summary>
/// List of Matchers for progress tracking
/// </summary>
private readonly List<Matcher?> _matchers;
/// <summary>
/// Cached value of the last matcher used
/// </summary>
private Matcher? lastUsedMatcher = null;
/// <summary>
/// Regex pattern to find DiscImageCreator progress messages
/// </summary>
private const string DiscImageCreatorProgressPattern = @"\s*(\d+)\/\s*(\d+)$";
#endregion
/// <summary>
/// Constructor
/// </summary>
public LogViewModel(LogOutput parent)
{
Parent = parent;
// Add handlers
Parent.OutputViewer.SizeChanged += OutputViewerSizeChanged;
Parent.Output.TextChanged += OnTextChanged;
Parent.ClearButton.Click += OnClearButton;
Parent.SaveButton.Click += OnSaveButton;
// Update the internal state
var document = new FlowDocument()
{
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
};
_paragraph = new Paragraph();
document.Blocks.Add(_paragraph);
Parent.Output.Document = document;
// TODO: Can we dynamically add matchers *only* during dumping?
_matchers = new List<Matcher?>();
AddAaruMatchers();
AddDiscImageCreatorMatchers();
logQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
}
#region Matching
/// <summary>
/// Matching wrapper
/// </summary>
private struct Matcher
{
private readonly string prefix;
private readonly Regex regex;
private readonly int start;
private readonly string progressBarText;
private readonly Action<Match, string> lambda;
public Matcher(string prefix, string regex, string progressBarText, Action<Match, string> lambda)
{
this.prefix = prefix;
this.regex = new Regex(regex);
this.start = prefix.Length;
this.progressBarText = progressBarText;
this.lambda = lambda;
}
/// <summary>
/// Check if the text matches the prefix
/// </summary>
/// <param name="text">Text to check</param>
/// <returns>True if the line starts with the prefix, false otherwise</returns>
public bool Matches(string text) => text.StartsWith(prefix);
/// <summary>
/// Generate a Match and apply the lambda
/// </summary>
/// <param name="text">Text to match and apply from</param>
public void Apply(string text)
{
Match match = regex?.Match(text, start);
lambda?.Invoke(match, progressBarText);
}
}
/// <summary>
/// Add all Matchers for Aaru
/// </summary>
private void AddAaruMatchers()
{
// TODO: Determine matchers that can be added
}
/// <summary>
/// Add all Matchers for DiscImageCreator
/// </summary>
private void AddDiscImageCreatorMatchers()
{
#region Pre-dump Checking
_matchers.Add(new Matcher(
"Checking EXE",
DiscImageCreatorProgressPattern,
"Checking executables...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Checking Pregap sync, msf, mode (LBA)",
@"\s*-(\d+)$",
"Checking Pregap sync, msf, mode",
(match, text) =>
{
Parent.ProgressBar.Value = 0;
Parent.ProgressLabel.Text = text;
}));
_matchers.Add(new Matcher(
"Checking SubQ adr (Track)",
DiscImageCreatorProgressPattern,
"Checking SubQ adr...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Checking SubQ ctl (Track)",
DiscImageCreatorProgressPattern,
"Checking SubQ ctl...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Checking SubRtoW (Track)",
DiscImageCreatorProgressPattern,
"Checking SubRtoW...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Reading DirectoryRecord",
DiscImageCreatorProgressPattern,
"Reading directory records...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Scanning sector for anti-mod string (LBA)",
DiscImageCreatorProgressPattern,
"Scanning sectors for anti-mod string...",
StandardDiscImageCreatorProgress
));
#endregion
#region Dumping
_matchers.Add(new Matcher(
@"Creating iso(LBA)",
DiscImageCreatorProgressPattern,
"Creating ISO...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
@"Creating .scm (LBA)",
DiscImageCreatorProgressPattern,
"Creating scrambled image...",
StandardDiscImageCreatorProgress
));
#endregion
#region Post-Dump Processing
_matchers.Add(new Matcher(
"Checking sectors:",
DiscImageCreatorProgressPattern,
"Checking for errors...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Creating bin (Track)",
DiscImageCreatorProgressPattern,
"Creating BIN(s)...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Creating cue and ccd (Track)",
DiscImageCreatorProgressPattern,
"Creating CUE and CCD...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Descrambling data sector of img:",
DiscImageCreatorProgressPattern,
"Descrambling image...",
StandardDiscImageCreatorProgress
));
_matchers.Add(new Matcher(
"Scanning sector (LBA)",
DiscImageCreatorProgressPattern,
"Scanning sectors for protection...",
StandardDiscImageCreatorProgress
));
#endregion
}
#endregion
#region Logging
/// <summary>
/// Log level for output
/// </summary>
public enum LogLevel
{
USER,
VERBOSE,
ERROR,
SECRET,
}
/// <summary>
/// Log line wrapper
/// </summary>
private struct LogLine
{
public readonly string Text;
public readonly LogLevel LogLevel;
public LogLine(string text, LogLevel logLevel)
{
this.Text = text;
this.LogLevel = logLevel;
}
/// <summary>
/// Get the foreground Brush for the current LogLevel
/// </summary>
/// <returns>Brush representing the color</returns>
public Brush GetForegroundColor()
{
switch (this.LogLevel)
{
case LogLevel.SECRET:
return Brushes.Blue;
case LogLevel.ERROR:
return Brushes.Red;
case LogLevel.VERBOSE:
return Brushes.Yellow;
case LogLevel.USER:
default:
return Brushes.White;
}
}
/// <summary>
/// Generate a Run object from the current LogLine
/// </summary>
/// <returns>Run object based on internal values</returns>
public Run GenerateRun()
{
return new Run { Text = this.Text, Foreground = GetForegroundColor() };
}
}
/// <summary>
/// Enqueue text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void Log(string text) => LogInternal(text);
/// <summary>
/// Enqueue text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void LogLn(string text) => Log(text + "\n");
/// <summary>
/// Enqueue error text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void ErrorLog(string text) => LogInternal(text, LogViewModel.LogLevel.ERROR);
/// <summary>
/// Enqueue error text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void ErrorLogLn(string text) => ErrorLog(text + "\n");
/// <summary>
/// Enqueue secret text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void SecretLog(string text) => LogInternal(text, LogViewModel.LogLevel.SECRET);
/// <summary>
/// Enqueue secret text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void SecretLogLn(string text) => SecretLog(text + "\n");
/// <summary>
/// Enqueue verbose text to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void VerboseLog(string text) => LogInternal(text, LogViewModel.LogLevel.VERBOSE);
/// <summary>
/// Enqueue verbose text with a newline to the log
/// </summary>
/// <param name="text">Text to write to the log</param>
public void VerboseLogLn(string text) => VerboseLog(text + "\n");
/// <summary>
/// Reset the progress bar state
/// </summary>
public void ResetProgressBar()
{
Parent.Dispatcher.Invoke(() =>
{
Parent.ProgressBar.Value = 0;
Parent.ProgressLabel.Text = string.Empty;
});
}
/// <summary>
/// Enqueue text to the log with formatting
/// </summary>
/// <param name="text">Text to write to the log</param>
/// <param name="logLevel">LogLevel for the log, defaults to USER</param>
private void LogInternal(string text, LogLevel logLevel = LogLevel.USER)
{
// Null text gets ignored
if (text == null)
return;
// If we have verbose logs but not enabled, ignore
if (logLevel == LogLevel.VERBOSE && !App.Options.VerboseLogging)
return;
// Enqueue the text
logQueue.Enqueue(new LogLine(text, logLevel));
}
/// <summary>
/// Process the log lines in the queue
/// </summary>
/// <param name="nextLogLine">LogLine item to process</param>
private void ProcessLogLine(LogLine nextLogLine)
{
// Null text gets ignored
string nextText = Parent.Dispatcher.Invoke(() => nextLogLine.Text);
if (nextText == null)
return;
try
{
// If we're not processing log formatting, just append and continue
if (!App.Options.EnableLogFormatting)
{
if (nextText.StartsWith("\r"))
ReplaceLastLine(nextLogLine);
else
AppendToTextBox(nextLogLine);
return;
}
// Get last line
lastLine = lastLine ?? GetLastLine();
// Always append if there's no previous line
if (lastLine == null)
{
AppendToTextBox(nextLogLine);
lastUsedMatcher = _matchers.FirstOrDefault(m => m?.Matches(nextText) == true);
}
// Return always means overwrite
else if (nextText.StartsWith("\r"))
{
ReplaceLastLine(nextLogLine);
lastUsedMatcher = _matchers.FirstOrDefault(m => m?.Matches(nextText.TrimStart('\r')) == true);
}
// If we have a cached matcher and we match
else if (lastUsedMatcher?.Matches(nextText) == true)
{
ReplaceLastLine(nextLogLine);
}
else
{
// Get the first matching Matcher
var firstMatcher = _matchers.FirstOrDefault(m => m?.Matches(nextText) == true);
if (firstMatcher.HasValue)
{
string lastText = Parent.Dispatcher.Invoke(() => { return lastLine.Text; });
if (firstMatcher.Value.Matches(lastText))
ReplaceLastLine(nextLogLine);
else if (string.IsNullOrWhiteSpace(lastText))
ReplaceLastLine(nextLogLine);
else
AppendToTextBox(nextLogLine);
// Cache the last used Matcher
lastUsedMatcher = firstMatcher;
}
// Default case for all other text
else
{
AppendToTextBox(nextLogLine);
lastUsedMatcher = null;
}
}
// Update the bar if needed
if (App.Options.EnableProgressProcessing)
ProcessStringForProgressBar(nextText, lastUsedMatcher);
}
catch (Exception ex)
{
// In the event that something fails horribly, we want to log
AppendToTextBox(new LogLine(ex.ToString(), LogLevel.ERROR));
}
}
/// <summary>
/// Append log line to the log text box
/// </summary>
/// <param name="logLine">LogLine value to append</param>
private void AppendToTextBox(LogLine logLine)
{
Parent.Dispatcher.Invoke(() =>
{
var run = logLine.GenerateRun();
_paragraph.Inlines.Add(run);
lastLine = run;
});
}
/// <summary>
/// Get the last line written to the log text box
/// </summary>
private Run GetLastLine()
{
return Parent.Dispatcher.Invoke(() =>
{
if (!_paragraph.Inlines.Any())
return null;
return _paragraph.Inlines.LastInline as Run;
});
}
/// <summary>
/// Process text if it should update the progress bar
/// </summary>
/// <param name="text">Text to check and update with</param>
private void ProcessStringForProgressBar(string text, Matcher? matcher)
{
Parent.Dispatcher.Invoke(() => { matcher?.Apply(text); });
}
/// <summary>
/// Replace the last line written to the log text box
/// </summary>
/// <param name="logLine">LogLine value to append</param>
private void ReplaceLastLine(LogLine logLine)
{
Parent.Dispatcher.Invoke(() =>
{
lastLine.Text = logLine.Text;
lastLine.Foreground = logLine.GetForegroundColor();
});
}
#endregion
#region Helpers
/// <summary>
/// Scroll the current view to the bottom
/// </summary>
public void ScrollToBottom()
{
Parent.OutputViewer.ScrollToBottom();
}
#endregion
#region EventHandlers
private void OnClearButton(object sender, EventArgs e)
{
_paragraph.Inlines.Clear();
ResetProgressBar();
}
private void OnSaveButton(object sender, EventArgs e)
{
using (StreamWriter tw = new StreamWriter(File.OpenWrite("console.log")))
{
foreach (var inline in _paragraph.Inlines)
{
if (inline is Run run)
tw.Write(run.Text);
}
}
}
private void OnTextChanged(object sender, TextChangedEventArgs e) =>
ScrollToBottom();
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e) =>
ScrollToBottom();
private void StandardDiscImageCreatorProgress(Match match, string text)
{
if (uint.TryParse(match.Groups[1].Value, out uint current) && uint.TryParse(match.Groups[2].Value, out uint total))
{
float percentProgress = (current / (float)total) * 100;
Parent.ProgressBar.Value = percentProgress;
Parent.ProgressLabel.Text = string.Format($"{text} ({percentProgress:N2}%)");
}
}
#endregion
}
}

106
README.md
View File

@@ -1,6 +1,6 @@
# Media Preservation Frontend (MPF)
DiscImageCreator/Aaru UI in C#
Redumper/Aaru/DiscImageCreator UI in C#
[![Build status](https://ci.appveyor.com/api/projects/status/3ldav3v0c373jeqa?svg=true)](https://ci.appveyor.com/project/mnadareski/MPF/build/artifacts)
@@ -13,17 +13,78 @@ For those who would rather use the most recent stable build, download the latest
For those who like to test the newest features, download the latest AppVeyor WIP build here: [AppVeyor](https://ci.appveyor.com/project/mnadareski/MPF/build/artifacts)
## System Requirements
## Media Preservation Frontend (MPF)
Even though this is written in C#, this program can only be used on Windows systems due to one of the base programs, DiscImageCreator, being Windows-only. There is some preliminary support for Linux underway, and we will try to integrate with that when the time comes.
MPF is the main, UI-centric application of the MPF suite. This program allows users to use Redumper, Aaru, DiscImageCreator, or dd for Windows in a more user-friendly way. Each backend dumping program is supported as fully as possible to ensure that all information is captured on output. There are many customization options and quality of life settings that can be access through the Options menu.
- Windows 7 (newest version of Windows recommended) or Mono-compatible Linux environment (MPF.Check only)
- .NET Framework 4.8 or .NET Core 3.1 Runtimes (.NET Core 3.1 is mostly functional due to a dependency issues but may be unstable in some situations)
- 1 GB of free RAM
### System Requirements
- Windows 8.1 (x86 or x64) or newer
- Users who wish to use MPF on Windows 7 need to disable strong name validation due to `Microsoft.Management.Infrastructure` being unsigned. Add the following registry keys (accurate at time of writing):
```
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName\Verification\*,31bf3856ad364e35]
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,31bf3856ad364e35]
```
- Alternatively, look at this [StackOverflow question](https://stackoverflow.com/questions/403731/strong-name-validation-failed) for more information.
- .NET Framework 4.8 or .NET 6.0 Runtimes
- As much hard drive space as the amount of discs you will be dumping (20+ GB recommended)
Ensure that your operating system is as up-to-date as possible, since some features may rely on those updates.
### Support Limitations
.NET 6 has some known limitations that are documented in code and in some prior support tickets:
- Windows-only due to reliance on WPF and Winforms
- MAUI is not a viable alternative due to lack of out-of-box support for Linux
- Avalonia is being heavily considered
- No media type detection due to lack of alternatives to IMAPI2
### Build Instructions
To build for .NET Framework 4.8 (Windows only), ensure that the .NET Framework 4.8 SDK is installed and included in your PATH. Then, run the following commands from command prompt, Powershell, or Terminal:
```
dotnet restore
msbuild MPF\MPF.csproj -property:TargetFramework=net48 -property:RuntimeIdentifiers=win7-x64
```
To build for .NET 6.0 (Windows only), ensure that the .NET 6.0 SDK (or later) is installed and included in your PATH. Then, run the following commands from command prompt, Powershell, or Terminal:
```
dotnet build MPF\MPF.csproj --framework net6.0-windows --runtime [win7-x64|win8-x64|win81-x64|win10-x64]
```
Choose one of `[win7-x64|win8-x64|win81-x64|win10-x64]` depending on the machine you are targeting. `win10-x64` also includes Windows 11.
## Media Preservation Frontend Checker (MPF.Check)
MPF.Check is a commandline-only program that allows users to generate submission information from their personal rips. This program supports the outputs from Redumper, Aaru, DiscImageCreator, dd for Windows, Cleanrip, and UmdImageCreator. Running this program without any parameters will display the help text, including all supported parameters.
### System Requirements
- Windows 8.1 (x86 or x64) or newer, GNU/Linux x64, or OSX x64
- .NET Framework 4.8 (Windows or `mono` only) or .NET 6.0 Runtimes
### Build Instructions
To build for .NET Framework 4.8 (Windows only), ensure that the .NET Framework 4.8 SDK is installed and included in your PATH. Then, run the following commands from command prompt, Powershell, or Terminal:
```
dotnet restore
msbuild MPF.Check\MPF.Check.csproj -property:TargetFramework=net48 -property:RuntimeIdentifiers=win7-x64
```
To build for .NET 6.0 (All supported OSes), ensure that the .NET 6.0 SDK (or later) is installed and included in your PATH. Then, run the following commands from command prompt, Powershell, Terminal, or shell:
```
dotnet build MPF.Check\MPF.Check.csproj --framework net6.0 --runtime [win7-x64|win8-x64|win81-x64|win10-x64|linux-x64|osx-x64]
```
Choose one of `[win7-x64|win8-x64|win81-x64|win10-x64|linux-x64|osx-x64]` depending on the machine you are targeting. `win10-x64` also includes Windows 11.
## Information
For all additional information, including information about the individual components included in the project and what dumping programs are supported, please see [the wiki](https://github.com/SabreTools/MPF/wiki) for more details.
@@ -36,35 +97,44 @@ 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)
- **UnshieldSharp** - Protection scanning - [GitHub](https://github.com/mnadareski/UnshieldSharp)
- **WPFCustomMessageBox.thabse** - Custom message boxes in UI - [GitHub](https://github.com/thabse/WPFCustomMessageBox)
## Contributors
Here are the talented people who have contributed to the project so far:
Here are the talented people who have contributed to the project so far in ways that GitHub doesn't like to track:
- **darksabre76** - Project Lead / Backend Design / UI Maintenence
- **ReignStumble** - Former Project Lead / UI Design
- **Jakz** - Primary Feature Contributor
- **NHellFire** - Feature Contributor
- **Shadów** - UI Support
For all others who have contributed in some way, please see [here](https://github.com/SabreTools/MPF/graphs/contributors).
## Notable Testers
These are the tireless individuals who have dedicated countless hours to help test the many features of MPF and have worked with the development team closely:
- **Dizzzy/user7** - Additonal thanks for the original concept
- [**ajshell1**](https://github.com/ajshell1)
- [**Billy**](https://github.com/InternalLoss)
- [**David 'Foxhack' Silva**](https://github.com/FoxhackDN)
- [**ehw**](https://github.com/ehw)
- [**fuzzball**](https://github.com/fuzz6001)
- [**Gameboi64**](https://github.com/gameboi64)
- [**Intothisworld**](https://github.com/Intothisworld)
- [**John Veness**](https://github.com/JohnVeness)
- **Kludge**
- **ajshell1**
- **Whovian**
- **Gameboi64**
- **silasqwerty**
- [**Matt Sephton**](https://github.com/gingerbeardman)
- [**NightsoN Blaze**](https://github.com/nightson)
- [**NovaSAurora**](https://github.com/NovaSAurora)
- [**Seventy7**](https://github.com/7Seventy7) - Additonal thanks for the original concept
- [**Silent**](https://github.com/CookiePLMonster)
- [**Terry Janas**](https://github.com/tjanas)
- [**TheRogueArchivist**](https://github.com/TheRogueArchivist)
- [**Whovian9369**](https://github.com/Whovian9369)
## Community Shout-Outs
Thanks to these communities for their use, testing, and feedback. I can't even hope to be able to thank everyone individually.
- **VGPC Discord** - Fast feedback and a lot of testing
- **Redump Community** - Near-daily use to assist with metadata gathering
- [**VGPC Discord**](https://discord.gg/AHTfxQV) - Fast feedback and a lot of testing
- [**Redump Community**](http://redump.org/) - Near-daily use to assist with metadata gathering

View File

@@ -1,48 +0,0 @@
using System;
using System.Linq;
namespace RedumpLib.Attributes
{
public static class AttributeHelper<T>
{
/// <summary>
/// Get the HumanReadableAttribute from a supported value
/// </summary>
/// <param name="value">Value to use</param>
/// <returns>HumanReadableAttribute attached to the value</returns>
public static HumanReadableAttribute GetAttribute(T value)
{
// Null value in, null value out
if (value == null)
return null;
// Current enumeration type
var enumType = typeof(T);
if (Nullable.GetUnderlyingType(enumType) != null)
enumType = Nullable.GetUnderlyingType(enumType);
// If the value returns a null on ToString, just return null
string valueStr = value.ToString();
if (string.IsNullOrWhiteSpace(valueStr))
return null;
// Get the member info array
var memberInfos = enumType?.GetMember(valueStr);
if (memberInfos == null)
return null;
// Get the enum value info from the array, if possible
var enumValueMemberInfo = memberInfos.FirstOrDefault(m => m.DeclaringType == enumType);
if (enumValueMemberInfo == null)
return null;
// Try to get the relevant attribute
var attributes = enumValueMemberInfo.GetCustomAttributes(typeof(HumanReadableAttribute), true);
if (attributes == null)
return null;
// Return the first attribute, if possible
return (HumanReadableAttribute)attributes.FirstOrDefault();
}
}
}

View File

@@ -1,25 +0,0 @@
using System;
namespace RedumpLib.Attributes
{
/// <summary>
/// Generic attribute for human readable values
/// </summary>
public class HumanReadableAttribute : Attribute
{
/// <summary>
/// Item is marked as obsolete or unusable
/// </summary>
public bool Available { get; set; } = true;
/// <summary>
/// Human-readable name of the item
/// </summary>
public string LongName { get; set; }
/// <summary>
/// Internally used name of the item
/// </summary>
public string ShortName { get; set; }
}
}

View File

@@ -1,26 +0,0 @@
namespace RedumpLib.Attributes
{
/// <summary>
/// Attribute specifc to Language values
/// </summary>
/// <remarks>
/// Some languages have multiple proper names. Should all be supported?
/// </remarks>
public class LanguageAttribute : HumanReadableAttribute
{
/// <summary>
/// ISO 639-1 Code
/// </summary>
public string TwoLetterCode { get; set; } = null;
/// <summary>
/// ISO 639-2 Code (Standard or Bibliographic)
/// </summary>
public string ThreeLetterCode { get; set; } = null;
/// <summary>
/// ISO 639-2 Code (Terminology)
/// </summary>
public string ThreeLetterCodeAlt { get; set; } = null;
}
}

View File

@@ -1,55 +0,0 @@
using RedumpLib.Data;
namespace RedumpLib.Attributes
{
/// <summary>
/// Attribute specifc to Redump System values
/// </summary>
public class SystemAttribute : HumanReadableAttribute
{
/// <summary>
/// Category for the system
/// </summary>
public SystemCategory Category { get; set; }
/// <summary>
/// System is restricted to dumpers
/// </summary>
public bool IsBanned { get; set; } = false;
/// <summary>
/// System has a CUE pack
/// </summary>
public bool HasCues { get; set; } = false;
/// <summary>
/// System has a DAT
/// </summary>
public bool HasDat { get; set; } = false;
/// <summary>
/// System has a decrypted keys pack
/// </summary>
public bool HasDkeys { get; set; } = false;
/// <summary>
/// System has a GDI pack
/// </summary>
public bool HasGdi { get; set; } = false;
/// <summary>
/// System has a keys pack
/// </summary>
public bool HasKeys { get; set; } = false;
/// <summary>
/// System has an LSD pack
/// </summary>
public bool HasLsd { get; set; } = false;
/// <summary>
/// System has an SBI pack
/// </summary>
public bool HasSbi { get; set; } = false;
}
}

View File

@@ -1,26 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RedumpLib.Data;
namespace RedumpLib.Converters
{
/// <summary>
/// Serialize DiscCategory enum values
/// </summary>
public class DiscCategoryConverter : JsonConverter<DiscCategory?>
{
public override bool CanRead { get { return false; } }
public override DiscCategory? ReadJson(JsonReader reader, Type objectType, DiscCategory? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, DiscCategory? value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value.LongName() ?? string.Empty);
t.WriteTo(writer);
}
}
}

View File

@@ -1,26 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RedumpLib.Data;
namespace RedumpLib.Converters
{
/// <summary>
/// Serialize DiscType enum values
/// </summary>
public class DiscTypeConverter : JsonConverter<DiscType?>
{
public override bool CanRead { get { return false; } }
public override DiscType? ReadJson(JsonReader reader, Type objectType, DiscType? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, DiscType? value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value.LongName() ?? string.Empty);
t.WriteTo(writer);
}
}
}

View File

@@ -1,32 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RedumpLib.Data;
namespace RedumpLib.Converters
{
/// <summary>
/// Serialize Language enum values
/// </summary>
public class LanguageConverter : JsonConverter<Language?[]>
{
public override bool CanRead { get { return false; } }
public override Language?[] ReadJson(JsonReader reader, Type objectType, Language?[] existingValue, bool hasExistingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, Language?[] value, JsonSerializer serializer)
{
JArray array = new JArray();
foreach (var val in value)
{
JToken t = JToken.FromObject(val.ShortName() ?? string.Empty);
array.Add(t);
}
array.WriteTo(writer);
}
}
}

View File

@@ -1,32 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RedumpLib.Data;
namespace RedumpLib.Converters
{
/// <summary>
/// Serialize LanguageSelection enum values
/// </summary>
public class LanguageSelectionConverter : JsonConverter<LanguageSelection?[]>
{
public override bool CanRead { get { return false; } }
public override LanguageSelection?[] ReadJson(JsonReader reader, Type objectType, LanguageSelection?[] existingValue, bool hasExistingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, LanguageSelection?[] value, JsonSerializer serializer)
{
JArray array = new JArray();
foreach (var val in value)
{
JToken t = JToken.FromObject(val.LongName() ?? string.Empty);
array.Add(t);
}
array.WriteTo(writer);
}
}
}

View File

@@ -1,26 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RedumpLib.Data;
namespace RedumpLib.Converters
{
/// <summary>
/// Serialize Region enum values
/// </summary>
public class RegionConverter : JsonConverter<Region?>
{
public override bool CanRead { get { return false; } }
public override Region? ReadJson(JsonReader reader, Type objectType, Region? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, Region? value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value.ShortName() ?? string.Empty);
t.WriteTo(writer);
}
}
}

View File

@@ -1,26 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RedumpLib.Data;
namespace RedumpLib.Converters
{
/// <summary>
/// Serialize RedumpSystem enum values
/// </summary>
public class SystemConverter : JsonConverter<RedumpSystem?>
{
public override bool CanRead { get { return false; } }
public override RedumpSystem? ReadJson(JsonReader reader, Type objectType, RedumpSystem? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, RedumpSystem? value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value.ShortName() ?? string.Empty);
t.WriteTo(writer);
}
}
}

View File

@@ -1,26 +0,0 @@
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using RedumpLib.Data;
namespace RedumpLib.Converters
{
/// <summary>
/// Serialize YesNo enum values
/// </summary>
public class YesNoConverter : JsonConverter<YesNo?>
{
public override bool CanRead { get { return false; } }
public override YesNo? ReadJson(JsonReader reader, Type objectType, YesNo? existingValue, bool hasExistingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, YesNo? value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value.LongName() ?? string.Empty);
t.WriteTo(writer);
}
}
}

View File

@@ -1,306 +0,0 @@
using System.Text.RegularExpressions;
namespace RedumpLib.Data
{
public static class Constants
{
// TODO: Add RegexOptions.Compiled
#region Regular Expressions
/// <summary>
/// Regex matching the added field on a disc page
/// </summary>
public static Regex AddedRegex = new Regex(@"<tr><th>Added</th><td>(.*?)</td></tr>");
/// <summary>
/// Regex matching the barcode field on a disc page
/// </summary>
public static Regex BarcodeRegex = new Regex(@"<tr><th>Barcode</th></tr><tr><td>(.*?)</td></tr>");
/// <summary>
/// Regex matching the BCA field on a disc page
/// </summary>
public static Regex BcaRegex = new Regex(@"<h3>BCA .*?/></h3></td><td .*?></td></tr>"
+ "<tr><th>Row</th><th>Contents</th><th>ASCII</th></tr>"
+ "<tr><td>(?<row1number>.*?)</td><td>(?<row1contents>.*?)</td><td>(?<row1ascii>.*?)</td></tr>"
+ "<tr><td>(?<row2number>.*?)</td><td>(?<row2contents>.*?)</td><td>(?<row2ascii>.*?)</td></tr>"
+ "<tr><td>(?<row3number>.*?)</td><td>(?<row3contents>.*?)</td><td>(?<row3ascii>.*?)</td></tr>"
+ "<tr><td>(?<row4number>.*?)</td><td>(?<row4contents>.*?)</td><td>(?<row4ascii>.*?)</td></tr>", RegexOptions.Singleline);
/// <summary>
/// Regex matching the category field on a disc page
/// </summary>
public static Regex CategoryRegex = new Regex(@"<tr><th>Category</th><td>(.*?)</td></tr>");
/// <summary>
/// Regex matching the comments field on a disc page
/// </summary>
public static Regex CommentsRegex = new Regex(@"<tr><th>Comments</th></tr><tr><td>(.*?)</td></tr>", RegexOptions.Singleline);
/// <summary>
/// Regex matching the contents field on a disc page
/// </summary>
public static Regex ContentsRegex = new Regex(@"<tr><th>Contents</th></tr><tr .*?><td>(.*?)</td></tr>", RegexOptions.Singleline);
/// <summary>
/// Regex matching individual disc links on a results page
/// </summary>
public static Regex DiscRegex = new Regex(@"<a href=""/disc/(\d+)/"">");
/// <summary>
/// Regex matching the disc number or letter field on a disc page
/// </summary>
public static Regex DiscNumberLetterRegex = new Regex(@"\((.*?)\)");
/// <summary>
/// Regex matching the dumpers on a disc page
/// </summary>
public static Regex DumpersRegex = new Regex(@"<a href=""/discs/dumper/(.*?)/"">");
/// <summary>
/// Regex matching the edition field on a disc page
/// </summary>
public static Regex EditionRegex = new Regex(@"<tr><th>Edition</th><td>(.*?)</td></tr>");
/// <summary>
/// Regex matching the error count field on a disc page
/// </summary>
public static Regex ErrorCountRegex = new Regex(@"<tr><th>Errors count</th><td>(.*?)</td></tr>");
/// <summary>
/// Regex matching the foreign title field on a disc page
/// </summary>
public static Regex ForeignTitleRegex = new Regex(@"<h2>(.*?)</h2>");
/// <summary>
/// Regex matching the "full match" ID list from a WIP disc page
/// </summary>
public static Regex FullMatchRegex = new Regex(@"<td class=""static"">full match ids: (.*?)</td>");
/// <summary>
/// Regex matching the languages field on a disc page
/// </summary>
public static Regex LanguagesRegex = new Regex(@"<img src=""/images/languages/(.*?)\.png"" alt="".*?"" title="".*?"" />\s*");
/// <summary>
/// Regex matching the last modified field on a disc page
/// </summary>
public static Regex LastModifiedRegex = new Regex(@"<tr><th>Last modified</th><td>(.*?)</td></tr>");
/// <summary>
/// Regex matching the media field on a disc page
/// </summary>
public static Regex MediaRegex = new Regex(@"<tr><th>Media</th><td>(.*?)</td></tr>");
/// <summary>
/// Regex matching individual WIP disc links on a results page
/// </summary>
public static Regex NewDiscRegex = new Regex(@"<a (style=.*)?href=""/newdisc/(\d+)/"">");
/// <summary>
/// Regex matching the "partial match" ID list from a WIP disc page
/// </summary>
public static Regex PartialMatchRegex = new Regex(@"<td class=""static"">partial match ids: (.*?)</td>");
/// <summary>
/// Regex matching the PVD field on a disc page
/// </summary>
public static Regex PvdRegex = new Regex(@"<h3>Primary Volume Descriptor (PVD) <img .*?/></h3></td><td .*?></td></tr>"
+ @"<tr><th>Record / Entry</th><th>Contents</th><th>Date</th><th>Time</th><th>GMT</th></tr>"
+ @"<tr><td>Creation</td><td>(?<creationbytes>.*?)</td><td>(?<creationdate>.*?)</td><td>(?<creationtime>.*?)</td><td>(?<creationtimezone>.*?)</td></tr>"
+ @"<tr><td>Modification</td><td>(?<modificationbytes>.*?)</td><td>(?<modificationdate>.*?)</td><td>(?<modificationtime>.*?)</td><td>(?<modificationtimezone>.*?)</td></tr>"
+ @"<tr><td>Expiration</td><td>(?<expirationbytes>.*?)</td><td>(?<expirationdate>.*?)</td><td>(?<expirationtime>.*?)</td><td>(?<expirationtimezone>.*?)</td></tr>"
+ @"<tr><td>Effective</td><td>(?<effectivebytes>.*?)</td><td>(?<effectivedate>.*?)</td><td>(?<effectivetime>.*?)</td><td>(?<effectivetimezone>.*?)</td></tr>", RegexOptions.Singleline);
/// <summary>
/// Regex matching the region field on a disc page
/// </summary>
public static Regex RegionRegex = new Regex(@"<tr><th>Region</th><td><a href=""/discs/region/(.*?)/"">");
/// <summary>
/// Regex matching a double-layer disc ringcode information
/// </summary>
public static Regex RingCodeDoubleRegex = new Regex(@"", RegexOptions.Singleline); // Varies based on available fields, like Addtional Mould
/// <summary>
/// Regex matching a single-layer disc ringcode information
/// </summary>
public static Regex RingCodeSingleRegex = new Regex(@"", RegexOptions.Singleline); // Varies based on available fields, like Addtional Mould
/// <summary>
/// Regex matching the serial field on a disc page
/// </summary>
public static Regex SerialRegex = new Regex(@"<tr><th>Serial</th><td>(.*?)</td></tr>");
/// <summary>
/// Regex matching the system field on a disc page
/// </summary>
public static Regex SystemRegex = new Regex(@"<tr><th>System</th><td><a href=""/discs/system/(.*?)/"">");
/// <summary>
/// Regex matching the title field on a disc page
/// </summary>
public static Regex TitleRegex = new Regex(@"<h1>(.*?)</h1>");
/// <summary>
/// Regex matching the current nonce token for login
/// </summary>
public static Regex TokenRegex = new Regex(@"<input type=""hidden"" name=""csrf_token"" value=""(.*?)"" />");
/// <summary>
/// Regex matching a single track on a disc page
/// </summary>
public static Regex TrackRegex = new Regex(@"<tr><td>(?<number>.*?)</td><td>(?<type>.*?)</td><td>(?<pregap>.*?)</td><td>(?<length>.*?)</td><td>(?<sectors>.*?)</td><td>(?<size>.*?)</td><td>(?<crc32>.*?)</td><td>(?<md5>.*?)</td><td>(?<sha1>.*?)</td></tr>", RegexOptions.Singleline);
/// <summary>
/// Regex matching the track count on a disc page
/// </summary>
public static Regex TrackCountRegex = new Regex(@"<tr><th>Number of tracks</th><td>(.*?)</td></tr>");
/// <summary>
/// Regex matching the version field on a disc page
/// </summary>
public static Regex VersionRegex = new Regex(@"<tr><th>Version</th><td>(.*?)</td></tr>");
/// <summary>
/// Regex matching the write offset field on a disc page
/// </summary>
public static Regex WriteOffsetRegex = new Regex(@"<tr><th>Write offset</th><td>(.*?)</td></tr>");
#endregion
#region URLs
/// <summary>
/// Redump disc page URL template
/// </summary>
public const string DiscPageUrl = @"http://redump.org/disc/{0}/";
/// <summary>
/// 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 template
/// </summary>
public const string LoginUrl = "http://forum.redump.org/login/";
/// <summary>
/// Redump CUE pack URL template
/// </summary>
public const string PackCuesUrl = @"http://redump.org/cues/{0}/";
/// <summary>
/// Redump DAT pack URL template
/// </summary>
public const string PackDatfileUrl = @"http://redump.org/datfile/{0}/";
/// <summary>
/// Redump DKEYS pack URL template
/// </summary>
public const string PackDkeysUrl = @"http://redump.org/dkeys/{0}/";
/// <summary>
/// Redump GDI pack URL template
/// </summary>
public const string PackGdiUrl = @"http://redump.org/gdi/{0}/";
/// <summary>
/// Redump KEYS pack URL template
/// </summary>
public const string PackKeysUrl = @"http://redump.org/keys/{0}/";
/// <summary>
/// Redump LSD pack URL template
/// </summary>
public const string PackLsdUrl = @"http://redump.org/lsd/{0}/";
/// <summary>
/// Redump SBI pack URL template
/// </summary>
public const string PackSbiUrl = @"http://redump.org/sbi/{0}/";
/// <summary>
/// Redump quicksearch URL template
/// </summary>
public const string QuickSearchUrl = @"http://redump.org/discs/quicksearch/{0}/?page={1}";
/// <summary>
/// Redump user dumps URL template
/// </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>
public const string WipDiscPageUrl = @"http://redump.org/newdisc/{0}/";
/// <summary>
/// Redump WIP dumps queue URL
/// </summary>
public const string WipDumpsUrl = @"http://redump.org/discs-wip/";
#endregion
#region URL Extensions
/// <summary>
/// Changes page subpath
/// </summary>
public const string ChangesExt = "changes/";
/// <summary>
/// Cuesheet download subpath
/// </summary>
public const string CueExt = "cue/";
/// <summary>
/// Edit page subpath
/// </summary>
public const string EditExt = "edit/";
/// <summary>
/// GDI download subpath
/// </summary>
public const string GdiExt = "gdi/";
/// <summary>
/// Key download subpath
/// </summary>
public const string KeyExt = "key/";
/// <summary>
/// LSD download subpath
/// </summary>
public const string LsdExt = "lsd/";
/// <summary>
/// MD5 download subpath
/// </summary>
public const string Md5Ext = "md5/";
/// <summary>
/// SBI download subpath
/// </summary>
public const string SbiExt = "sbi/";
/// <summary>
/// SFV download subpath
/// </summary>
public const string SfvExt = "sfv/";
/// <summary>
/// SHA1 download subpath
/// </summary>
public const string Sha1Ext = "sha1/";
#endregion
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,561 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using RedumpLib.Converters;
namespace RedumpLib.Data
{
public class SubmissionInfo : ICloneable
{
/// <summary>
/// Version of the current schema
/// </summary>
[JsonProperty(PropertyName = "schema_version", DefaultValueHandling = DefaultValueHandling.Ignore)]
public int SchemaVersion { get; set; } = 2;
/// <summary>
/// Fully matched Redump ID
/// </summary>
[JsonIgnore]
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
/// </summary>
[JsonIgnore]
public DateTime? Added { get; set; }
/// <summary>
/// DateTime of when the disc was last modified
/// </summary>
[JsonIgnore]
public DateTime? LastModified { get; set; }
[JsonProperty(PropertyName = "common_disc_info", DefaultValueHandling = DefaultValueHandling.Ignore)]
public CommonDiscInfoSection CommonDiscInfo { get; set; } = new CommonDiscInfoSection();
[JsonProperty(PropertyName = "versions_and_editions", DefaultValueHandling = DefaultValueHandling.Ignore)]
public VersionAndEditionsSection VersionAndEditions { get; set; } = new VersionAndEditionsSection();
[JsonProperty(PropertyName = "edc", DefaultValueHandling = DefaultValueHandling.Ignore)]
public EDCSection EDC { get; set; } = new EDCSection();
[JsonProperty(PropertyName = "parent_clone_relationship", DefaultValueHandling = DefaultValueHandling.Ignore)]
public ParentCloneRelationshipSection ParentCloneRelationship { get; set; } = new ParentCloneRelationshipSection();
[JsonProperty(PropertyName = "extras", DefaultValueHandling = DefaultValueHandling.Ignore)]
public ExtrasSection Extras { get; set; } = new ExtrasSection();
[JsonProperty(PropertyName = "copy_protection", DefaultValueHandling = DefaultValueHandling.Ignore)]
public CopyProtectionSection CopyProtection { get; set; } = new CopyProtectionSection();
[JsonProperty(PropertyName = "dumpers_and_status", DefaultValueHandling = DefaultValueHandling.Ignore)]
public DumpersAndStatusSection DumpersAndStatus { get; set; } = new DumpersAndStatusSection();
[JsonProperty(PropertyName = "tracks_and_write_offsets", DefaultValueHandling = DefaultValueHandling.Ignore)]
public TracksAndWriteOffsetsSection TracksAndWriteOffsets { get; set; } = new TracksAndWriteOffsetsSection();
[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,
FullyMatchedID = this.FullyMatchedID,
PartiallyMatchedIDs = this.PartiallyMatchedIDs,
Added = this.Added,
LastModified = this.LastModified,
CommonDiscInfo = this.CommonDiscInfo?.Clone() as CommonDiscInfoSection,
VersionAndEditions = this.VersionAndEditions?.Clone() as VersionAndEditionsSection,
EDC = this.EDC?.Clone() as EDCSection,
ParentCloneRelationship = this.ParentCloneRelationship?.Clone() as ParentCloneRelationshipSection,
Extras = this.Extras?.Clone() as ExtrasSection,
CopyProtection = this.CopyProtection?.Clone() as CopyProtectionSection,
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),
};
}
}
/// <summary>
/// Common disc info section of New Disc Form
/// </summary>
public class CommonDiscInfoSection : ICloneable
{
// Name not defined by Redump
[JsonProperty(PropertyName = "d_system", Required = Required.AllowNull)]
[JsonConverter(typeof(SystemConverter))]
public RedumpSystem? System { get; set; }
// Name not defined by Redump
[JsonProperty(PropertyName = "d_media", Required = Required.AllowNull)]
[JsonConverter(typeof(DiscTypeConverter))]
public DiscType? Media { get; set; }
[JsonProperty(PropertyName = "d_title", Required = Required.AllowNull)]
public string Title { get; set; }
[JsonProperty(PropertyName = "d_title_foreign", DefaultValueHandling = DefaultValueHandling.Ignore)]
public string ForeignTitleNonLatin { get; set; }
[JsonProperty(PropertyName = "d_number", NullValueHandling = NullValueHandling.Ignore)]
public string DiscNumberLetter { get; set; }
[JsonProperty(PropertyName = "d_label", NullValueHandling = NullValueHandling.Ignore)]
public string DiscTitle { get; set; }
[JsonProperty(PropertyName = "d_category", Required = Required.AllowNull)]
[JsonConverter(typeof(DiscCategoryConverter))]
public DiscCategory? Category { get; set; }
[JsonProperty(PropertyName = "d_region", Required = Required.AllowNull)]
[JsonConverter(typeof(RegionConverter))]
public Region? Region { get; set; }
[JsonProperty(PropertyName = "d_languages", Required = Required.AllowNull)]
[JsonConverter(typeof(LanguageConverter))]
public Language?[] Languages { get; set; }
[JsonProperty(PropertyName = "d_languages_selection", NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Ignore)]
[JsonConverter(typeof(LanguageSelectionConverter))]
public LanguageSelection?[] LanguageSelection { get; set; }
[JsonProperty(PropertyName = "d_serial", NullValueHandling = NullValueHandling.Ignore)]
public string Serial { get; set; }
[JsonProperty(PropertyName = "d_ring", NullValueHandling = NullValueHandling.Ignore)]
public string Ring { get; private set; }
[JsonProperty(PropertyName = "d_ring_0_id", NullValueHandling = NullValueHandling.Ignore)]
public string RingId { get; private set; }
[JsonProperty(PropertyName = "d_ring_0_ma1", Required = Required.AllowNull)]
public string Layer0MasteringRing { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma1_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer0MasteringSID { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ts1", NullValueHandling = NullValueHandling.Ignore)]
public string Layer0ToolstampMasteringCode { get; set; }
[JsonProperty(PropertyName = "d_ring_0_mo1_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer0MouldSID { get; set; }
[JsonProperty(PropertyName = "d_ring_0_mo1", NullValueHandling = NullValueHandling.Ignore)]
public string Layer0AdditionalMould { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma2", Required = Required.AllowNull)]
public string Layer1MasteringRing { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma2_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer1MasteringSID { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ts2", NullValueHandling = NullValueHandling.Ignore)]
public string Layer1ToolstampMasteringCode { get; set; }
[JsonProperty(PropertyName = "d_ring_0_mo2_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer1MouldSID { get; set; }
[JsonProperty(PropertyName = "d_ring_0_mo2", NullValueHandling = NullValueHandling.Ignore)]
public string Layer1AdditionalMould { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma3", Required = Required.AllowNull)]
public string Layer2MasteringRing { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma3_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer2MasteringSID { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ts3", NullValueHandling = NullValueHandling.Ignore)]
public string Layer2ToolstampMasteringCode { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma4", Required = Required.AllowNull)]
public string Layer3MasteringRing { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ma4_sid", NullValueHandling = NullValueHandling.Ignore)]
public string Layer3MasteringSID { get; set; }
[JsonProperty(PropertyName = "d_ring_0_ts4", NullValueHandling = NullValueHandling.Ignore)]
public string Layer3ToolstampMasteringCode { get; set; }
[JsonProperty(PropertyName = "d_ring_0_offsets", NullValueHandling = NullValueHandling.Ignore)]
public string RingOffsetsHidden { get { return "1"; } }
[JsonProperty(PropertyName = "d_ring_0_0_id", NullValueHandling = NullValueHandling.Ignore)]
public string RingZeroId { get; private set; }
[JsonProperty(PropertyName = "d_ring_0_0_density", NullValueHandling = NullValueHandling.Ignore)]
public string RingZeroDensity { get; private set; }
[JsonProperty(PropertyName = "d_ring_0_0_value", NullValueHandling = NullValueHandling.Ignore)]
public string RingWriteOffset { get; set; }
[JsonProperty(PropertyName = "d_ring_count", NullValueHandling = NullValueHandling.Ignore)]
public string RingCount { get { return "1"; } }
[JsonProperty(PropertyName = "d_barcode", NullValueHandling = NullValueHandling.Ignore)]
public string Barcode { get; set; }
[JsonProperty(PropertyName = "d_date", NullValueHandling = NullValueHandling.Ignore)]
public string EXEDateBuildDate { get; set; }
[JsonProperty(PropertyName = "d_errors", NullValueHandling = NullValueHandling.Ignore)]
public string ErrorsCount { get; set; }
[JsonProperty(PropertyName = "d_comments", NullValueHandling = NullValueHandling.Ignore)]
public string Comments { get; set; }
[JsonIgnore]
public Dictionary<SiteCode?, string> CommentsSpecialFields { get; set; }
[JsonProperty(PropertyName = "d_contents", NullValueHandling = NullValueHandling.Ignore)]
public string Contents { get; set; }
[JsonIgnore]
public Dictionary<SiteCode?, string> ContentsSpecialFields { get; set; }
public object Clone()
{
return new CommonDiscInfoSection
{
System = this.System,
Media = this.Media,
Title = this.Title,
ForeignTitleNonLatin = this.ForeignTitleNonLatin,
DiscNumberLetter = this.DiscNumberLetter,
DiscTitle = this.DiscTitle,
Category = this.Category,
Region = this.Region,
Languages = this.Languages?.Clone() as Language?[],
LanguageSelection = this.LanguageSelection?.Clone() as LanguageSelection?[],
Serial = this.Serial,
Ring = this.Ring,
RingId = this.RingId,
Layer0MasteringRing = this.Layer0MasteringRing,
Layer0MasteringSID = this.Layer0MasteringSID,
Layer0ToolstampMasteringCode = this.Layer0ToolstampMasteringCode,
Layer0MouldSID = this.Layer0MouldSID,
Layer0AdditionalMould = this.Layer0AdditionalMould,
Layer1MasteringRing = this.Layer1MasteringRing,
Layer1MasteringSID = this.Layer1MasteringSID,
Layer1ToolstampMasteringCode = this.Layer1ToolstampMasteringCode,
Layer1MouldSID = this.Layer1MouldSID,
Layer1AdditionalMould = this.Layer1AdditionalMould,
Layer2MasteringRing = this.Layer2MasteringRing,
Layer2MasteringSID = this.Layer2MasteringSID,
Layer2ToolstampMasteringCode = this.Layer2ToolstampMasteringCode,
Layer3MasteringRing = this.Layer3MasteringRing,
Layer3MasteringSID = this.Layer3MasteringSID,
Layer3ToolstampMasteringCode = this.Layer3ToolstampMasteringCode,
RingZeroId = this.RingZeroId,
RingZeroDensity = this.RingZeroDensity,
RingWriteOffset = this.RingWriteOffset,
Barcode = this.Barcode,
EXEDateBuildDate = this.EXEDateBuildDate,
ErrorsCount = this.ErrorsCount,
Comments = this.Comments,
CommentsSpecialFields = this.CommentsSpecialFields?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
Contents = this.Contents,
ContentsSpecialFields = this.ContentsSpecialFields?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
};
}
}
/// <summary>
/// Version and editions section of New Disc form
/// </summary>
public class VersionAndEditionsSection : ICloneable
{
[JsonProperty(PropertyName = "d_version", NullValueHandling = NullValueHandling.Ignore)]
public string Version { get; set; }
[JsonProperty(PropertyName = "d_version_datfile", NullValueHandling = NullValueHandling.Ignore)]
public string VersionDatfile { get; set; }
[JsonProperty(PropertyName = "d_editions", NullValueHandling = NullValueHandling.Ignore)]
public string[] CommonEditions { get; set; }
[JsonProperty(PropertyName = "d_editions_text", NullValueHandling = NullValueHandling.Ignore)]
public string OtherEditions { get; set; }
public object Clone()
{
return new VersionAndEditionsSection
{
Version = this.Version,
VersionDatfile = this.VersionDatfile,
CommonEditions = this.CommonEditions,
OtherEditions = this.OtherEditions,
};
}
}
/// <summary>
/// EDC section of New Disc form (PSX only)
/// </summary>
public class EDCSection : ICloneable
{
[JsonProperty(PropertyName = "d_edc", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(YesNoConverter))]
public YesNo? EDC { get; set; }
public object Clone()
{
return new EDCSection
{
EDC = this.EDC,
};
}
}
/// <summary>
/// Parent/Clone relationship section of New Disc form
/// </summary>
public class ParentCloneRelationshipSection : ICloneable
{
[JsonProperty(PropertyName = "d_parent_id", NullValueHandling = NullValueHandling.Ignore)]
public string ParentID { get; set; }
[JsonProperty(PropertyName = "d_is_regional_parent", NullValueHandling = NullValueHandling.Ignore)]
public bool RegionalParent { get; set; }
public object Clone()
{
return new ParentCloneRelationshipSection
{
ParentID = this.ParentID,
RegionalParent = this.RegionalParent,
};
}
}
/// <summary>
/// Extras section of New Disc form
/// </summary>
public class ExtrasSection : ICloneable
{
[JsonProperty(PropertyName = "d_pvd", NullValueHandling = NullValueHandling.Ignore)]
public string PVD { get; set; }
[JsonProperty(PropertyName = "d_d1_key", NullValueHandling = NullValueHandling.Ignore)]
public string DiscKey { get; set; }
[JsonProperty(PropertyName = "d_d2_key", NullValueHandling = NullValueHandling.Ignore)]
public string DiscID { get; set; }
[JsonProperty(PropertyName = "d_pic_data", NullValueHandling = NullValueHandling.Ignore)]
public string PIC { get; set; }
[JsonProperty(PropertyName = "d_header", NullValueHandling = NullValueHandling.Ignore)]
public string Header { get; set; }
[JsonProperty(PropertyName = "d_bca", NullValueHandling = NullValueHandling.Ignore)]
public string BCA { get; set; }
[JsonProperty(PropertyName = "d_ssranges", NullValueHandling = NullValueHandling.Ignore)]
public string SecuritySectorRanges { get; set; }
public object Clone()
{
return new ExtrasSection
{
PVD = this.PVD,
DiscKey = this.DiscKey,
DiscID = this.DiscID,
PIC = this.PIC,
Header = this.Header,
BCA = this.BCA,
SecuritySectorRanges = this.SecuritySectorRanges,
};
}
}
/// <summary>
/// Copy protection section of New Disc form
/// </summary>
public class CopyProtectionSection : ICloneable
{
[JsonProperty(PropertyName = "d_protection_a", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(YesNoConverter))]
public YesNo? AntiModchip { get; set; }
[JsonProperty(PropertyName = "d_protection_1", NullValueHandling = NullValueHandling.Ignore)]
[JsonConverter(typeof(YesNoConverter))]
public YesNo? LibCrypt { get; set; }
[JsonProperty(PropertyName = "d_libcrypt", NullValueHandling = NullValueHandling.Ignore)]
public string LibCryptData { get; set; }
[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; }
public object Clone()
{
return new CopyProtectionSection
{
AntiModchip = this.AntiModchip,
LibCrypt = this.LibCrypt,
LibCryptData = this.LibCryptData,
Protection = this.Protection,
FullProtections = this.FullProtections?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
SecuROMData = this.SecuROMData,
};
}
}
/// <summary>
/// Dumpers and status section of New Disc form (Moderator only)
/// </summary>
public class DumpersAndStatusSection : ICloneable
{
[JsonProperty(PropertyName = "d_status", NullValueHandling = NullValueHandling.Ignore)]
public DumpStatus Status { get; set; }
[JsonProperty(PropertyName = "d_dumpers", NullValueHandling = NullValueHandling.Ignore)]
public string[] Dumpers { get; set; }
[JsonProperty(PropertyName = "d_dumpers_text", NullValueHandling = NullValueHandling.Ignore)]
public string OtherDumpers { get; set; }
public object Clone()
{
return new DumpersAndStatusSection
{
Status = this.Status,
Dumpers = this.Dumpers?.Clone() as string[],
OtherDumpers = this.OtherDumpers,
};
}
}
/// <summary>
/// Tracks and write offsets section of New Disc form (CD/GD-based)
/// </summary>
public class TracksAndWriteOffsetsSection : ICloneable
{
[JsonProperty(PropertyName = "d_tracks", NullValueHandling = NullValueHandling.Ignore)]
public string ClrMameProData { get; set; }
[JsonProperty(PropertyName = "d_cue", NullValueHandling = NullValueHandling.Ignore)]
public string Cuesheet { get; set; }
[JsonProperty(PropertyName = "d_offset", NullValueHandling = NullValueHandling.Ignore)]
public int[] CommonWriteOffsets { get; set; }
[JsonProperty(PropertyName = "d_offset_text", NullValueHandling = NullValueHandling.Ignore)]
public string OtherWriteOffsets { get; set; }
public object Clone()
{
return new TracksAndWriteOffsetsSection
{
ClrMameProData = this.ClrMameProData,
Cuesheet = this.Cuesheet,
CommonWriteOffsets = this.CommonWriteOffsets?.Clone() as int[],
OtherWriteOffsets = this.OtherWriteOffsets,
};
}
}
/// <summary>
/// Size &amp; checksums section of New Disc form (DVD/BD/UMD-based)
/// </summary>
public class SizeAndChecksumsSection : ICloneable
{
[JsonProperty(PropertyName = "d_layerbreak", NullValueHandling = NullValueHandling.Ignore)]
public long Layerbreak { get; set; }
[JsonProperty(PropertyName = "d_layerbreak_2", NullValueHandling = NullValueHandling.Ignore)]
public long Layerbreak2 { get; set; }
[JsonProperty(PropertyName = "d_layerbreak_3", NullValueHandling = NullValueHandling.Ignore)]
public long Layerbreak3 { get; set; }
[JsonProperty(PropertyName = "d_size", NullValueHandling = NullValueHandling.Ignore)]
public long Size { get; set; }
[JsonProperty(PropertyName = "d_crc32", NullValueHandling = NullValueHandling.Ignore)]
public string CRC32 { get; set; }
[JsonProperty(PropertyName = "d_md5", NullValueHandling = NullValueHandling.Ignore)]
public string MD5 { get; set; }
[JsonProperty(PropertyName = "d_sha1", NullValueHandling = NullValueHandling.Ignore)]
public string SHA1 { get; set; }
public object Clone()
{
return new SizeAndChecksumsSection
{
Layerbreak = this.Layerbreak,
Layerbreak2 = this.Layerbreak2,
Layerbreak3 = this.Layerbreak3,
Size = this.Size,
CRC32 = this.CRC32,
MD5 = this.MD5,
SHA1 = this.SHA1,
};
}
}
/// <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,12 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;netstandard2.1;net6.0</TargetFrameworks>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>

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