Compare commits

...

1657 Commits
1.06 ... 3.1.1

Author SHA1 Message Date
Matt Nadareski
fe20905524 Bump version 2024-02-20 10:34:48 -05:00
Deterous
88f19180a4 Update LibIRD, disable UI elements when creating IRD (#653)
* Update LibIRD, disable UI when creating IRD

* Update changelog
2024-02-20 07:25:03 -08:00
Deterous
de89968a1d Add a GUI for PS3 IRD Creation (#647)
* Create a non-functional Create IRD window

* Add PIC and getkey.log options for IRDs

* Disable IRD creation window for unsupport .NET versions

* Finalise UI and parse inputs

* Add LibIRD package

* Manually define PIC in IRD creation

* Better output file browser

* Bump LibIRD version

* Update changelog

* Custom Disc ID, bump LibIRD version

* Ignore custom Disc ID for BD-50

* Provide a status message when creating IRD

* Better logpath enabled logic

* Nicer PIC UX

* Scrollbar only appears for unusually tall PIC
2024-02-18 17:38:28 -08:00
Matt Nadareski
8fc53c91b0 Limit DVD protection outputs (fixes #651) 2024-02-17 23:23:00 -05:00
Matt Nadareski
1a1fbd4b40 Fix Aaru drive parameter generation (fixes #652) 2024-02-17 23:09:29 -05:00
Matt Nadareski
cac6c3049b Add funworld Photo Play detection (fixes #650) 2024-02-15 12:56:30 -05:00
Matt Nadareski
6a6871e922 Write outputs with UTF-8 2024-02-15 12:00:40 -05:00
Deterous
4a02a3efac Fix DIC log parsing for SS version (#649) 2024-02-12 08:30:50 -08:00
Matt Nadareski
f6eb961af4 Make Redumper the default for new users (fixes #638) 2024-02-06 12:57:26 -05:00
Matt Nadareski
faeaaef02a Remove .NET 6 from auto-builds (fixes #646) 2024-02-06 12:50:01 -05:00
Matt Nadareski
ebf393e634 Bump version and copyright 2024-02-06 11:20:57 -05:00
Deterous
3fbd4ea719 Fix compiler warning (#645)
* Fix compiler warning about PresentationFramework.Aero2

* Update changelog
2024-02-06 07:53:45 -08:00
Matt Nadareski
d09ff6cf1c Enable Windows builds on Linux and Mac 2024-02-05 00:41:11 -05:00
Matt Nadareski
1dc0d57d47 Remove -disc2 from Cleanrip serial (fixes #644) 2024-02-03 19:39:30 -05:00
Matt Nadareski
a748bd4d3a Fix build from rushed code 2024-02-01 11:11:13 -05:00
Matt Nadareski
35dec7fe57 Retrieve serial from Cleanrip (fixes #643) 2024-02-01 11:05:42 -05:00
Deterous
c22d16349a Verbose Redumper log by default (#642) 2024-01-30 19:12:30 -08:00
Deterous
0d77a8950c Exclude extra tracks when finding disc matches (#641) 2024-01-30 19:08:41 -08:00
Deterous
285e94ca69 Parse PSX/PS2/KP2 exe date from logs (#639)
* Parse EXE date from Redumper

* Parse EXE date from DIC logs

* Fix DIC exe date parsing

* Split PS EXE name from EXE info

* Remove redundant path splitting
2024-01-30 19:00:08 -08:00
Matt Nadareski
747ac4ea3b Detect Photo CD 2024-01-25 20:53:21 -05:00
Matt Nadareski
405ae7c7e4 Add UMD handling for the disc info window 2024-01-24 22:56:50 -05:00
Matt Nadareski
f5ebe968c0 Fix information pulling for CleanRip and UIC (fixes #637) 2024-01-24 22:50:16 -05:00
Deterous
06a61b17cb Add a GUI for MPF.Check (#635)
* Draft MPF.Check GUI window

* Functional MPF.Check GUI

* Update changelog

* Show DiscInformationWindow after Check Dump

* Change layout

* Refactor
2024-01-24 11:57:21 -08:00
Matt Nadareski
9e8e4f6e36 Skip warning line in Redumper log 2024-01-24 10:17:58 -05:00
Deterous
fa72211b57 Normalize Disc Title in Submission Info (#634)
* NormalizeDiscTitle for SubmissionInfo

* Update changelog
2024-01-19 09:53:52 -08:00
Deterous
d5f66000a9 Differentiate CD32 from CDTV (#633) 2024-01-18 20:28:23 -08:00
Deterous
a52ba0aa7a Detect CDTV discs (#632) 2024-01-18 20:00:45 -08:00
Deterous
eb045928f9 Prevent crashing on invalid parameters (#631)
* Prevent crashing on invalid parameters

* Parse hex strings properly

* Helper function for hex numbers

* remove region label
2024-01-18 19:16:09 -08:00
fuzzball
440302495b Correct missing space in PVD (#628)
* Remove trim

* Update changelist
2024-01-16 05:54:56 -08:00
Deterous
0732e9db78 Retrieve volume label from logs (#627)
* Retrieve volume label from DIC and redumper logs

* Fix logic

* Remove unnecessary using

* Update changelog

* Sanitise label somewhat

* Refactor
2024-01-15 19:14:34 -08:00
Deterous
a167652b2b Check for presence of complete dump from other programs (#625)
* Check for presence of complete dump from other programs

* Better changelog message

* Refactor
2024-01-10 19:24:17 -08:00
Deterous
cfa07c1918 Allow variables in output path (#624)
* Allow variables in output path and default output path

* Remove angle brackets when normalising path

* Add tooltip hover text for default output path

* Better tooltip formatting

* Use percent sign rather than angle brackets as variable delimiter

* Refactor
2024-01-10 16:42:34 -08:00
Deterous
53b31f91cf Use PSX/PS2 serial as filename when Volume Label not present (#623)
* Use PSX/PS2 serial as volume label if none found

* Refactor
2024-01-09 14:02:17 -08:00
Deterous
01cbd2cff5 Update Redumper to build 311 (#622)
* Update redumper to build 311

* Update changelog

* Deal with new redumper parameters properly
2024-01-08 17:45:08 -08:00
Deterous
65ad629ee0 Cleanup !protectionInfo.txt (#621)
* Remove drive letter from !protectionInfo.txt

* Sort files in !protectionInfo.txt

* Add option for removing drive letter

* Refactor niceties
2024-01-08 17:20:30 -08:00
fuzzball
06adbde715 Support ringcode and PIC for triple/quad-layer (#620)
* Support ringcodes for triple/quad-layer

* Increase PIC length of triple/quad-layer for dic

* Increase PIC length of triple/quad-layer for redumper

* Change the method of determining PIC length

* Update change logs

* Remove braces from if statements
2024-01-08 13:53:48 -08:00
Deterous
1e5000bd8a Support redumper skeleton and hash files (#616)
* Add .hash/.skeleton files to log zip, update redumper to build 306, update SabreTools.Serialization

* Improvements to .skeleton/.hash code

* Revert redumper update, remove redudant string creation

* Deal with nullable strings

* Update changelog

* Invert if for readability
2024-01-05 20:06:46 -08:00
Deterous
8cb0b37e80 Get BD PIC Identifier for redumper (#619)
* Get PIC Identifier for bluray redumper

* Update changelog
2024-01-02 17:50:48 -08:00
Matt Nadareski
32c12e1332 Make missing hash data clearer 2023-12-14 20:27:38 -05:00
Matt Nadareski
09b307aa24 Fix commented out code 2023-12-07 23:22:12 -05:00
Matt Nadareski
a5a8fbbf51 Update Redumper to build 294 2023-12-07 21:38:38 -05:00
Matt Nadareski
b366d236c8 Update RedumpLib 2023-12-05 11:18:58 -05:00
Matt Nadareski
a833e926f3 Bump version 2023-12-04 12:10:19 -05:00
Matt Nadareski
950be07bf0 Handle or suppress some messages 2023-12-01 23:58:01 -05:00
Matt Nadareski
4c5c1417e9 Remove .NET Framework 3.5 from build script 2023-12-01 23:31:59 -05:00
Matt Nadareski
6fdc3412e0 Fix build warning for NRE 2023-12-01 23:28:45 -05:00
Matt Nadareski
807b0c5f9e Fix using SHA-1 for track checks (fixes #613) 2023-12-01 20:46:38 -05:00
Matt Nadareski
9e0b64a1d1 Fix broken tests 2023-12-01 20:39:36 -05:00
Matt Nadareski
8cfbf2d9f1 Bump version 2023-12-01 12:49:43 -05:00
Matt Nadareski
0064737130 Handle most VS and dotnet differences 2023-11-30 23:34:44 -05:00
Matt Nadareski
292e3999c5 Reference .NET Framework 3.0 for 3.5 2023-11-30 20:51:04 -05:00
Matt Nadareski
5ed1e94d84 Import WinFX for .NET Framework 3.5 2023-11-30 18:00:45 -05:00
Matt Nadareski
5b094f57cb Update USE_ALL in Powershell script 2023-11-30 13:43:20 -05:00
Matt Nadareski
8066b5541e Update Redumper to build 271 2023-11-30 13:39:32 -05:00
Matt Nadareski
921d0207c2 Fix cross-framework UI styles 2023-11-30 13:12:56 -05:00
Matt Nadareski
4374ff7f74 Fix most .NET Framework 3.5 issues 2023-11-30 12:58:06 -05:00
Matt Nadareski
0be5825b5e Fix cross-framework UI rendering 2023-11-30 11:54:40 -05:00
Matt Nadareski
14c630bea7 Fix Powershell build script 2023-11-30 03:19:39 -05:00
Matt Nadareski
9a66c685fd Replace build script with Powershell
Thanks to Deterous for the original script conversion.
2023-11-30 00:48:20 -05:00
Matt Nadareski
5e0fa1ad47 Add Disc ID and Key fields in info window (fixes #609) 2023-11-30 00:08:39 -05:00
Matt Nadareski
79065dcc69 Read CSS for some copy protections (fixes #608) 2023-11-29 23:57:58 -05:00
Matt Nadareski
3d7355aee1 Bump version 2023-11-29 23:36:57 -05:00
Matt Nadareski
6e9a6724c3 Update Redumper to build 268 2023-11-29 17:30:46 -05:00
Matt Nadareski
be35acfb48 Support .NET Framework 3.5 in UI 2023-11-23 03:37:00 -05:00
Matt Nadareski
f1a46c2e82 Get UI building with Framework 4.0 again 2023-11-23 03:05:52 -05:00
Matt Nadareski
872959c889 Temporarily remove .NET Framework 4.0 from UI 2023-11-23 02:32:57 -05:00
Matt Nadareski
b848a401f8 Get UI building with Framework 4.0 2023-11-23 01:19:17 -05:00
Matt Nadareski
ee4762f8b3 Support .NET Framework 3.5 in UI.Core 2023-11-22 23:50:37 -05:00
Matt Nadareski
d68bcfb96a Support .NET Framework 2.0
This does not include the UI, as per the same reasons why .NET Framework 4.0 doesn't support it.
2023-11-22 23:38:59 -05:00
Matt Nadareski
d2433e4749 Update changelog 2023-11-22 23:28:57 -05:00
Matt Nadareski
56ec0e7057 Update compatibility libraries 2023-11-22 23:28:24 -05:00
Matt Nadareski
26e5d33d17 Support .NET Framework 3.5
This does not include the UI, as per the same reasons why .NET Framework 4.0 doesn't support it.
2023-11-22 16:26:31 -05:00
Matt Nadareski
8b8b51ace4 Re-enable .NET Framework 4.0 building in Check 2023-11-22 16:02:08 -05:00
Matt Nadareski
f350904441 Re-enable .NET Framework 4.0 building in UI.Core 2023-11-22 16:00:18 -05:00
Matt Nadareski
8e8e3368d0 Re-enable .NET Framework 4.0 building in Core 2023-11-22 15:56:43 -05:00
Matt Nadareski
4d8153dba1 Update to BinaryObjectScanner 3.0.2 2023-11-22 13:46:39 -05:00
Matt Nadareski
e4e4b5ecde Temporarily remove .NET Framework 4.0 2023-11-21 11:13:58 -05:00
Matt Nadareski
8373a6b8f5 Support proper async in .NET Framework 4.0 2023-11-20 16:46:54 -05:00
Matt Nadareski
45c51ebc80 Use TryGetValue on dictionaries 2023-11-20 13:24:17 -05:00
Matt Nadareski
af27085cc1 Get Check building with Framework 4.0 2023-11-20 13:20:48 -05:00
Matt Nadareski
82e3707dce Get UI.Core building with Framework 4.0 2023-11-20 13:19:30 -05:00
Matt Nadareski
85192e8d3e Get Core building with Framework 4.0 2023-11-20 13:15:06 -05:00
Matt Nadareski
6f784a352e Update Xunit packages 2023-11-20 12:29:33 -05:00
Matt Nadareski
ee707cf1af Update to BinaryObjectScanner 3.0.1 2023-11-20 12:28:52 -05:00
Matt Nadareski
a14c998b3b More C# 12 cleanup in Core 2023-11-16 15:33:37 -05:00
Matt Nadareski
004208df6a Perform some post-move cleanup 2023-11-16 15:20:19 -05:00
Matt Nadareski
2f765146d1 Update RedumpLib and use moved methods 2023-11-16 12:45:47 -05:00
Matt Nadareski
a7d548f7ce Trim PS3 serial and add unrelated notes (fixes #607) 2023-11-16 12:18:07 -05:00
Matt Nadareski
fbdb9875f3 Add C#12 syntax to tests 2023-11-15 16:39:52 -05:00
Matt Nadareski
39a524e3cc Fix reversed ringcode test 2023-11-15 16:32:23 -05:00
Matt Nadareski
9740ca3a7a Suppress deprecation warnings 2023-11-15 16:29:47 -05:00
Matt Nadareski
f8d81972bf Prepare XAML for ancient .NET support 2023-11-15 12:10:54 -05:00
Matt Nadareski
fe9302a553 Perform more ancient .NET support work 2023-11-15 11:58:53 -05:00
Matt Nadareski
c0ed7a7a0e Fix TLS for older .NET 2023-11-15 00:18:52 -05:00
Matt Nadareski
b0b48743ac Support ancient .NET in UI 2023-11-15 00:08:43 -05:00
Matt Nadareski
47e79dab31 Support ancient .NET in Check 2023-11-14 23:53:15 -05:00
Matt Nadareski
90edc42fdf Support C# 12 syntax 2023-11-14 23:40:41 -05:00
Matt Nadareski
45db365705 Support ancient .NET in UI Core 2023-11-14 23:14:33 -05:00
Matt Nadareski
952828dddd Support ancient .NET in Core
This does not include .NET Framework 4.0 due to some issues with libraries.
2023-11-14 23:09:55 -05:00
Matt Nadareski
4a1e953ffd Fix BE flag logic bug in DIC (fixes #606) 2023-11-14 21:27:09 -05:00
Matt Nadareski
25740c2936 Zip manufacturer files for Redumper (fixes #604) 2023-11-14 21:19:52 -05:00
Matt Nadareski
3696257940 Add Bandai Pippin detection (fixes #600) 2023-11-14 21:16:47 -05:00
Matt Nadareski
4d46b7db5c Bump version 2023-11-14 16:39:19 -05:00
Matt Nadareski
21f5f29ac0 Update to .NET 8 (fixes #593) 2023-11-14 16:37:41 -05:00
Matt Nadareski
436e198826 Bump library versions 2023-11-14 16:32:32 -05:00
Matt Nadareski
91eb8c96c3 Update changelog 2023-11-08 21:03:27 -05:00
Matt Nadareski
edf0bcb4fe Update documentation for BOS change 2023-11-08 21:03:09 -05:00
Matt Nadareski
1526714ab9 Remove Unshield reference 2023-11-07 14:32:18 -05:00
Matt Nadareski
767e0bb05b Remove .NET Framework 4.8 from issue report 2023-11-07 12:03:45 -05:00
Matt Nadareski
52e781329c Make releases less verbose 2023-11-07 11:13:12 -05:00
Matt Nadareski
0c1395d3ec Remove lingering script reference 2023-11-07 00:23:55 -05:00
Matt Nadareski
791d9cdb7b Consolidate build script information 2023-11-07 00:22:54 -05:00
Matt Nadareski
7d7dc4ee4e Even more README cleanup 2023-11-07 00:18:54 -05:00
Matt Nadareski
97fd04b13a Remove 4.8 note from build script 2023-11-06 23:38:27 -05:00
Matt Nadareski
899db4c8c0 Remove redundant information 2023-11-06 23:15:27 -05:00
Matt Nadareski
bba204cbfe Update README for Legacy 2023-11-06 23:13:08 -05:00
Matt Nadareski
145660e9f9 Address lingering modern .NET syntax 2023-11-06 23:06:11 -05:00
Matt Nadareski
24de14aea5 Remove .NET Framework 4.8 gated code 2023-11-06 22:07:40 -05:00
Matt Nadareski
d57161e4d1 Remove .NET Framework 4.8 from projects 2023-11-06 21:00:07 -05:00
Matt Nadareski
a066a5234a Remove .NET Framework 4.8 from build 2023-11-06 20:57:03 -05:00
Matt Nadareski
229db5dda2 Bump version 2023-11-06 10:14:16 -05:00
Deterous
4f2a8d354a Update to MMI 3.0.0 (#603) 2023-11-06 07:10:25 -08:00
Matt Nadareski
b886471d72 Update changelog 2023-11-05 19:03:52 -05:00
Deterous
2bab266ae2 Enable browsing for Redumper path (#602) 2023-11-05 15:54:09 -08:00
Matt Nadareski
6c5fd9bac8 Fix default layerbreak output (fixes #601) 2023-11-05 11:39:56 -05:00
Matt Nadareski
08e93d7f13 Update changelog 2023-11-03 23:35:41 -04:00
Deterous
7a510e084b Pull PS3 Firmware Version (#599)
* Attempt to read PS3 firmware version

* Improve PS3 Firmware Version parsing

* Enable PS3 Firmware Version detection for all 3 Programs

* Big endian byte location

* Contents field not Comments field
2023-11-03 20:35:24 -07:00
Matt Nadareski
da46d20ffc Fix PS3 version finding 2023-11-03 22:41:22 -04:00
Matt Nadareski
234c0bfbab Try to get PS3 data from SFB 2023-11-03 15:34:05 -04:00
Matt Nadareski
82d60dbf4a Update changelog 2023-11-03 11:23:28 -04:00
Deterous
6dffb80609 Focus main window after child window closes (#596) 2023-11-03 08:23:11 -07:00
Matt Nadareski
267c0e3184 Update Redumper to build 247 2023-11-01 13:07:36 -04:00
Matt Nadareski
033ccbbe67 Update changelog 2023-10-31 19:59:38 -04:00
Deterous
c31b3f5894 Remove psxt001z Pkg Ref in MPF.Test (#592) 2023-10-31 16:58:43 -07:00
Matt Nadareski
9b1a303fea Bump version 2023-10-31 14:21:46 -04:00
Matt Nadareski
80a0f6da35 Attempt to fix window owner issue 2023-10-30 21:59:30 -04:00
Matt Nadareski
0c30eb7a60 Enable HD-DVD for Redumper in UI 2023-10-30 21:55:03 -04:00
Matt Nadareski
7a8125bb71 Update Redumper to build 246 2023-10-30 21:52:34 -04:00
Matt Nadareski
c4beffd845 Add PIC output for Redumper 2023-10-30 21:43:43 -04:00
Matt Nadareski
f97c112a53 Remove duplicate check 2023-10-30 18:44:08 -04:00
Matt Nadareski
5ef43ab6be Fix build 2023-10-30 18:40:17 -04:00
Matt Nadareski
2c399f99bf Update changelog 2023-10-30 18:38:03 -04:00
Deterous
42e9eb0b96 Fix typo in ProgramSupportsMeida (#591) 2023-10-30 15:36:51 -07:00
Matt Nadareski
e67d65f908 Remove .manufacturer for Bluray 2023-10-30 18:32:58 -04:00
Matt Nadareski
4ec8954b14 Handle a couple of messages 2023-10-30 16:42:15 -04:00
Matt Nadareski
1a6abb039c Compile most regex statements 2023-10-30 16:31:50 -04:00
Matt Nadareski
bb5d1e5ac8 Remove now-obsolete notes 2023-10-30 13:07:29 -04:00
Matt Nadareski
03c4c475eb Add Bluray layerbreak support for Redumper 2023-10-30 12:56:58 -04:00
Matt Nadareski
04d7817d28 Enable Bluray dumping with Redumper 2023-10-30 12:45:19 -04:00
Matt Nadareski
7cd100bc53 Update Redumper to build 244 2023-10-30 12:09:38 -04:00
Matt Nadareski
019924232a Remove and Sort Usings 2023-10-30 01:55:56 -04:00
Matt Nadareski
b5fc9f0275 Ignore empty protection results (fixes #590) 2023-10-30 01:05:15 -04:00
Matt Nadareski
44509b72ed Fix whitespace 2023-10-26 12:37:36 -04:00
Matt Nadareski
d532a63dbd Version-gate new switch statement 2023-10-26 12:36:44 -04:00
Matt Nadareski
227785b079 Remove unncessary summary 2023-10-26 12:33:28 -04:00
Matt Nadareski
0e364be998 Separate out static hashing 2023-10-26 12:16:42 -04:00
Matt Nadareski
7ae1f64ee3 Move all hashing to Hasher 2023-10-26 11:56:38 -04:00
Matt Nadareski
92463a103d Make Hasher more consistent 2023-10-26 11:54:13 -04:00
Matt Nadareski
101cdb7b34 Avoid unncessary allocation in hashing 2023-10-26 11:42:36 -04:00
Matt Nadareski
e924299f85 Add other non-cryptographic hashes 2023-10-26 11:37:41 -04:00
Matt Nadareski
b983f7eb4a Move Hash enum and simplify 2023-10-26 11:30:00 -04:00
Matt Nadareski
33b4be8b24 Bump version 2023-10-26 09:35:25 -04:00
Matt Nadareski
71a4edc8ba Update changelog 2023-10-26 09:18:13 -04:00
Deterous
ceb305eb54 Use relative path internally (#589) 2023-10-26 06:17:17 -07:00
Matt Nadareski
0b0d13dcf3 Use Array.Empty in hasher 2023-10-26 01:25:34 -04:00
Matt Nadareski
9f02368622 Fix two small nullability issues 2023-10-26 01:00:09 -04:00
Matt Nadareski
152010ee14 Update to MMI 3.0.0-preview.4 2023-10-26 00:58:44 -04:00
Matt Nadareski
c6d5f0aea5 Update to BurnOutSharp 2.9.0 2023-10-26 00:57:23 -04:00
Matt Nadareski
8c2ad64bb8 Update XUnit packages 2023-10-25 23:19:44 -04:00
Matt Nadareski
fa54d694b6 Fix CRC32 hashing (fixes #587) 2023-10-25 22:38:30 -04:00
Matt Nadareski
dc35b08624 Attempt to parse out PS5 params.json 2023-10-25 16:56:13 -04:00
Matt Nadareski
4429515ba2 Update package versions (fixes #586) 2023-10-25 16:22:32 -04:00
Matt Nadareski
fdbc7b34e5 Update changelog 2023-10-19 20:58:15 -04:00
Deterous
a1ab442cf0 Set UDF CD threshold at 800MB (#585)
* Set UDF CD threshold at 800MB

* Condense DriveFormat checks
2023-10-19 17:56:58 -07:00
Matt Nadareski
9ed5c297f6 Expose suffix setting (fixes #428) 2023-10-18 22:23:40 -04:00
Matt Nadareski
4ce9b214b0 Wire through filename suffix 2023-10-18 22:19:25 -04:00
Matt Nadareski
7dcdadda75 Add filename suffix setting (nw) 2023-10-18 22:04:15 -04:00
Matt Nadareski
f87a4d9fe2 Fix up DumpEnvironment a bit 2023-10-18 21:54:04 -04:00
Matt Nadareski
e4e5d173f0 Rearrange OptionsWindow to be easier to navigate 2023-10-18 16:35:34 -04:00
Matt Nadareski
115b242d59 Add optional file deletion (fixes #126) 2023-10-18 15:49:57 -04:00
Matt Nadareski
706bf8a431 Add deleteable file lists for Redumper and DIC 2023-10-18 15:41:51 -04:00
Matt Nadareski
87ecf1aa99 Add framework for deleteable files 2023-10-18 15:33:32 -04:00
Matt Nadareski
b5cf274333 Simply theme application 2023-10-18 10:31:58 -04:00
Matt Nadareski
4f4b6879b6 Fix drive letter check (fixes #584) 2023-10-18 10:22:05 -04:00
Matt Nadareski
3b19463913 Create method for applying theme 2023-10-18 10:21:40 -04:00
Matt Nadareski
37386cd182 Fix multiple handler invocation 2023-10-18 03:28:41 -04:00
Matt Nadareski
e04bdad16c Add first-run Options title, fix saving bug 2023-10-18 02:59:41 -04:00
Matt Nadareski
e37f12705d Fix drive letter issue in UI 2023-10-18 02:30:38 -04:00
Matt Nadareski
5c8dc2c23a Show options window on first run (fixes #467) 2023-10-18 02:10:29 -04:00
Matt Nadareski
e9121f3b03 Create skeleton for first-run 2023-10-18 01:59:55 -04:00
Matt Nadareski
d68175db4e Convert Drive to use paths internally 2023-10-18 01:47:42 -04:00
Matt Nadareski
9d8181b0e2 Add path variants for PlayStation info 2023-10-18 01:29:26 -04:00
Matt Nadareski
6d657f268a Split InfoTool into 2 classes 2023-10-18 01:17:12 -04:00
Matt Nadareski
3b3c5f823d Bump version 2023-10-17 20:53:07 -04:00
Matt Nadareski
09fc313492 Support Redumper 231 outputs 2023-10-17 20:51:48 -04:00
Matt Nadareski
316d0f6e54 Clean up issue templates 2023-10-16 00:44:57 -04:00
Matt Nadareski
a0033238bd Get SecuROM data from Redumper (fixes #583) 2023-10-16 00:38:56 -04:00
Matt Nadareski
5b1c6a7f46 Update changlog 2023-10-15 21:51:51 -04:00
fuzzball
8c0dff6552 Correct the condition (#582) 2023-10-15 18:51:23 -07:00
Matt Nadareski
43b230c84a Update Redumper to build 230 2023-10-15 21:42:45 -04:00
Matt Nadareski
f1b657011d Enable parameters checkbox by default (fixes #580) 2023-10-15 15:33:18 -04:00
Matt Nadareski
e4d8ac8e1c Fix Redumper retry count not showing 2023-10-14 23:04:05 -04:00
Matt Nadareski
08f44173dd Update changelog 2023-10-13 10:56:10 -04:00
Deterous
54765c71fd Remove code for getting UH from DIC logs (#577) 2023-10-13 07:55:52 -07:00
Matt Nadareski
01f8b49214 Update changelog 2023-10-13 10:54:57 -04:00
Deterous
e8b0b3efaa Improve check for which program supports which media (#578)
* Move ProgramSupportsMedia to MPF.Core.Utilities.Tools

* Implement ProgramSupportsMedia slightly less naively

* ProgramSupportsMedia only considers MPF-supported types
2023-10-13 07:54:10 -07:00
Matt Nadareski
f637938858 Update changelog 2023-10-12 15:47:13 -04:00
Deterous
ae326c1d2f Disable dumping button when Redumper selected with unsupported media type (#576)
* Disable dumping button when HDDVD + Redumper selected

* Exclude Bluray+Redumper as well

* Move program-supports-mediatype logic to helper function

* Fix for net48

* Fix for net48

* Undo fixes for net48
2023-10-12 12:46:44 -07:00
Matt Nadareski
a4a1e6bf0a Suppress some unnecessary messages 2023-10-12 01:27:54 -04:00
Matt Nadareski
ecee44966e Gate some switch expressions 2023-10-12 01:19:47 -04:00
Matt Nadareski
83437977ba Update changelog 2023-10-11 23:56:00 -04:00
Matt Nadareski
8fcac1d425 Cleanup and gated code 2023-10-11 23:55:50 -04:00
Matt Nadareski
705b5f1049 Fix options loading for Check 2023-10-11 16:37:10 -04:00
Matt Nadareski
367d0c104b Bump version 2023-10-11 13:48:32 -04:00
Matt Nadareski
5d4bed2d9e Remove unnecessary namespacing 2023-10-11 13:41:42 -04:00
Matt Nadareski
63756192d8 Enable path browse button by default 2023-10-11 13:13:36 -04:00
Matt Nadareski
b68ec78184 Version-gate some more newer syntax 2023-10-11 13:07:45 -04:00
Matt Nadareski
c55d5183fb Fix Check always showing Help text 2023-10-11 12:54:40 -04:00
Matt Nadareski
f82e925944 Version-gate some newer syntax 2023-10-11 12:37:31 -04:00
Matt Nadareski
c19f9ea173 Var-ify many instances 2023-10-11 12:29:06 -04:00
Matt Nadareski
76b2dd79ab Handle some suggested changes 2023-10-11 12:26:24 -04:00
Matt Nadareski
c96ff23ad1 Update changelog 2023-10-11 12:14:56 -04:00
Matt Nadareski
cd68b55b93 Enable nullability in MPF.Test 2023-10-11 12:14:42 -04:00
Matt Nadareski
cad14d96f7 Enable nullability in MPF.UI.Core 2023-10-11 11:49:56 -04:00
Matt Nadareski
daaf9f1932 Enable nullability in MPF 2023-10-11 11:16:34 -04:00
Matt Nadareski
cb7502b450 Update changelog 2023-10-11 10:34:00 -04:00
Deterous
ece142bbf1 Place message boxes at center of main window (#574) 2023-10-11 07:33:26 -07:00
Matt Nadareski
611fee4605 Remove all but .NET 6 for AppVeyor packaging (fixes #573) 2023-10-10 23:44:30 -04:00
Matt Nadareski
791e2d0272 Enable nullability in Check 2023-10-10 23:22:21 -04:00
Matt Nadareski
81742a4676 Unify handling of enable/disable events 2023-10-10 22:05:39 -04:00
Matt Nadareski
1ff3f2210c Handle numeric disc titles as issue numbers (fixes #543) 2023-10-10 21:55:51 -04:00
Matt Nadareski
be9e4b91d5 Add generic drive flag for Redumper (fixes #546) 2023-10-10 21:45:17 -04:00
Matt Nadareski
854dcc5f95 Add BE read flag for Redumper (fixes #545) 2023-10-10 21:42:16 -04:00
Matt Nadareski
29b71db33a Allow user-supplied information in Check (fixes #572) 2023-10-10 21:27:36 -04:00
Matt Nadareski
2ee64b222a Fix errant version detection issue 2023-10-10 20:56:53 -04:00
Matt Nadareski
afda54f97b Add pull-all flag for Check (fixes #571) 2023-10-10 20:51:39 -04:00
Matt Nadareski
ad37c573b6 Bump version 2023-10-10 20:25:11 -04:00
Matt Nadareski
b6cb3104ae Update changelog 2023-10-10 16:16:06 -04:00
Deterous
af9b0dc214 Fix media combobox not updating (#568)
* Fix media combobox not updating

* Don't call SetCurrentDiscType rather than clear the variable
2023-10-10 13:15:47 -07:00
Matt Nadareski
b5440032de Recentralize some Check functionality 2023-10-10 15:15:42 -04:00
Matt Nadareski
a8e783235c Fix dumping button not enabling 2023-10-10 02:03:18 -04:00
Matt Nadareski
fc97fe99e3 Fix media type ordering 2023-10-10 01:52:47 -04:00
Matt Nadareski
1c73b1133f Consolidate some constants 2023-10-09 11:49:26 -04:00
Matt Nadareski
908eccaafa Remove LogOutputViewModel 2023-10-09 11:44:06 -04:00
Matt Nadareski
e114d126c5 Move MainViewModel to Core 2023-10-09 11:35:49 -04:00
Matt Nadareski
c07b4e4a28 Remove message boxes from MainViewModel 2023-10-09 11:01:43 -04:00
Matt Nadareski
06491a6611 Detected type and selected type are different 2023-10-09 10:35:44 -04:00
Matt Nadareski
b9a35850ad Fix null reference exception in disc type 2023-10-09 00:42:33 -04:00
Matt Nadareski
17a0c5d083 Remove more Windows from MainViewModel 2023-10-09 00:13:40 -04:00
Matt Nadareski
1b9523c799 Remove some message boxes from MainViewModel 2023-10-09 00:03:02 -04:00
Matt Nadareski
ae80ecefc8 Remove WinForms from MainViewModel 2023-10-08 23:46:24 -04:00
Matt Nadareski
50ae32e3db Use callback for logging, fix Options window 2023-10-08 23:40:56 -04:00
Matt Nadareski
2d43873398 Remove MainWindow from MainViewModel 2023-10-08 23:15:24 -04:00
Matt Nadareski
a3df433fef Small updtes to MainViewModel 2023-10-08 23:06:17 -04:00
Matt Nadareski
35e71d8527 Perform most of MainViewModel changes 2023-10-08 22:49:46 -04:00
Matt Nadareski
c5d07e4be1 Start migrating MainViewModel 2023-10-08 20:52:29 -04:00
Matt Nadareski
0df5093b45 Fix log output 2023-10-08 00:39:31 -04:00
Matt Nadareski
05d920d095 Move decoupled view models 2023-10-07 23:37:01 -04:00
Matt Nadareski
d2f7ac9843 Refine info window bindings further 2023-10-07 23:12:40 -04:00
Matt Nadareski
857a302aad Refine options window bindings further 2023-10-07 22:48:43 -04:00
Matt Nadareski
46de589791 Refine options window bindings 2023-10-07 22:40:51 -04:00
Matt Nadareski
f14961061c Handle Redump password changing better 2023-10-07 21:31:39 -04:00
Matt Nadareski
453fcf5cb1 Use binding for more disc info textboxes 2023-10-07 21:23:14 -04:00
Matt Nadareski
24cdb27cdb Split options window code a bit more 2023-10-07 11:47:24 -04:00
Matt Nadareski
24235c5896 Update changelog 2023-10-07 11:32:19 -04:00
Deterous
c226f8cf58 Clarify build instructions in README (#566)
* Update README.md

* publish-win.bat requirements

* Mention publish-nix.sh and its requirements
2023-10-07 08:31:59 -07:00
Matt Nadareski
b8f6c9f65f Split info window code a bit more 2023-10-07 02:19:04 -04:00
Matt Nadareski
bb42e2db10 Split logging code a bit more 2023-10-07 01:59:28 -04:00
Matt Nadareski
db544fd4b7 Move LogLevel enumeration 2023-10-07 01:34:59 -04:00
Matt Nadareski
370c6bde5a Move element items to Core 2023-10-07 01:30:11 -04:00
Matt Nadareski
d7965ee37f Remove unnecessary include 2023-10-07 01:22:50 -04:00
Matt Nadareski
b2c6e07ed1 Fix failing tests 2023-10-07 01:11:56 -04:00
Matt Nadareski
19cef20ceb Allow nullability for modern .NET 2023-10-07 01:02:21 -04:00
Deterous
058a1aeeaa Remove debug symbols in release builds (#565)
* Remove debugging symbols from appveyor builds

* Remove debugging symbols from release build with nix build script

* Remove debug symbols from release build with win build script

* Explicitly define Debug profile

* Add missing Debug profile flag
2023-10-06 18:35:20 -07:00
Matt Nadareski
d185077925 Fix failing tests 2023-10-06 20:42:34 -04:00
Matt Nadareski
e3948ba91b Consolidate into MPF.Core 2023-10-06 20:39:59 -04:00
Matt Nadareski
4a30f94007 Remove IMAPI2 as a dependency (#563)
* Remove IMAPI2 with no substitution

* Remove framework exceptions

* Slight reorganization of code

* Update README with support information

* Remove msbuild-specific AppVeyor items

* Be trickier when it comes to type from size

* Fix formatting of changelist

* Fix "detected" message

* Be smarter about media type based on system
2023-10-06 17:22:09 -07:00
Matt Nadareski
1ae27bf9e3 Fix missing comma 2023-10-04 20:53:33 -04:00
Matt Nadareski
bc88103471 Add submission info preamble 2023-10-04 20:51:07 -04:00
Matt Nadareski
9ec6fbfe52 Don't reverse the CRC32 output 2023-10-04 16:40:46 -04:00
Matt Nadareski
ebdb8de6b3 Use System.IO.Hashing for CRC32 2023-10-04 16:37:33 -04:00
Matt Nadareski
b8b56f6308 Build number not job number 2023-10-04 16:03:11 -04:00
Matt Nadareski
0726f5a402 Undo accidential temp commit 2023-10-04 15:51:10 -04:00
Matt Nadareski
90da5d1a7e Fix errant space in variable name 2023-10-04 15:50:56 -04:00
Matt Nadareski
bd3c518fa0 Job number not build ID 2023-10-04 15:39:11 -04:00
Matt Nadareski
44272f60bf Try out using version number in AppVeyor 2023-10-04 15:31:43 -04:00
Matt Nadareski
640ce8d854 Add note about git being required 2023-10-04 14:53:55 -04:00
Matt Nadareski
74732058f2 Publish, but don't package, release builds 2023-10-04 14:48:56 -04:00
Matt Nadareski
22e480ccd1 Try alternate archive naming 2023-10-04 14:35:46 -04:00
Matt Nadareski
32a0cb0394 Attempt to replace NRT 2023-10-04 14:26:13 -04:00
Matt Nadareski
88551bc2ed Bump version 2023-10-04 13:14:05 -04:00
Matt Nadareski
039af56f6a Be clearer with protection outputs (fixes #561) 2023-10-04 12:51:29 -04:00
Matt Nadareski
7a428e2add Ensure multisession info is populated (fixes #560) 2023-10-04 12:01:01 -04:00
Matt Nadareski
c8dd85eb72 Update redumper to build 221 (fixes #559) 2023-10-03 17:41:23 -04:00
Matt Nadareski
7c7a19c5a0 Clean up csproj files 2023-10-02 18:18:14 -04:00
Matt Nadareski
9bedd26b24 Fix path tests 2023-10-02 17:50:18 -04:00
Matt Nadareski
09afdf52fb Skip system detection on inactive drives (fixes #558) 2023-10-02 01:17:14 -04:00
Matt Nadareski
c69afe69dd Try out more UI functionality 2023-10-02 01:00:31 -04:00
Matt Nadareski
ab18c7920a Ensure popups are topmost 2023-10-01 23:28:58 -04:00
Matt Nadareski
5af1841d13 Fix XGD4 PIC reading (fixes #557) 2023-09-29 11:12:35 -04:00
Matt Nadareski
8c324e3b8b Fix redumper EDC detection output (fixes #556) 2023-09-28 22:30:51 -04:00
Matt Nadareski
d99099d587 Tweak README again 2023-09-27 15:50:30 -04:00
Matt Nadareski
fa7b46a516 Omit track 0.2 and 00.2 from hash search (fixes #554) 2023-09-27 13:28:40 -04:00
Matt Nadareski
f7c746b536 Move to config.json 2023-09-27 11:29:44 -04:00
Matt Nadareski
b6e109133f Add setting for pulling comment/contents (fixes #552) 2023-09-27 10:45:53 -04:00
Matt Nadareski
0d694c1bde Address some warnings and infos 2023-09-27 00:38:00 -04:00
Matt Nadareski
47f45fa46f Stop compiling Chime finally 2023-09-27 00:05:36 -04:00
Matt Nadareski
481f4b41d1 Fully sync AppVeyor build with script 2023-09-26 23:44:06 -04:00
Matt Nadareski
359ad87faa Add placeholders for release builds 2023-09-26 23:40:17 -04:00
Matt Nadareski
ba47cb7da2 Remove errant character from script 2023-09-26 23:30:53 -04:00
Matt Nadareski
8927c49963 Update to MMI 3.0.0-preview.2 2023-09-26 23:26:48 -04:00
Matt Nadareski
21f9668093 Update Nuget packages 2023-09-26 23:01:05 -04:00
Matt Nadareski
371571d13f Bump version 2023-09-26 22:15:05 -04:00
Matt Nadareski
b231f82c4c Reset debug option to false 2023-09-26 21:41:19 -04:00
Matt Nadareski
1741326253 Force information window to top 2023-09-26 21:39:41 -04:00
Matt Nadareski
1878ef5ad6 Combine build scripts 2023-09-26 21:35:43 -04:00
Matt Nadareski
ae42f5edd7 Fix options not saving on update 2023-09-26 21:27:36 -04:00
Matt Nadareski
4362ed71e0 Handle invalid characters when changing program 2023-09-26 21:09:53 -04:00
Matt Nadareski
f02904ea49 Add release publish scripts 2023-09-26 17:13:15 -04:00
Matt Nadareski
abf4eb9b7c Update AppVeyor to match scripts 2023-09-26 16:43:57 -04:00
Matt Nadareski
1f942977cc Normalize publish scripts 2023-09-26 16:15:43 -04:00
Matt Nadareski
7edadd4739 Disable subdump download in AppVeyor 2023-09-26 14:12:32 -04:00
Matt Nadareski
cb09816c63 Add .NET 7 to build scripts 2023-09-26 14:11:33 -04:00
Matt Nadareski
48f0a826ca Update redumper to build 219 2023-09-26 13:59:09 -04:00
Matt Nadareski
9c10842924 Normalize Redumper CSS output (fixes #553) 2023-09-26 11:26:56 -04:00
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
Matt Nadareski
c4ef14ea3c Bump version to 2.4 2022-10-26 13:08:40 -07:00
Matt Nadareski
03668bd6af Update changelog 2022-10-25 21:11:16 -07:00
fuzzball
2d90a63ca7 Add + to positive offsets in a better way (#422) 2022-10-25 21:07:51 -07:00
Matt Nadareski
11e6e37331 Update changelog 2022-10-24 22:49:09 -07:00
Terry Janas
b229a2d59e Populate internal serial from PS5 disc (#423) 2022-10-24 21:06:57 -07:00
Matt Nadareski
c61af9316f Move to unused Chime class 2022-10-21 23:06:07 -07:00
Matt Nadareski
02e3040e1b Add unused notification method 2022-10-21 22:41:23 -07:00
Matt Nadareski
60bbe29435 Update changelog 2022-10-21 11:42:15 -07:00
Matt Nadareski
3274ea08aa Add PS4 version finding (tjanas) 2022-10-21 11:34:14 -07:00
Terry Janas
97a61dea32 Populate internal serial from PS4 disc (#421)
* Populate internal serial from PS4 disc

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

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

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

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

* Typo fixed the odd w.
2022-03-01 21:43:05 -08:00
Matt Nadareski
b75c2d80bf Force internal drive refresh 2022-03-01 16:41:54 -08:00
Matt Nadareski
aa747ff651 Update to DIC 20220301 2022-03-01 13:27:31 -08:00
Matt Nadareski
bcbf5daf0d Add Xbox One system detection (fixes #368) 2022-02-24 11:22:04 -08:00
Matt Nadareski
aee1c05a45 Update Nuget packages to newest stable 2022-02-24 10:58:36 -08:00
Matt Nadareski
0bb96a8dd3 Specifically include Unsafe Nuget package 2022-02-22 09:08:29 -08:00
Matt Nadareski
1e1d2c7b63 Move path normalization to better place 2022-02-13 22:49:33 -08:00
Matt Nadareski
f12375cddc Assign normalized path to parameters 2022-02-12 23:25:34 -08:00
Matt Nadareski
ed26e6611a Cap X360 directory check to 500MB (fixes #362) 2022-02-11 12:59:37 -08:00
Matt Nadareski
98f77eca07 Add option to limit region and language selections (fixes #361) 2022-02-11 09:55:00 -08:00
Matt Nadareski
1b2b560f8f Fix failing module tests 2022-02-10 13:40:07 -08:00
Matt Nadareski
b49cc0c9bd Fix small DVD layer finding corner case (fixes #363) 2022-02-10 12:02:54 -08:00
Matt Nadareski
4ba58ea861 Make FillFromRedump private again 2022-02-10 11:54:25 -08:00
Matt Nadareski
3f52a20c90 Add /mr default flag options (fixes #360) 2022-02-10 11:52:13 -08:00
Matt Nadareski
580089d06e Fix Redump disc title pulling (fixes #359) 2022-02-10 11:37:36 -08:00
Matt Nadareski
1397ab0fa6 Update to DIC 20211001
Note that there are some code changes that could affect how things like offsets are parsed. Testing is needed.
2022-02-10 11:21:38 -08:00
Matt Nadareski
87400793eb Bump version to 2.3 2022-02-05 13:45:06 -08:00
Matt Nadareski
45f79d95b1 Adjust paths for DIC just before dumping (fixes #358) 2022-02-04 20:46:56 -08:00
Matt Nadareski
c8a4a61028 Normalize PS1/PS2 executable names 2022-02-03 22:28:14 -08:00
Matt Nadareski
44091981b2 Check explicitly for no matches on Redump pull (fixes #357) 2022-02-03 15:43:17 -08:00
Matt Nadareski
d3352643fc Ensure drive is not null for volume labels (fixes #356) 2022-02-02 20:05:15 -08:00
Matt Nadareski
114c7fb38a Make error clearer if something is unsupported in Check 2022-02-02 19:59:53 -08:00
Matt Nadareski
dc7da708dc Add alternate pseudo-tag for Playable Demos 2022-02-02 12:54:08 -08:00
Matt Nadareski
72e56aa1c7 Ensure Games pseudo-tag is multi-line 2022-02-02 12:50:32 -08:00
Matt Nadareski
99ceab07ad Ensure version only pulled if one doesn't exist (fixes #355) 2022-02-01 20:54:26 -08:00
Matt Nadareski
c0f6c072ce Read longer string for Saturn internal serial 2022-02-01 20:51:33 -08:00
Matt Nadareski
e039124f6c Add verification reminders for pulled tags 2022-02-01 13:04:28 -08:00
Matt Nadareski
c96e4a4c7a Add another hand-formatted version of SS tag 2022-01-31 10:50:53 -08:00
Matt Nadareski
622a08acf3 Update changelog 2022-01-31 09:49:07 -08:00
Matt Nadareski
f44b6bf0d0 Slightly rename UK and USA regions for UI 2022-01-31 09:47:42 -08:00
Matt Nadareski
a6d75e15ea Check for $SystemUpdate folder for X360 discs 2022-01-30 20:51:47 -08:00
Matt Nadareski
a02f03c4cb Move internal serial before volume label 2022-01-30 15:57:12 -08:00
Matt Nadareski
d48f5132fb Add another hand-formatted version of SS tag 2022-01-28 23:12:25 -08:00
Matt Nadareski
ed4ac24efa Add Sierra ID to list of pseudo-tags 2022-01-28 09:24:37 -08:00
Matt Nadareski
9f3b8a7c2c Adjust long names for some languages 2022-01-27 21:40:23 -08:00
Matt Nadareski
612d4bb1f5 Fix incorrect region two-letter code 2022-01-27 21:25:58 -08:00
Matt Nadareski
b58a50d246 Disable unnecessary cuesheet parsing 2022-01-27 20:49:32 -08:00
Matt Nadareski
af83811d57 Fix parsing of non-tag tags again 2022-01-27 17:17:42 -08:00
Matt Nadareski
66835fe6ab Fix non-tag tag shortnames 2022-01-27 16:27:40 -08:00
Matt Nadareski
34cc1d33c6 Hook up additional Xbox field to disc info window 2022-01-27 16:04:14 -08:00
Matt Nadareski
a42d14e3b8 Fix incorrect language three-letter code 2022-01-27 15:39:09 -08:00
Matt Nadareski
87aa165edf Add more non-tag support; rearrange info window 2022-01-27 13:35:47 -08:00
Matt Nadareski
d217d62007 Start supporting ordered tags and non-tags 2022-01-27 12:13:17 -08:00
Matt Nadareski
27bcc0d40a Better helper method organization 2022-01-27 10:58:40 -08:00
Matt Nadareski
e1df075cde Make site code formatting helper method 2022-01-27 10:51:27 -08:00
Matt Nadareski
8358692e8d Ensure ordering in output site tags 2022-01-27 10:45:48 -08:00
Matt Nadareski
e1fae01dab Add support for all ISO region codes 2022-01-26 23:29:06 -08:00
Matt Nadareski
d206ab140a Add support for all ISO language codes 2022-01-26 21:38:11 -08:00
Matt Nadareski
9d8722ab17 Try to delete old log archive before writing (fixes #348) 2022-01-26 09:51:37 -08:00
Matt Nadareski
c4fa40c403 Sync with Redump region and language selection (fixes #349) 2022-01-26 09:41:34 -08:00
Matt Nadareski
1d0b06bfbe Use volume label in checks, not formatted version 2022-01-20 13:20:15 -08:00
Matt Nadareski
2cdf473dcb Be smarter about volume labels 2022-01-20 13:15:53 -08:00
Matt Nadareski
1af9e2c2da Conditionally pull region from Redump 2022-01-14 16:30:20 -08:00
Matt Nadareski
9a1815fa1e Differentiate XMID and XeMID 2022-01-14 13:06:11 -08:00
Matt Nadareski
f601961c49 Fix crash on invalid parameters 2022-01-13 10:25:57 -08:00
Matt Nadareski
406acd34c5 Fix Sega CD internal serial reading 2022-01-09 22:38:22 -08:00
Matt Nadareski
31cdcbbc25 Reformat Saturn internal date (fixes #346) 2022-01-09 14:44:55 -08:00
Matt Nadareski
2215ce71c9 Make protection read-only field multiline 2022-01-07 21:41:49 -08:00
Matt Nadareski
1872fbb1c8 Fix incorrect header check 2022-01-07 20:57:47 -08:00
Matt Nadareski
d99f912ac2 Add hidden debug option for "ShowDebugViewMenuItem" (fixes #334) 2022-01-07 13:25:46 -08:00
Matt Nadareski
00a76fb648 Only include booleans if the value is true 2022-01-07 12:49:10 -08:00
Matt Nadareski
187e951a47 Fix IsReadOnly 2022-01-07 09:04:12 -08:00
Matt Nadareski
c0b9b27aae Adjust width ratios for disc info window 2022-01-06 22:19:06 -08:00
Matt Nadareski
b76bb17396 Convert postgap and VCD fields to checkboxes 2022-01-06 21:51:20 -08:00
Matt Nadareski
2efa6d3623 Fix scrolling issues in disc info window 2022-01-06 21:31:46 -08:00
Matt Nadareski
3972ce633d Changed IsEnabled to IsReadOnly 2022-01-06 21:30:47 -08:00
Matt Nadareski
0dc7901393 Further disc info window tweaks 2022-01-06 17:08:45 -08:00
Matt Nadareski
a25ba6eaa5 Add tab setting (fixes #303) 2022-01-06 15:25:25 -08:00
Matt Nadareski
4ea48dfe57 Tweak minimalized layout a bit more 2022-01-06 14:57:38 -08:00
Matt Nadareski
8f7ad8b2ee Unban newly opened consoles 2022-01-06 10:23:07 -08:00
Matt Nadareski
3b9800df07 Add <tab> processing 2022-01-04 21:28:51 -08:00
Matt Nadareski
4c80d3234e Logically group more things in disc info window 2022-01-04 21:19:46 -08:00
Matt Nadareski
9e4af1d66b Remove Enter/Escape registration on disc info window 2022-01-04 11:13:17 -08:00
Matt Nadareski
73555df2ea Omit volume label for "Audio CD" (fixes #343) 2022-01-03 21:26:54 -08:00
Matt Nadareski
3ca78604fd Fix InfoTool tests 2022-01-02 22:01:21 -08:00
Matt Nadareski
0138046923 Add newlines for mutliline special fields 2022-01-02 21:54:49 -08:00
Matt Nadareski
2129184209 Add missing continue statement 2022-01-02 13:58:58 -08:00
Matt Nadareski
dd2116f8a6 Fix newline skipping 2022-01-01 22:24:03 -08:00
Matt Nadareski
814c2d9149 Force scroll visibility, tweak text sizes again 2022-01-01 21:42:05 -08:00
Matt Nadareski
b3f7276044 Skip unnecessary newlines in parsing 2022-01-01 21:36:39 -08:00
Matt Nadareski
ad88aa980b Tweak more Disc Info window formatting 2022-01-01 21:13:35 -08:00
Matt Nadareski
aca55e9203 Handle pulled linebreaks better, again 2022-01-01 20:46:44 -08:00
Matt Nadareski
cc3330bb27 Skip anti-modchip string in some cases 2022-01-01 20:42:52 -08:00
Matt Nadareski
1370909db7 Handle pulled linebreaks better (fixes #342) 2022-01-01 14:18:14 -08:00
Matt Nadareski
08cc0c394b Sanitize filename after check (fixes #341) 2022-01-01 14:16:58 -08:00
Matt Nadareski
cb6692aea3 Add even more safety to clone 2022-01-01 13:58:33 -08:00
Matt Nadareski
b3badb3a55 Add first attempt 2-layer ringcode guide 2021-12-31 14:21:14 -08:00
Matt Nadareski
cbf73901d3 Add model for 2-layer ringcode guide 2021-12-31 13:54:31 -08:00
Matt Nadareski
4822e45d58 Ensure all fields are read-only on read-only tab 2021-12-30 22:21:33 -08:00
Matt Nadareski
1d930d36bf Be smarter about showing update checks 2021-12-30 21:00:01 -08:00
Matt Nadareski
9effcc403d Allow internal serial and volume label to be hidden 2021-12-30 20:56:55 -08:00
Matt Nadareski
05dcc039bd Tweak new disc information fields and tabs 2021-12-30 20:48:32 -08:00
Matt Nadareski
cb08656abc Add horizontal scroll to user input 2021-12-30 20:45:15 -08:00
Matt Nadareski
69b22fc736 Show most read-only fields in new tab (fixes #301) 2021-12-30 17:27:08 -08:00
Matt Nadareski
bf857f6ce7 Fix CSSKey log handling (fixes #333) 2021-12-30 15:47:44 -08:00
Matt Nadareski
7ebf2378b3 Try to handle multi-line fields during parsing 2021-12-30 15:36:58 -08:00
Matt Nadareski
aec25dab37 Clean up default handling of fields 2021-12-30 15:23:17 -08:00
Matt Nadareski
e11969780d Add new tabs for special site information 2021-12-30 14:52:49 -08:00
Matt Nadareski
02c98b1547 Add internal structure for special site codes 2021-12-30 13:00:55 -08:00
Matt Nadareski
c864589478 Start overhauling Redump information pulling, again 2021-12-30 11:09:37 -08:00
Matt Nadareski
bdea1593be Bump version to 2.2 2021-12-30 09:47:44 -08:00
Matt Nadareski
f6b78c07ca Add safety around volume labels 2021-12-29 10:00:28 -08:00
Matt Nadareski
982a217d32 Temporarily disable pulling comments from Redump pages 2021-12-27 13:10:48 -08:00
Matt Nadareski
06588752ad Allow for better matching of multi track discs 2021-12-25 21:37:19 -08:00
Matt Nadareski
9b057d7141 Validate track count when matching Redump 2021-12-25 21:23:24 -08:00
Matt Nadareski
93fb3a85b5 Update changelist 2021-12-25 14:22:17 -08:00
Matt Nadareski
7320f9ba66 Fix ISN string 2021-12-25 14:22:05 -08:00
Matt Nadareski
a94f43ae0c Fix missing ISN usages 2021-12-25 13:54:06 -08:00
Matt Nadareski
e19a3f02e5 Move constants to proper place 2021-12-24 21:53:53 -08:00
Matt Nadareski
375c2c896c Invert condition for volume label 2021-12-24 21:32:45 -08:00
Matt Nadareski
b151e79aed Capture newlines in Redump fields 2021-12-24 21:06:07 -08:00
Matt Nadareski
b45901c133 Fix default value bug 2021-12-24 15:37:10 -08:00
Matt Nadareski
fab54ca0ae Use the Volume Label special site code (fixes #337) 2021-12-24 15:13:06 -08:00
Matt Nadareski
31dd32f19b Add internal support for all Redump site codes (fixes #336) 2021-12-24 14:30:50 -08:00
Matt Nadareski
f51c79d282 Allow default system if skipping system detection enabled 2021-12-21 21:11:18 -08:00
Matt Nadareski
743c943363 Fix saving path settings if set from dialog 2021-12-21 20:49:35 -08:00
Matt Nadareski
91f6e266d1 Fix output dialog issues in Options window 2021-12-21 12:50:19 -08:00
Matt Nadareski
8e843647bf Overhaul XeMID handling (fixes #334) 2021-12-21 10:54:57 -08:00
Matt Nadareski
151402dc50 Detect BD-Video (fixes #332) 2021-12-11 21:00:27 -08:00
Matt Nadareski
057f340c9c Refine the "missing disc" text (fixes #329) 2021-11-30 15:03:14 -08:00
Matt Nadareski
f3f9c63156 Add and overhaul some more tests 2021-11-26 14:07:07 -08:00
Matt Nadareski
7910a79917 Minor code cleanups 2021-11-26 14:06:57 -08:00
Matt Nadareski
cc7acfcd00 Minor usings cleanup 2021-11-25 22:45:13 -08:00
Matt Nadareski
5580e208f5 More test changes/additions, new helper method 2021-11-25 22:38:59 -08:00
Matt Nadareski
56ef06d651 Add enum converter tests, fix other related things 2021-11-25 22:05:35 -08:00
Matt Nadareski
204dfca126 Remove unused converter class 2021-11-25 21:21:27 -08:00
Matt Nadareski
eb337a8aaf Reorganize result tests 2021-11-25 21:06:19 -08:00
TonyLizard
aa6fd781e8 Adding a space to the -No Title Key- string in the DVD Protection section (#328) 2021-11-25 21:03:28 -08:00
Matt Nadareski
32bcfa1d42 Add SubmissionInfo serialization tests 2021-11-24 23:58:08 -08:00
Matt Nadareski
3b2e14d0de Finish filling out first round of Extension tests 2021-11-24 23:36:12 -08:00
Matt Nadareski
88a0ce38f9 Fill in another significant chunk of extension tests 2021-11-24 23:19:58 -08:00
Matt Nadareski
110c8337aa Overhaul cross-enumeration RedumpLib tests 2021-11-24 23:02:13 -08:00
Matt Nadareski
3dc79fea6b Cleanup some old test classes 2021-11-24 22:19:57 -08:00
Matt Nadareski
fb036385f9 Update changelog 2021-11-24 22:11:56 -08:00
Matt Nadareski
428bb2817b Strip ";1" from DVD protection results (fixes #327) 2021-11-24 22:10:10 -08:00
Matt Nadareski
791caff240 Minor cleanups and safety checks 2021-11-24 22:05:10 -08:00
Matt Nadareski
d92015d071 Add sanitization for StarForce 2021-11-19 11:03:24 -08:00
Matt Nadareski
de98bd9e0b Update to DIC 20211101 2021-11-19 10:33:13 -08:00
Matt Nadareski
67f5a7794a Fix protection sanitization; add tests 2021-11-19 10:09:34 -08:00
Matt Nadareski
fbef94f634 Only exclude Audio CDs from offset writing 2021-11-12 11:06:27 -08:00
Matt Nadareski
0e8363f06e Ensure parameters box is safer during options save 2021-11-08 20:45:37 -08:00
Matt Nadareski
a1bafe0426 Update changelist 2021-11-08 10:18:15 -08:00
Matt Nadareski
c51461592c Start adding regression tests for DIC 2021-11-05 14:44:58 -07:00
Matt Nadareski
ef5f996ec4 Remove offset from DIC output (fixes #326) 2021-11-05 11:25:45 -07:00
Matt Nadareski
aa5998a52e Be consistent with LBA values (fixes #325) 2021-11-04 22:20:52 -07:00
Matt Nadareski
37207c1cb0 Remove lower bound checking on LBA values 2021-11-04 21:59:26 -07:00
Matt Nadareski
d1813fdbc7 Make anti-modchip scans safer 2021-11-04 15:23:40 -07:00
Matt Nadareski
faf9d1d6f8 Add retry to Redump calls (fixes #309) 2021-11-02 11:35:31 -07:00
Matt Nadareski
0360df0604 Wrap directory checks in case of corruption 2021-11-02 10:54:08 -07:00
Matt Nadareski
fe6487eeb4 Fix enum converters (fixes #306) 2021-11-01 12:02:07 -07:00
Matt Nadareski
b9eedd1cf6 Add submission JSON option to check 2021-11-01 10:43:55 -07:00
Shiz
26281b8f0b Fix various arcade system metadata issues (#324)
* redumplib: correct media type for Sega Nu

* redumplib: merge Namco System 246 and System 256

* redumplib: drop Namco System 357 / 369

This was not a disc-based system, none of its released forms
have a disc reader.

* redumplib: add SEGA ALLS
2021-10-26 10:30:14 -07:00
sadikyo
f0508fdf93 Minor spelling fix. (#321)
Co-authored-by: sadik <sadik@TONY-HOME>
2021-10-22 16:51:40 -07:00
Matt Nadareski
166d69c7ec Fix NRE in InfoTool 2021-10-03 13:53:23 -07:00
Matt Nadareski
fabf08ba55 HTTP encode password for Redump login, again (fixes #314) 2021-10-01 09:01:41 -07:00
Matt Nadareski
0e5d8af0e9 Update to DIC 20211101 2021-09-30 22:09:09 -07:00
Matt Nadareski
3e1263ebff Update to Aaru v5.3.0 LTS 2021-09-30 20:59:13 -07:00
Matt Nadareski
c0ed830241 These should live in Drive 2021-09-29 16:37:28 -07:00
Matt Nadareski
4209158807 Extract anything that can be static 2021-09-29 16:16:54 -07:00
Matt Nadareski
6d79ebf449 Post-separation cleanup 2021-09-29 15:18:35 -07:00
Matt Nadareski
88ab691f07 Simplify remaining structure in MPF.Library 2021-09-29 15:05:17 -07:00
Matt Nadareski
37b1dc574c Separate out modules into own library; fix csproj's 2021-09-29 14:56:15 -07:00
Matt Nadareski
ad56e249ed Small cleanups for dead code 2021-09-29 12:24:03 -07:00
Matt Nadareski
f28cfe3d69 Segment out protection checks a bit more 2021-09-29 12:17:34 -07:00
Matt Nadareski
9643442281 Create MPF.Core and move a lot to there 2021-09-29 11:48:37 -07:00
Matt Nadareski
dd44ec7ac1 Clean up usings 2021-09-29 10:28:15 -07:00
Matt Nadareski
801c1126cf Remove now-unnecessary excludes from csproj 2021-09-29 09:40:01 -07:00
Matt Nadareski
4fd72efc46 Move CICMMetadata to top-level 2021-09-29 09:36:13 -07:00
Matt Nadareski
9ec045cb21 Move cuesheet code to separate DLL 2021-09-29 09:08:10 -07:00
Matt Nadareski
52801ee94c Null-safeguard RedumpLib conversions 2021-09-28 11:58:11 -07:00
Matt Nadareski
2077d53964 Don't print entire exception to popup 2021-09-28 11:46:37 -07:00
Matt Nadareski
14046e9217 Catch exception on update check 2021-09-28 11:29:23 -07:00
Matt Nadareski
2766debffc Fix double include 2021-09-27 22:12:32 -07:00
Matt Nadareski
4f4688899c Attempt to use Aaru.Devices and fail 2021-09-27 21:38:35 -07:00
Matt Nadareski
8151db9696 Remove non-4.8 from AppVeyor artifacts 2021-09-27 20:59:56 -07:00
Matt Nadareski
227c61b588 Add .NET 5.0 as build target 2021-09-27 20:59:37 -07:00
Matt Nadareski
61224fba7b Update to Aaru v5.3.0-rc2 2021-09-27 14:27:56 -07:00
Matt Nadareski
1dbf5516d8 Forgot ToLowerInvariant (fixes #304) 2021-09-22 14:38:24 -07:00
Matt Nadareski
10fe6ac32a Write out label-side data, if it exists (fixes #311) 2021-09-22 13:25:16 -07:00
Matt Nadareski
e39ee66726 Fix BOS-related things 2021-09-22 12:46:43 -07:00
Matt Nadareski
fff2db75d0 Add on-startup "check for updates" option (fixes #302) 2021-09-22 11:30:56 -07:00
Matt Nadareski
3a14db53c6 Update packages 2021-09-22 11:19:01 -07:00
Matt Nadareski
8b76b82e0f Update default Aaru dumping parameters 2021-09-17 22:23:11 -07:00
Matt Nadareski
e110c55e1a Create proper section for previous change 2021-09-16 14:08:07 -07:00
Matt Nadareski
85c30fbad3 Try to use another method of success/failure in AppVeyor 2021-09-16 14:07:19 -07:00
Matt Nadareski
14e5c74c65 Try to fix AppVeyor config 2021-09-16 13:37:24 -07:00
Matt Nadareski
b180e31b56 Update to Aaru v5.3.0-rc1 2021-09-16 12:02:19 -07:00
Matt Nadareski
d172a45891 Start adding special filtering to BOS outputs 2021-09-13 15:32:45 -07:00
Matt Nadareski
e74289116e Change how aux hashes are displayed (fixes #308) 2021-09-13 12:13:12 -07:00
Matt Nadareski
68b932bad0 Fix extension and conversion methods 2021-08-18 23:17:47 -07:00
Matt Nadareski
c4793e849c Move a bit more 2021-08-18 22:48:31 -07:00
Matt Nadareski
1c525a6d2e Update library, move more things there 2021-08-18 22:13:38 -07:00
Matt Nadareski
c8610ee16a Convert to using separate Redump library code 2021-08-18 15:32:45 -07:00
Matt Nadareski
0fb3bc1106 Clarify CUE or ISO in Check help text 2021-08-09 22:55:17 -07:00
Matt Nadareski
3cf133d750 Add HD-DVD-Video Redump support 2021-08-08 21:53:27 -07:00
Matt Nadareski
f0be6b0bc9 MVVM Overhaul (#300)
* Start migrating to MVVM

* Finish gutting LogOutput

* Remove now-useless regions

* Incremental initialization of the UI

* Add options directly to App-level

* Move options globally

* Accept -> OK

* Add future work notes to App.xaml.cs

* Add back cancel to DiscInformationWindow

* Enable/Disable instead of Add/Remove

* Add disc eject reminder option

* Add PS3VOLUME check
2021-08-04 14:17:53 -07:00
Matt Nadareski
e0ccb8b10d Add drive size check (fixes #299) 2021-08-02 20:40:37 -07:00
Matt Nadareski
940afae1fd Update PPC for Redump values 2021-08-01 13:24:39 -07:00
Matt Nadareski
94ba339ef2 Add Pocket PC support (fixes #298) 2021-07-30 13:04:21 -07:00
Matt Nadareski
bfead10d87 Use MCN check for Sega header, not CVD (fixes #297) 2021-07-27 22:06:26 -07:00
Matt Nadareski
97f9a73fc1 Fix Saturn header finding 2021-07-24 13:40:38 -07:00
Matt Nadareski
7f4921a47c Bump version to 2.1 2021-07-22 21:02:32 -07:00
Matt Nadareski
07359d2a63 Tweak Linq statement 2021-07-22 20:59:08 -07:00
Matt Nadareski
e5ba670c04 Update to BurnOutSharp 1.7.0 2021-07-22 11:19:58 -07:00
Matt Nadareski
3096c369d3 Update changelog 2021-07-19 22:47:25 -07:00
Matt Nadareski
333368675a Add DMI/PFI/SS to zipfile (fixes #296) 2021-07-19 13:38:16 -07:00
Wilson
d78d744ae2 Fixed log window background turning white after some "time". (#295)
* Fixed launching MPF in Windows 7.

* Potential fix for log window background 'random' color change.

* Updated the color to use hex digits.
2021-07-17 20:35:38 -07:00
Wilson
a8efe056c1 Fixed launching MPF in Windows 7. (#292) 2021-07-12 16:46:30 -07:00
Matt Nadareski
9b2475a854 Add BD to list of supported types for PC (fixes #291) 2021-07-05 22:31:58 -07:00
Matt Nadareski
33e2b27e6d Add new header for mainInfo 2021-07-05 13:26:23 -07:00
Matt Nadareski
91fe9d9bec Support /mr value parameter 2021-07-02 22:57:07 -07:00
Matt Nadareski
b1b9b3134f Update to DIC 20210701 2021-07-01 14:19:25 -07:00
Matt Nadareski
ca5386b529 Update changelog 2021-06-20 11:23:30 -07:00
Matt Nadareski
347beca874 Handle Redump 503s / Timeouts (fixes #289) 2021-06-20 11:21:43 -07:00
Matt Nadareski
27ca4fd1af Check for updates logs as well 2021-06-19 20:47:41 -07:00
Matt Nadareski
6d57a05f8c Encode params for login 2021-06-19 16:12:42 -07:00
Matt Nadareski
4d81449cfa Minor updates 2021-06-19 15:51:18 -07:00
Matt Nadareski
76fcb3ae10 Separate "Include Artifacts" into a separate setting (fixes #287) 2021-06-19 15:41:46 -07:00
Matt Nadareski
b913f5f9ae Update Xbox disc detection (fixes #288) 2021-06-19 15:25:43 -07:00
Matt Nadareski
68fa1eecfb Replace dumping program output processing 2021-06-15 10:24:19 -07:00
Matt Nadareski
dc84f17ac5 Set the matcher for carriage returns 2021-06-15 10:23:14 -07:00
Matt Nadareski
c91137130b Fix newline processing, add changelog 2021-06-15 10:20:13 -07:00
Matt Nadareski
972b07551f Add Logging utility class 2021-06-15 09:55:14 -07:00
Matt Nadareski
61562fe995 Handle carriage returns even without fancy formatting 2021-06-15 09:35:11 -07:00
Matt Nadareski
01b6c3cc98 Minor formatting change in ProcessingQueue 2021-06-14 22:37:47 -07:00
Matt Nadareski
b461dc76b9 Fix incorrect normalization for Aaru 2021-06-14 22:15:36 -07:00
Matt Nadareski
03eeed4113 Update nuget packages 2021-06-03 22:39:50 -07:00
Matt Nadareski
07615cb336 Update nuget packages 2021-06-03 22:19:03 -07:00
Matt Nadareski
9a9a977cc4 Simplify flag validation
Instead of trying to determine if something is a flag, just check if it's one of the supported flags. This should reduce the amount of potential issues that come along with making assumptions about what a parameter could look like.
2021-06-03 22:13:50 -07:00
Matt Nadareski
ab5869331b Add throwability to cuesheet code 2021-06-03 21:58:52 -07:00
Matt Nadareski
1c9ebfd703 TODO additions 2021-06-03 21:20:45 -07:00
Matt Nadareski
a9bc3587a6 Update to DIC 20210601 2021-06-03 21:19:33 -07:00
BadAd84
b1af2b6061 Fix for high cpu usage, issue with loop. (#286) 2021-05-30 12:58:08 -07:00
Matt Nadareski
2fccd53098 Ensure CSSKey file included in log zipping 2021-05-27 12:07:21 -07:00
Matt Nadareski
a7ad3e41d9 Change offset value for HVN discs (fixes #285) 2021-05-27 11:49:24 -07:00
Matt Nadareski
1fc6476f71 Be smarter about checking for zipped logs 2021-05-27 11:46:47 -07:00
Matt Nadareski
53e5a1b1b1 Check for the zipped logs for dealing with overwrites (fixes #277) 2021-05-27 11:20:48 -07:00
Matt Nadareski
119e9dc4cc Always check for all DIC log files, just in case (fixes #281) 2021-05-27 11:00:56 -07:00
Matt Nadareski
9263769906 Fix negative offsets (fixes #282) 2021-05-27 10:41:25 -07:00
Matt Nadareski
ebaf9539b2 Allow users to customize protection scanning 2021-05-06 21:47:57 -07:00
Matt Nadareski
818fb5ab34 Add right click menu with theming 2021-04-28 11:07:54 -07:00
Matt Nadareski
2f514082a9 Replace WindowChrome with dragable titles instead 2021-04-28 10:08:18 -07:00
Matt Nadareski
5c5379e972 Update ScrollViewer style 2021-04-27 11:07:07 -07:00
Matt Nadareski
c39894d9fc Update ProgressBar style 2021-04-27 10:51:32 -07:00
Matt Nadareski
2c6cf0e736 Custom UI elements v2 2021-04-26 23:08:21 -07:00
Matt Nadareski
7e96bbee27 Custom UI elements v1
This enables full-window dark mode to be a reality. The problem is that my design skills are very low
2021-04-26 22:32:05 -07:00
Matt Nadareski
f7019546d1 Add experimental dark mode (fixes #272) 2021-04-26 12:07:21 -07:00
Matt Nadareski
086f54abf0 Topsy-turvy (fixes #274) 2021-04-25 14:08:00 -07:00
Matt Nadareski
143a4149bd Parametrics (#276)
* Begin reducing complexity of parameters classes

* Make the method virtual, not the field

* Consolidate helper methods

* Fix "empty" commands; Fix DD generation

* Handle all sorts of numerical values

* Rip out remaining unneeded enum
2021-04-25 14:03:55 -07:00
Whovian9369
20aed68d58 Fix README formatting in "Notable Testers" Section (#275) 2021-04-25 10:19:15 -07:00
Matt Nadareski
7a2061a36e Bump version for first MPF rebranded stable 2021-04-23 10:30:44 -07:00
Matt Nadareski
191c7b080e Add offset for all HVN discs 2021-04-18 10:42:09 -07:00
Matt Nadareski
aab0813688 Remove HVN-specific flags for now
These flags may cause dumping issues for some, if not a majority, of VideoNow discs
2021-04-16 21:18:48 -07:00
Matt Nadareski
9ad564772c Add content alignment to user input control 2021-04-15 12:59:44 -07:00
Matt Nadareski
20bb34c3c7 Handle Enter/Esc for relevant buttons 2021-04-14 22:26:23 -07:00
Matt Nadareski
b15ddf390b Update to BurnOutSharp 1.6.1 2021-04-14 09:27:42 -07:00
Matt Nadareski
18d1562ad3 Update changelog 2021-04-13 09:31:23 -07:00
Matt Nadareski
d6f2ecbd25 Add version from Git commit (fixes #271)
This also ensures that the new version check still works as well, so the commit hash addition doesn't screw anything up.
2021-04-13 09:30:26 -07:00
Matt Nadareski
3620686afa Change window title 2021-04-13 09:28:26 -07:00
Matt Nadareski
1524224bc3 Fix cursor positioning issue 2021-04-12 22:04:54 -07:00
Matt Nadareski
35200d53d6 Update changelog 2021-04-12 14:59:22 -07:00
Matt Nadareski
4b7d4e05e7 Streamline output path textbox handling
Addresses issue of cursor moving during typing
Addresses issue of extraneous spaces appearing
2021-04-12 13:25:12 -07:00
Matt Nadareski
7e73f69433 Fix Optuons XAML 2021-04-12 08:42:22 -07:00
Matt Nadareski
9e1a972df5 Remove needless UIOptions intermediate class 2021-04-11 22:30:23 -07:00
Matt Nadareski
1edac34c1a Handle dialog return properly
Also fixes an issue where dumpable media with no ring would show a ringcode tab
2021-04-11 09:31:22 -07:00
Matt Nadareski
9adc588c9c Reintroduce options cancel button 2021-04-11 00:09:13 -07:00
Matt Nadareski
71bfdcba8f Proofreading 2021-04-09 13:52:03 -07:00
Matt Nadareski
9854c9970a Add descriptions to new options, change defaults
The default changes are:
- Setting `CompressLogFiles` to `true`
- Setting `ToolsInSeparateWindow` to `true`
- Setting `EnableLogFormatting` to `false`
- Setting `EnableProgressProcessing` to `false`

The last two don't really matter if `ToolsInSeparateWindow` is `true` since no UI-derived logs will ever trigger them, so they're being disabled for now.
2021-04-09 13:31:23 -07:00
Matt Nadareski
0c68164d6d Add option for log formatting 2021-04-09 12:03:30 -07:00
Matt Nadareski
a413357b57 Help users be more patient when starting a dump 2021-04-09 11:47:12 -07:00
Matt Nadareski
5e283c9e80 Add option to enable progress processing
This is one of the larger pain-points for redirecting the output as it involves a regex. Make it optional so people with weaker systems have a better chance.
2021-04-09 11:16:50 -07:00
Matt Nadareski
500eb66e30 Fix the text... got it inverted 2021-04-09 10:59:00 -07:00
Matt Nadareski
411e5245a5 Update tooltip to help a bit more 2021-04-09 10:57:48 -07:00
Matt Nadareski
d270501c2d Update to BurnOutSharp 1.6.0 2021-04-09 10:31:27 -07:00
Matt Nadareski
f7f924593f Add Audio CD detection
On Windows 10, at least, when a purely Audio CD is put in, it gets the volume label of "Audio CD", conveniently. We can use this to at least attempt to match, even if this could be a false positive for some CD-i or things like Jag CD
2021-04-07 10:04:59 -07:00
Matt Nadareski
5126a66eb9 Color code the text 2021-04-06 13:35:47 -07:00
Matt Nadareski
7fddef238d Update changelog 2021-04-06 11:31:27 -07:00
Matt Nadareski
ace673f90d Integrate ring code guide into submission window
Adds a new button to show the ring code guide for easier reference
2021-04-06 11:05:03 -07:00
Matt Nadareski
e063df63df Merge pull request #269 from SabreTools/logjam
Add option to compress log files after dumping
2021-04-02 22:47:26 -07:00
Matt Nadareski
22b15710d0 Take output of compression into account for log 2021-04-02 22:34:46 -07:00
Matt Nadareski
87199c1b0d Update changelog 2021-04-02 22:28:01 -07:00
Matt Nadareski
7e5952bbb8 Add log file lsits for CleanRip and UIC 2021-04-02 22:23:15 -07:00
Matt Nadareski
1a52a3a205 Add Check parameter for compression 2021-04-02 22:20:15 -07:00
Matt Nadareski
742db4c854 Change default archive name 2021-04-02 22:12:32 -07:00
Matt Nadareski
4fd51dbe45 Default to false, for now 2021-04-02 21:57:00 -07:00
Matt Nadareski
096a8a6a06 Add Aaru log file paths, remove TODO in DIC 2021-04-02 21:56:14 -07:00
Matt Nadareski
ad5cd5b8f9 Only encode artifacts if we're outputting JSON 2021-04-02 21:53:41 -07:00
Matt Nadareski
3230d59f6a Fix options window 2021-04-02 21:49:57 -07:00
Matt Nadareski
96fa8d8cef Add DiscImageCreator log paths 2021-04-02 21:36:14 -07:00
Matt Nadareski
01bf3c9efb Add log file compression 2021-04-02 21:21:50 -07:00
Matt Nadareski
da0bf64e94 Add log compression option 2021-04-02 20:55:20 -07:00
Matt Nadareski
d39cf5d13f Make JSON optional (fixes #268) 2021-04-02 18:57:25 -07:00
Matt Nadareski
7c7e78fd4e Errors during dumping should be more verbose
This should *hopefully* be a temporary change in order to determine what is failing after many dumps in a row.
2021-04-02 14:23:02 -07:00
Matt Nadareski
d2c504898a Empty the queue at least 2021-04-01 19:08:39 -07:00
Matt Nadareski
58028b4f18 Reset queue entirely to prevent issues 2021-04-01 15:11:30 -07:00
Matt Nadareski
54e835687b Only rely on cancellation not disposal 2021-04-01 15:10:43 -07:00
Matt Nadareski
10f2318541 Update to DIC 20210401 2021-03-31 22:11:02 -07:00
Matt Nadareski
eb4e95e5db Fix ProcessingQueue disposal (fixes #267) 2021-03-31 21:26:55 -07:00
Matt Nadareski
203418eebc CD-i is a fake audio format 2021-03-31 20:36:06 -07:00
Matt Nadareski
136c6c8fbf Remove two useless TODOs 2021-03-29 13:11:45 -07:00
Matt Nadareski
2c216d6a58 Use BurnOutSharp for PSX scanning 2021-03-29 13:10:49 -07:00
Matt Nadareski
f7ec19cc5d Clean up a couple more TODOs 2021-03-29 13:05:46 -07:00
Matt Nadareski
e8c4a97158 Fix flag value 2021-03-29 12:14:06 -07:00
Matt Nadareski
5e5fc4c812 Factor in size to media type output 2021-03-29 12:12:20 -07:00
Matt Nadareski
5e0d99ff5d Remove unnecessary TODOs 2021-03-29 12:12:05 -07:00
Matt Nadareski
e88e67c927 Fix broken test 2021-03-29 11:56:28 -07:00
Matt Nadareski
d963dbbd5f Move some classes around and cleanup 2021-03-29 11:55:29 -07:00
Matt Nadareski
11ce78fca2 Fix and use ProcessingQueue 2021-03-29 10:50:43 -07:00
Matt Nadareski
fd4022ba84 Add ProcessingQueue (nw) 2021-03-29 10:23:34 -07:00
Matt Nadareski
f91aecad8e Add queueing mechanism to DumpEnvironment 2021-03-29 09:35:39 -07:00
Matt Nadareski
709b7d357f It's a secret... 2021-03-28 22:29:22 -07:00
Matt Nadareski
59980fa160 Clear button should clear progress bar too 2021-03-28 22:25:03 -07:00
Matt Nadareski
180caf83cc Forgot to update param comment and changelog 2021-03-28 22:22:27 -07:00
Matt Nadareski
7047ee832a Simplify null checks 2021-03-28 22:15:33 -07:00
Matt Nadareski
4b5532e1b0 Log levels, struct methods, simpler constructor 2021-03-28 22:10:17 -07:00
Matt Nadareski
137155d3dc Forgot summary comment 2021-03-28 21:34:27 -07:00
Matt Nadareski
793dd8289a Safer queue, use LogLine struct again
Once ConcurrentQueue was used, there were issues with using Run objects in the queue. Even with Dispatcher, there were access issues. So back to the minimal struct.
2021-03-28 21:30:42 -07:00
Matt Nadareski
bcf298e40b Change name of log queue 2021-03-28 20:38:59 -07:00
Matt Nadareski
589aef1e21 Use Run instead of custom wrapper
This simplifies a whole bunch of logic that had to convert from one type to another.
2021-03-28 20:38:15 -07:00
Matt Nadareski
9ab0b7ab3b Clean up var and methods in logger 2021-03-28 20:28:59 -07:00
Matt Nadareski
4c5ea13aac Add log queuing 2021-03-28 15:38:51 -07:00
Matt Nadareski
0cf48bbdaa Cache last line object instead 2021-03-28 13:58:11 -07:00
Matt Nadareski
8fc9cc18a3 Simplify dispatcher calls 2021-03-28 00:21:48 -07:00
Matt Nadareski
b1e156eed6 Smarter about matchers 2021-03-27 22:23:29 -07:00
Matt Nadareski
a8b3a837ef Simplify matchers, fix percentages 2021-03-27 13:21:42 -07:00
Matt Nadareski
3f3a1c9a44 Support new Redump languages and regions 2021-03-27 10:28:45 -07:00
Matt Nadareski
cf4d2f5055 Fix typo in progress 2021-03-25 22:56:17 -07:00
Matt Nadareski
2d19d51bc7 Add matcher for anti-mod string search 2021-03-25 18:48:19 -07:00
Matt Nadareski
4842bdd38b Have the log catch exceptions during matching 2021-03-24 20:20:24 -07:00
Matt Nadareski
7cc4f6d6a7 Only list protections found, not the files
This may be gated behind a flag in the future as having the full list of files and their associated protections can be useful.
2021-03-21 15:38:52 -07:00
Matt Nadareski
b284ffd99d Fix build 2021-03-20 12:47:51 -07:00
Matt Nadareski
81d0cc2cc2 Replace . with _ for DIC output paths (fixes #264) 2021-03-19 21:14:33 -07:00
Matt Nadareski
70f368d6e9 Make copy scan errors separate (fixes #263) 2021-03-19 17:44:42 -07:00
Matt Nadareski
bfbae5cf5c Fix default output directory in UI 2021-03-15 22:06:31 -07:00
Matt Nadareski
7cc231f5fd Fix incorrectly disposing stream 2021-03-15 22:06:14 -07:00
Matt Nadareski
eda34b3477 Fix text alignment 2021-03-14 19:37:06 -07:00
Matt Nadareski
bfb67ec0c9 Make UI more consistent for dump button 2021-03-12 15:29:26 -08:00
Matt Nadareski
275fd0da48 Rearrange DumpEnvironment code 2021-03-12 15:14:40 -08:00
Matt Nadareski
1ad5c25ed0 Remove .NET Framework 4.7.2, update copyright dates 2021-03-12 15:05:33 -08:00
Matt Nadareski
512f7ae016 Clean up parameters and related
This makes some former virtual methods into properties, cleans up the location of some logic, and makes the BaseParameters class a little neater
2021-03-12 14:59:04 -08:00
Matt Nadareski
3a00efc7fd Make dumping path a little easier to follow
Resets the progress bar a bit earlier
Disables/enables UI elements during dumping to avoid weird interactions
Handle more things that shouldn't be verbose logs
2021-03-11 22:14:19 -08:00
Matt Nadareski
3068c74ad7 DVD/BD have more label info possible 2021-03-11 18:50:06 -08:00
Matt Nadareski
9bdead5616 Various bits of maintenence
Added and fixed some of the CD DIC Matchers
Added caching for last line of text and last matcher used
Made replacing the last line more efficient
Fixed default system handling
Fixed potential issue with sector reading
2021-03-11 13:02:48 -08:00
Matt Nadareski
0ed5feb96b Support a new Matcher for DIC DVD 2021-03-10 19:11:31 -08:00
Matt Nadareski
ec80ef2ede Sort matchers and add a couple more 2021-03-10 16:08:08 -08:00
Matt Nadareski
617591cc26 Fix redump username binding 2021-03-10 15:25:42 -08:00
Matt Nadareski
c855aa5cef Make outputting to separate window a setting 2021-03-10 15:13:11 -08:00
Matt Nadareski
e2a27479f5 Display dump progress in log window (fixes #131)
This builds heavily upon the work that Jack put into capturing and processing DIC outputs. For now, there are only matchers for DIC outputs. All other programs will still direct to the log window, but may not be processed as neatly
2021-03-10 14:51:13 -08:00
Matt Nadareski
c8cfe76aa3 Better text bindings 2021-03-09 16:38:15 -08:00
Matt Nadareski
ecb0234258 Make inner and outer layers more apparent
Also makes label and data side data make more sense
2021-03-09 14:06:18 -08:00
Matt Nadareski
24ce4bcc51 Make layers more clearly named 2021-03-09 13:32:10 -08:00
Matt Nadareski
162af3de31 Hook up default system in options (fixes #246) 2021-03-09 12:01:45 -08:00
Matt Nadareski
7c0af96dc9 Make XGD extension 2021-03-09 09:55:08 -08:00
Matt Nadareski
026f999b46 VideoNow discs are audio only (fixes #261) 2021-03-09 09:41:39 -08:00
Matt Nadareski
bbbc3e98a0 One more pass on options 2021-03-08 22:19:22 -08:00
Matt Nadareski
f78ad4e7b6 Fix incorrect eject value for Check 2021-03-08 20:48:51 -08:00
Matt Nadareski
d756ae2163 Fix poorly designed tests 2021-03-08 16:19:53 -08:00
Matt Nadareski
701738ce25 Forgot about nulls 2021-03-08 15:47:29 -08:00
Matt Nadareski
620bdc9132 Reduce logic weirdness, possibly fix PIC layerbreak 2021-03-08 15:40:30 -08:00
Matt Nadareski
71f474c26c CMI is its own option 2021-03-08 15:20:13 -08:00
Matt Nadareski
c48137a5c1 Update changelog (fixes #259) 2021-03-08 15:11:11 -08:00
Matt Nadareski
e248368484 Add force dumping option for Aaru 2021-03-08 15:10:09 -08:00
Matt Nadareski
7225b8c32d Make eject after dump an option 2021-03-08 15:06:40 -08:00
Matt Nadareski
20e475e130 Separate out Aaru and DIC options 2021-03-08 14:56:26 -08:00
Matt Nadareski
88dc005592 Use aaruf instead of aif 2021-03-08 14:51:44 -08:00
Matt Nadareski
40792ff0eb Update changelist 2021-03-07 14:20:01 -08:00
Matt Nadareski
6ae59e91de Shuffle things for Redump obviousness 2021-03-07 14:17:25 -08:00
Matt Nadareski
5c7aa5f7f5 Hook up language selection for PS2 2021-03-07 13:45:24 -08:00
Matt Nadareski
aaa1ed674f Fix path population on drive scan 2021-03-07 10:19:59 -08:00
Matt Nadareski
42def3bbe5 Fix dropdown population 2021-03-07 10:05:54 -08:00
Matt Nadareski
9cdab52556 Fix system selection regression 2021-03-06 21:49:33 -08:00
Matt Nadareski
5c104ebf3d Fix tests 2021-03-06 21:47:02 -08:00
Matt Nadareski
7ab72e542c Fix auto extension filling 2021-03-05 17:18:08 -08:00
Matt Nadareski
3a8420cd2f Fix broken dumping 2021-03-05 17:14:15 -08:00
Matt Nadareski
916aa9fd70 Move things around like Shadow 2021-03-02 11:34:48 -08:00
Matt Nadareski
8809df10b6 Use Shadow's disable code for categories 2021-03-02 09:55:50 -08:00
Matt Nadareski
34d65a9d70 KnownSystem cleanup based on Shadow's stuff 2021-03-02 09:48:36 -08:00
Matt Nadareski
2830d7c8a2 Use Shadow's combined element model 2021-03-02 09:08:56 -08:00
Matt Nadareski
c702c8b0ac Update to DIC 20210301 2021-03-02 09:05:44 -08:00
Matt Nadareski
e1df9a9754 Use Shadow's description converter implementation 2021-03-01 23:02:45 -08:00
Matt Nadareski
03fe5ce8a5 Take Shadow's advice about lists 2021-03-01 22:58:55 -08:00
Matt Nadareski
432a9dda16 Clean up options binding 2021-03-01 21:51:40 -08:00
Matt Nadareski
1dfc0f731e UniformGrid 2021-03-01 21:09:26 -08:00
Matt Nadareski
af515e0adf Update attributions 2021-02-28 13:19:55 -08:00
Matt Nadareski
2222b57143 Fix spelling 2021-02-28 11:37:53 -08:00
Matt Nadareski
d8cfa2e6a4 Quick touch-up on disc info window 2021-02-28 11:36:29 -08:00
Matt Nadareski
a622cdd4e0 Clean up tabified Options window 2021-02-28 11:20:38 -08:00
Matt Nadareski
2a4ec83d0d 1 to 1 tabification of Options window 2021-02-28 11:02:21 -08:00
Matt Nadareski
b1d144d091 A little more log view cleanup 2021-02-28 10:55:49 -08:00
Matt Nadareski
23a8b66c16 Get rid of warning 2021-02-28 10:29:51 -08:00
Matt Nadareski
381ebd5961 Clean up logging view 2021-02-28 10:28:31 -08:00
Matt Nadareski
b4d1f7d6c3 LogWindow -> LogOutput 2021-02-28 02:28:53 -08:00
Matt Nadareski
b3eff64275 Get fancy with expander 2021-02-28 00:46:38 -08:00
Matt Nadareski
f15c00acca Multiline comments and contents 2021-02-28 00:36:36 -08:00
Matt Nadareski
a66d0d4722 Perform some cleanup on Options window 2021-02-27 23:23:12 -08:00
Matt Nadareski
66cdade01b Fix layerbreak reading (wrong endianness) 2021-02-27 23:04:25 -08:00
Matt Nadareski
9e9f001768 More StackPanel stuff 2021-02-27 22:39:07 -08:00
Matt Nadareski
3514bdcbc4 Update changelist 2021-02-27 22:32:37 -08:00
Matt Nadareski
312df37365 Use tabs, enable layers 2 and 3 2021-02-27 22:32:03 -08:00
Matt Nadareski
6214d91940 Rename ring fields in SubmissionInfo 2021-02-27 21:47:10 -08:00
Matt Nadareski
23d500434b Add and use UserInput
Please note that this only affects the disc information window for now. This may end up in the others later.
2021-02-27 21:33:37 -08:00
Matt Nadareski
7ee5e3ab43 Add PIC parsing 2021-02-27 16:58:09 -08:00
Matt Nadareski
6be4bb7c85 Boilerplate for 4 layer support 2021-02-26 22:52:28 -08:00
Matt Nadareski
d620c13e22 Update changelist 2021-02-26 22:38:21 -08:00
Matt Nadareski
73b585cdd2 Add PS5 version extraction (fixes #258) 2021-02-26 22:36:43 -08:00
Matt Nadareski
2f33581e4a Add PS5 console to list 2021-02-26 22:27:36 -08:00
Matt Nadareski
f3dba9a026 Add Xbox Series consoles to list 2021-02-26 22:13:18 -08:00
Matt Nadareski
d7d0af369c Boilerplate for 3 layer support 2021-02-26 21:58:10 -08:00
Matt Nadareski
2ab9a45a71 Update changelist 2021-02-04 22:06:13 -08:00
Matt Nadareski
dc0bf642a9 Better handling of drive changes
Similar to how things like changed options, UI initialization, and media scan all share basically the same path, most of what goes into changing drive letter is the same. So with a small tweak to that code, it is viable to use that method for all 4 paths.
2021-02-04 21:52:26 -08:00
Matt Nadareski
9e32435972 Reorganize Drive class a little 2021-02-04 21:38:57 -08:00
Matt Nadareski
c2b724e949 Auto-detect VCD (fixes #257) 2021-02-04 21:16:42 -08:00
Matt Nadareski
195d3c6ff7 Update to DIC 20210202 2021-02-01 21:16:59 -08:00
Matt Nadareski
04a8079cee Add better notes and outputs for process 2021-01-26 22:46:06 -08:00
Matt Nadareski
0b2daf7d30 Add notes, remove a couple files from serialization 2021-01-26 22:45:38 -08:00
Matt Nadareski
1353ee603b Fix serialization issue 2021-01-26 22:45:04 -08:00
Matt Nadareski
e9d272d139 Potentially massive file gets compressed 2021-01-26 20:55:45 -08:00
Matt Nadareski
0308c7a81e Direct to Base64 for binary files 2021-01-26 17:12:44 -08:00
Matt Nadareski
4caad10c9b Comment out binary files for now 2021-01-26 14:36:35 -08:00
Matt Nadareski
85c1623d33 Forgot the DVD/BD binary files 2021-01-26 13:55:05 -08:00
Matt Nadareski
5be1825bb5 Add all output files as encoded artifacts to JSON 2021-01-26 13:45:12 -08:00
Matt Nadareski
ec5e533bd8 Rename currently unused variable 2021-01-26 13:44:38 -08:00
Matt Nadareski
c3e4c7fa78 Add artifacts section to submission info 2021-01-26 11:39:45 -08:00
Matt Nadareski
bf66a3fcbd Add version to submission info 2021-01-25 17:05:32 -08:00
Matt Nadareski
101193cb78 Streamline statuses from trying to execute 2021-01-25 13:11:20 -08:00
Matt Nadareski
b610c29be6 Log exceptions on dumping as well 2021-01-25 12:05:46 -08:00
Matt Nadareski
999c4dceb5 Ensure the executable exists before trying to run it 2021-01-25 11:55:18 -08:00
Matt Nadareski
2957370fac Missed a couple of places 2021-01-25 10:21:50 -08:00
Matt Nadareski
5dd36d7b06 Inline all of those params during logging 2021-01-25 10:02:34 -08:00
Matt Nadareski
47b5bbe7e7 Update to BurnOutSharp 1.5.1 2021-01-22 12:03:12 -08:00
Matt Nadareski
5a2e4ca77e Add link to BOS in issue templates 2021-01-21 11:15:10 -08:00
Matt Nadareski
c479ddb80b Fix duplicate security sector data 2021-01-15 22:06:19 -08:00
Matt Nadareski
da1f59e0c1 Fix UIC output parsing and generation 2021-01-15 14:31:31 -08:00
Matt Nadareski
9574832e86 Add ability to get hashes for UIC outputs 2021-01-15 12:52:55 -08:00
Matt Nadareski
3df7842d23 Add ability to hash a file to base parameters 2021-01-15 12:13:27 -08:00
Matt Nadareski
62a0f6e49a Port most hashing code from SabreTools
Note that this explcitly omits both the formerly-supported RIPEMD160 as well as the currently-Aaru-specific SpamSum.
2021-01-15 11:57:24 -08:00
Matt Nadareski
60c180e4dd UIC outputs don't contain a datfile 2021-01-15 11:49:27 -08:00
Matt Nadareski
343d937362 Fix BCA formatting for CleanRip outputs (fixes #251) 2021-01-12 15:36:02 -08:00
Matt Nadareski
f20b321433 Add *tmp file support for DIC 2021-01-07 09:52:00 -08:00
Matt Nadareski
ba19b0825f Update changelist 2021-01-06 09:58:50 -08:00
Matt Nadareski
5abb44957c Add common path for init of full UI
This unites the code that runs when the application is loaded, when options are updated, and when a drive scan is initiated. All 3 situations require that nearly every bit of the UI and possibly even the disc to be re-scanned.
2021-01-05 21:39:46 -08:00
Matt Nadareski
b9d9cf812f Slightly different _mainInfo wording now... 2021-01-03 14:22:25 -08:00
Matt Nadareski
d1529f428a Updated to DIC version 20210102 2021-01-02 22:03:18 -08:00
Matt Nadareski
5061a79c4c Make initial window load more accurate 2020-12-31 14:57:06 -08:00
Matt Nadareski
eab3e25cf2 Fix crash on drive scan 2020-12-31 13:39:19 -08:00
Matt Nadareski
33e73a8992 More messages isnt a bad thing 2020-12-31 11:02:14 -08:00
Matt Nadareski
a63972b549 Get reasonable defaults based on selected system 2020-12-30 22:57:36 -08:00
Matt Nadareski
bc57df7fc1 Add a note for later 2020-12-30 22:29:42 -08:00
Matt Nadareski
e16f616286 Safer queries on getting disc type 2020-12-30 22:20:47 -08:00
Matt Nadareski
c5497d787b Ensure parameters have system and type set 2020-12-30 21:14:06 -08:00
Matt Nadareski
64f60004c4 Error messages on Redump errors 2020-12-30 15:00:52 -08:00
Matt Nadareski
020c063177 Parameters should know their state better 2020-12-30 14:49:28 -08:00
Matt Nadareski
a3e6f8157f Extension cleanup 2020-12-30 14:28:46 -08:00
Matt Nadareski
f27040cd1c Add informational issue template 2020-12-30 14:07:11 -08:00
Matt Nadareski
3a490cfc8d Add note about DIC-only dumps 2020-12-30 14:02:51 -08:00
Matt Nadareski
1f8c5998c1 Fix copy-paste error 2020-12-29 11:15:24 -08:00
Matt Nadareski
f96d4ee37e More safety around listing operations 2020-12-29 11:10:15 -08:00
Matt Nadareski
d0d15f03af Remove Avalonia UI (fixes #245) 2020-12-29 10:56:25 -08:00
Matt Nadareski
eff6407206 Remove CD-i DV from supported profiles 2020-12-27 13:39:26 -08:00
Matt Nadareski
c1e559568a Fix build 2020-12-26 14:31:59 -08:00
Matt Nadareski
ef030b290c Add support for new Redump regions 2020-12-26 14:03:24 -08:00
Matt Nadareski
71630591a1 Add short-circuit for unmatched tracks 2020-12-12 13:37:33 -08:00
Matt Nadareski
31505745be Update to Aaru v5.2 2020-12-02 20:51:56 -08:00
Matt Nadareski
e40444b60c Update changelist 2020-11-30 21:24:15 -08:00
Matt Nadareski
44edf387a6 Relabel labels depending on media type 2020-11-30 16:12:54 -08:00
Matt Nadareski
d3d225644d Add preliminary "default system" support 2020-11-23 14:00:15 -08:00
Matt Nadareski
3afd9200a9 Multiline fields require matching newlines 2020-11-23 13:45:36 -08:00
Matt Nadareski
c5c340ff12 Make label SID change for Avalonia too 2020-11-23 13:22:30 -08:00
Matt Nadareski
b28b85f7c6 Don't gray out label SID for DVD, HD-DVD, BD (fixes #247) 2020-11-23 13:20:37 -08:00
Matt Nadareski
c60684b83c Pass through progress in missing place 2020-11-23 13:17:27 -08:00
Matt Nadareski
a183a1ec91 Fix issue with 0xbe drives, again 2020-11-12 14:43:24 -08:00
Matt Nadareski
abd8488185 Fix remaining links 2020-11-10 20:42:04 -08:00
Matt Nadareski
e6dea158e5 Rename to MPF 2020-11-10 17:43:21 -08:00
Matt Nadareski
24150c7b3c Bump version to 1.18 2020-11-10 10:55:29 -08:00
Matt Nadareski
040720a1c2 Detect HD-DVD-Video 2020-11-08 13:40:30 -08:00
Matt Nadareski
70d30d370f Update to BurnOutSharp 1.5.0 2020-11-03 21:49:59 -08:00
Matt Nadareski
a50b99da07 Use older version of DIC for now 2020-11-01 16:08:57 -08:00
Matt Nadareski
2cecbe69ad Support the /ps command for DIC 2020-10-31 23:05:11 -07:00
Matt Nadareski
557410660f Update to DIC 20201101 2020-10-31 22:55:07 -07:00
Matt Nadareski
0d75dbaaa2 Add dumping program selection finally 2020-10-11 22:06:40 -07:00
Matt Nadareski
d38f95c73a Check sector 0 for Saturn, if possible (fixes #241) 2020-10-11 21:15:35 -07:00
Matt Nadareski
c9d59a90e4 Default to CD-ROM when detection fails as well 2020-10-11 21:07:28 -07:00
Matt Nadareski
fc6a34d987 Use CD-ROM as default when no type detection (fixes #237) 2020-10-11 20:46:24 -07:00
Matt Nadareski
8acabb692f Remove default config file (fixes #238) 2020-10-11 20:36:12 -07:00
Matt Nadareski
61f8871839 Change default for fixed drive detection (fixes #233) 2020-10-11 20:35:39 -07:00
Matt Nadareski
69d61ad185 Use selected drive in UI for copy protect scan (fixes #235) 2020-10-11 20:31:27 -07:00
Matt Nadareski
27391ed31b Remove subdump (fixes #234)
Note that it still downloaads the subdump executable and puts it in the same place. This won't be changing anytime soon since it's useful. It also is flat out removing the option in the menu without replacing or reshaping the menu. This will be addressed at a later date.
2020-10-11 20:24:41 -07:00
Matt Nadareski
ed56c92101 Clean up sector read method a bit more (nw) 2020-10-08 21:21:11 -07:00
Matt Nadareski
ab8ae1524f Actually use the drive letter (nw) 2020-10-08 14:18:23 -07:00
Matt Nadareski
de7c247583 Add initial version of sector reading to library 2020-10-08 12:26:28 -07:00
Matt Nadareski
997fd8f7ac Why do I keep forgetting changelist? 2020-10-01 11:46:55 -07:00
Matt Nadareski
e39e07246a Fix archive naming 2020-10-01 11:34:27 -07:00
Matt Nadareski
e0b0406d76 Let's see how badly this goes... 2020-10-01 11:28:39 -07:00
Matt Nadareski
1efbe9a784 Update BurnOutSharp version; fix autobuild 2020-10-01 09:44:49 -07:00
Matt Nadareski
fab921c2dd Added feature request template 2020-09-29 13:01:28 -07:00
Matt Nadareski
56896b4bea Update issue templates 2020-09-29 12:57:19 -07:00
Matt Nadareski
54eb7d8ecd Forgot the changelog 2020-09-28 11:26:47 -07:00
Matt Nadareski
114587b9f6 Reset paragraph state on clear (fixes #231) 2020-09-28 11:22:40 -07:00
Matt Nadareski
cad33c6c07 Don't lose automatic comments 2020-09-24 15:25:56 -07:00
Matt Nadareski
8154999f1c Fix alternate mainInfo parsing 2020-09-24 15:08:17 -07:00
Matt Nadareski
3a30c14085 Make this apparent on the main page 2020-09-24 11:42:28 -07:00
Matt Nadareski
db8b1df480 Add rolling changelog section 2020-09-24 11:35:16 -07:00
Matt Nadareski
e20cac8a80 Sync 2020-09-24 10:57:09 -07:00
Matt Nadareski
ce9b4e39f5 Add more internal info for GD-ROM LD 2020-09-24 10:49:27 -07:00
Matt Nadareski
ddff4b2e58 SCD has internal serial 2020-09-24 10:26:51 -07:00
Matt Nadareski
16a470475c Remember, no Disc 2020-09-24 10:21:35 -07:00
Matt Nadareski
6e01473e87 Saturn serial -> internal serial, strip 'V' from version 2020-09-24 10:19:34 -07:00
Matt Nadareski
8682089151 Update to DIC 20200921 2020-09-23 10:11:38 -07:00
Matt Nadareski
f45cb0075f Remove superflous check for PVD retrieval (fixes #229) 2020-09-19 14:31:17 -07:00
Matt Nadareski
afccb48798 Specify solution file 2020-09-19 13:40:36 -07:00
Matt Nadareski
ce6995fba0 It was seriously a directory marker issue 2020-09-17 22:04:31 -07:00
Matt Nadareski
3328d1adea Try without the git extension 2020-09-17 22:00:51 -07:00
Matt Nadareski
33d79df9a8 Update AppVeyor 2020-09-17 21:59:07 -07:00
Matt Nadareski
4fb64b19d6 Remove weird csproj stuff 2020-09-17 21:57:00 -07:00
Matt Nadareski
bf0f495c8b Ignore all files except the compiled cicm.cs 2020-09-17 21:54:50 -07:00
Matt Nadareski
5d5f8e8d8c Use official repo as submodule for CICM 2020-09-17 21:52:40 -07:00
Matt Nadareski
91aa248355 A little more cleanup and safety 2020-09-17 21:38:45 -07:00
Matt Nadareski
633c6c1efb Slight tweaks, mostly naming 2020-09-17 21:25:22 -07:00
Matt Nadareski
facb1f673f Wrap login call with try/catch 2020-09-17 15:52:29 -07:00
Matt Nadareski
9c8938c7f2 Get some more layerbreak stuff 2020-09-17 13:30:35 -07:00
Matt Nadareski
17f75f3ce6 First parts of layerbreak info 2020-09-17 10:37:04 -07:00
Matt Nadareski
b99c48afa2 First part of DVD protection extraction for Aaru 2020-09-16 20:45:07 -07:00
Matt Nadareski
9481fada23 Fix writing of valid indexes for Aaru cuesheet 2020-09-16 17:13:15 -07:00
Matt Nadareski
7efbc5043c Add error count for Aaru outputs 2020-09-16 17:00:11 -07:00
Matt Nadareski
92880d3148 Fix discs with ISO extension 2020-09-16 16:25:33 -07:00
Matt Nadareski
9800f2b8ae Use spans instead of Linq (and accidental commit) 2020-09-16 16:09:34 -07:00
Matt Nadareski
a5d01604cb Strip out hard-to-find CD Check instances 2020-09-16 16:01:16 -07:00
Matt Nadareski
aaab84f90a Fix multiple things in Aaru
Addresses overlooked issue with DatFile generation, consolidates common code into methods, fixes PVD generation and write
2020-09-16 15:59:11 -07:00
Matt Nadareski
318a1a303c Fix multiline outputs 2020-09-16 15:58:02 -07:00
Matt Nadareski
0061de6b2e Fix cuesheet write-to-stream 2020-09-16 15:57:22 -07:00
Matt Nadareski
b3db7c547f Generate cuesheet using new classes for Aaru 2020-09-16 14:31:29 -07:00
Matt Nadareski
1da29464a8 Add writing of cuesheets 2020-09-16 14:09:02 -07:00
Matt Nadareski
d1d4ff41c6 Fix reading (assumes reasonably sized cuesheets) 2020-09-16 13:41:00 -07:00
Matt Nadareski
673c2745a9 Fix index check 2020-09-16 13:00:42 -07:00
Matt Nadareski
4ad88441cc Add parsing in of cuesheets 2020-09-16 12:53:28 -07:00
Matt Nadareski
71bb822856 Add cuesheet structures 2020-09-16 11:22:33 -07:00
Matt Nadareski
2a7789bd12 Bump version to 1.17.1 2020-09-15 10:03:39 -07:00
Matt Nadareski
1d0417cc1c Fix DiscInformationWindow issues 2020-09-14 13:51:26 -07:00
Matt Nadareski
905c578d90 Fix DIC flags based on the code 2020-09-14 13:30:14 -07:00
Matt Nadareski
a457c85e53 Better internal handling of Options window 2020-09-13 21:01:53 -07:00
Matt Nadareski
36630fc0ed Fix the tests I broke 2020-09-13 20:52:08 -07:00
Matt Nadareski
f509ac60da Make DiscInformationWindow more robust 2020-09-13 20:41:23 -07:00
Matt Nadareski
bf8aac6f81 Move some constants around 2020-09-13 20:41:09 -07:00
Matt Nadareski
ceddcc0722 Remove useless passthrough vars 2020-09-12 14:11:55 -07:00
Matt Nadareski
3a2bda6fb6 Bump version to 1.17 2020-09-11 21:31:46 -07:00
Matt Nadareski
aea2403153 Remove fields that are too variable 2020-09-11 11:48:45 -07:00
Matt Nadareski
6e334df42f Cleanup now that .NET 4.6.2 is gone 2020-09-10 23:30:07 -07:00
Matt Nadareski
04cb68c9bd Rearrange based on Avalonia 2020-09-10 23:21:18 -07:00
Matt Nadareski
06060c071b Update all libraries, including BurnOutSharp 2020-09-10 22:45:36 -07:00
Matt Nadareski
02903686ad Remove accidental add 2020-09-10 22:02:12 -07:00
Matt Nadareski
00cbed725d Only fill error count if it's missing 2020-09-10 21:09:35 -07:00
Matt Nadareski
dabccd3a8a Fix for LibCrypt detect until BOS updated 2020-09-10 21:00:16 -07:00
Matt Nadareski
0e1e499a66 Guard against false positive LibCrypt (fixes #226) 2020-09-10 20:46:43 -07:00
Matt Nadareski
b4f485a0cc Add PS1/PS2 internal serial to comments (fixes #227) 2020-09-10 20:32:51 -07:00
Matt Nadareski
5bad68b9ff Include warning count with error count 2020-09-10 20:14:08 -07:00
Matt Nadareski
2db686fc47 Backport accept/cancel from Avalonia 2020-09-10 11:44:44 -07:00
Matt Nadareski
d705a4b316 UmdImageCreator stands alone 2020-09-10 11:27:25 -07:00
Matt Nadareski
0154ee5bb6 Start wiring through DCDumper a bit 2020-09-10 11:11:26 -07:00
Matt Nadareski
29f14f5690 Remove .NET 4.6.2 from readme 2020-09-10 10:54:47 -07:00
Matt Nadareski
dd6f7b0794 Consolidate version checking 2020-09-10 10:53:47 -07:00
Matt Nadareski
e5c9591d64 Merge pull request #225 from SabreTools/crossplat
Add Avalonia UI
2020-09-10 10:37:13 -07:00
Matt Nadareski
df8da8bd46 Fix PS1 copy protect output (fixes #222) 2020-09-10 10:32:21 -07:00
Matt Nadareski
c9b21006d1 Fix update check (fixes #220) 2020-09-09 14:34:50 -07:00
Matt Nadareski
2b37882322 Reformat CSS data (fixes #224) 2020-09-09 14:07:12 -07:00
Matt Nadareski
8a2b3f2c89 Separation of Creators 2020-09-09 11:08:20 -07:00
Matt Nadareski
c16a9aeb70 Couple more 2020-09-09 10:38:00 -07:00
Matt Nadareski
d5a0797c92 Couple more mapped values 2020-09-09 10:36:43 -07:00
Matt Nadareski
a55a769886 Fix conversion issue 2020-09-09 10:05:09 -07:00
Matt Nadareski
fd18c60f28 Let's reference the Wiki 2020-09-08 23:30:02 -07:00
Matt Nadareski
2704d1f88d Add 'private' flag to Paranoia mode for Aaru (fixes #212) 2020-09-08 22:53:43 -07:00
Matt Nadareski
c16c938fa4 Change end of section marker for XGD (fixes #219) 2020-09-08 22:51:17 -07:00
Matt Nadareski
497e2a09fd Add as many default regions as possible 2020-09-08 22:41:36 -07:00
Matt Nadareski
8bc8887ce6 Set Region name default again 2020-09-08 22:22:52 -07:00
Matt Nadareski
7e5a5586f9 Merge pull request #221 from sadikyo/patch-1
Update README.md
2020-09-08 22:14:56 -07:00
sadikyo
eee068db7e Update README.md
A few minor spelling and grammar corrections
2020-08-27 13:31:47 -04:00
Matt Nadareski
79e70173fa Add more standard programs to doc 2020-08-14 16:12:57 -07:00
Matt Nadareski
4f732557b9 Drive label should not contain bad chars 2020-08-14 12:59:23 -07:00
Matt Nadareski
6cbcebd661 Do the README shuffle 2020-08-14 11:06:57 -07:00
Matt Nadareski
f74dc01657 Fix percentage in Avalonia 2020-08-07 22:05:04 -07:00
Matt Nadareski
4735bef7b4 Merge branch 'master' into crossplat 2020-08-07 22:01:56 -07:00
Matt Nadareski
52a925df12 Fix percentage output 2020-08-07 22:00:15 -07:00
Matt Nadareski
3e10199cb0 Add path and scan to Check 2020-08-07 21:15:36 -07:00
Matt Nadareski
470e641a8c Protection scan output to Avalonia 2020-08-06 22:53:11 -07:00
Matt Nadareski
f61b84d058 Okay, fine 2020-08-06 22:53:11 -07:00
Matt Nadareski
5e77b43be1 Add icon, fix version, copyright, appveyor 2020-08-06 22:53:09 -07:00
Matt Nadareski
46530a9fca Add Avalonia, remove NET462 2020-08-06 22:53:07 -07:00
Matt Nadareski
12610c0d69 Add missing file feedback 2020-08-06 22:52:25 -07:00
Matt Nadareski
9bb9d3f407 Add progress to protection scan 2020-08-06 22:05:16 -07:00
Matt Nadareski
a0ed20042c Explain components 2020-08-04 13:53:33 -07:00
Matt Nadareski
37c253aac2 Preliminary use of cicm.cs 2020-08-03 12:52:53 -07:00
Matt Nadareski
68135c58ae Vut default hol output 2020-08-02 22:22:33 -07:00
Matt Nadareski
f9efa71fcc Better categorization 2020-08-02 22:00:07 -07:00
Matt Nadareski
184913ad28 Fix a couple of small things 2020-08-02 21:51:19 -07:00
Matt Nadareski
ff52767a02 Add DVD-Audio 2020-08-02 21:41:38 -07:00
Matt Nadareski
34ac5f9ba0 Fix audio disc error count (fixes #215) 2020-08-02 21:22:24 -07:00
Matt Nadareski
7296d109cc Set default categories (fixes #214) 2020-08-02 20:46:30 -07:00
Matt Nadareski
9be705ae30 Remove erroneous return (fixes #213) 2020-07-30 14:32:39 -07:00
Matt Nadareski
af8b376f5a Clarify wording 2020-07-28 14:28:54 -07:00
Matt Nadareski
03f9668048 Update README with more information 2020-07-28 14:10:29 -07:00
Matt Nadareski
3bb23fa7cc Update to Aaru v5.1 2020-07-24 21:04:43 -07:00
Matt Nadareski
f85c9d4e7e Move submission info to more proper place 2020-07-22 21:49:20 -07:00
Matt Nadareski
5de3d209be Migrate more to the proper place 2020-07-22 21:45:58 -07:00
Matt Nadareski
f1b136c817 Remote stands alone 2020-07-22 21:11:40 -07:00
Matt Nadareski
c18829e0b3 Sync 2020-07-22 13:54:45 -07:00
Matt Nadareski
d114b7f868 Add missing media dump params 2020-07-22 11:17:38 -07:00
Matt Nadareski
445cc173ce Fix incorrect PIC formatting 2020-07-16 14:45:51 -07:00
Matt Nadareski
1e28fcad2e Fix usage of avdp flag 2020-07-16 11:46:50 -07:00
Matt Nadareski
8d4e5155d9 Update to DIC 20200716 2020-07-16 11:06:58 -07:00
Matt Nadareski
027f562573 Change types order for XBOX/360 (fixes #209) 2020-07-07 13:13:18 -07:00
Matt Nadareski
74caf084ca Read entire PIC file (fixes #189) 2020-07-07 13:09:54 -07:00
Matt Nadareski
7396b02543 Update to DIC 20200620 2020-07-06 22:15:10 -07:00
Matt Nadareski
8035826b1b Fix datetime parsing 2020-06-25 12:39:27 -07:00
Matt Nadareski
c6e73582c5 More Aaru improvements and fixes 2020-06-25 11:19:16 -07:00
Matt Nadareski
ca09d8c703 Add Aaru to official list in Check 2020-06-25 11:17:26 -07:00
Matt Nadareski
40b6551c7a Fix README formatting 2020-06-16 17:05:17 -07:00
Matt Nadareski
95b664705d Command and DICer 2020-06-16 17:02:28 -07:00
Matt Nadareski
6bda7b35a2 Make async things async 2020-06-16 15:25:55 -07:00
Matt Nadareski
6fedebf2a9 Lower-case SHA-1 for search 2020-06-16 13:57:11 -07:00
Matt Nadareski
02f98c674b Trim on input files for Check 2020-06-16 13:25:45 -07:00
Matt Nadareski
fb1e4130df More CleanRip fixes 2020-06-16 13:25:24 -07:00
Matt Nadareski
c507f52b80 Add a hack for DICUI check in CleanRip 2020-06-16 12:50:04 -07:00
Matt Nadareski
7b2784f1a2 Wii needs CMP for Redump, separate for submission 2020-06-16 12:00:23 -07:00
Matt Nadareski
7a7c83e8cf Fix Redump check for DIC 2020-06-16 11:41:55 -07:00
Matt Nadareski
b86ef09763 Inform Check users if credentials invalid 2020-06-16 11:16:39 -07:00
Matt Nadareski
71c050edf6 Make subdump optional 2020-06-12 11:17:57 -07:00
Matt Nadareski
988f25b514 Fix INI parsing 2020-06-11 21:46:25 -07:00
Matt Nadareski
e878e8b904 Anti-modchip for the rest of us 2020-06-05 15:38:48 -07:00
Matt Nadareski
bd3484cb3d Better common methods 2020-06-05 14:15:06 -07:00
Matt Nadareski
3d769ed707 Private again 2020-06-05 11:15:51 -07:00
Matt Nadareski
613be66f91 Add INI parser, use for PS1/2 discs 2020-06-05 11:13:40 -07:00
Matt Nadareski
81e0ffbc3c Update to DIC 20200604, static 2020-06-04 16:36:09 -07:00
Matt Nadareski
8f3d325e7d Update to version 1.16.1 2020-05-07 15:40:29 -07:00
Matt Nadareski
a63472c6e6 Options (#201)
* Start reorganizing options and internals

* Make entire options flow more robust

* Few more TODOs, slightly cleaner code

* Simplify Options constructor

* Fix eject and reset

* Some other abstractions

* Enforce readonly

* Fix tests, like the TODO said

* Move specific output file parsing to specific parameters

* Add some future enums, add notes around .NET Core build path

* Wrap last incompatible .NET Core stuff

* Extract out CleanRip, fix a bunch of other spaghetti
2020-05-07 14:36:06 -07:00
Matt Nadareski
8c98005605 Fix CleanRip support 2020-05-07 14:23:49 -07:00
Matt Nadareski
8062d6cf17 Extract out CleanRip, fix a bunch of other spaghetti 2020-05-07 13:56:21 -07:00
Matt Nadareski
78bf6e63ed Wrap last incompatible .NET Core stuff 2020-05-07 13:03:24 -07:00
Matt Nadareski
68e0d759f7 Add some future enums, add notes around .NET Core build path 2020-05-07 12:58:37 -07:00
Matt Nadareski
26e72284af Move specific output file parsing to specific parameters 2020-05-06 20:24:33 -07:00
Matt Nadareski
0efecd6601 Fix tests, like the TODO said 2020-05-06 17:00:34 -07:00
Matt Nadareski
9a3c2eb626 Enforce readonly 2020-05-06 16:46:26 -07:00
Matt Nadareski
ca753b4526 Some other abstractions 2020-05-06 16:37:33 -07:00
Matt Nadareski
230b6ca721 Fix eject and reset 2020-05-06 16:06:30 -07:00
Matt Nadareski
dc90f9af3f Simplify Options constructor 2020-05-06 15:56:08 -07:00
Matt Nadareski
b5898c7ea3 Few more TODOs, slightly cleaner code 2020-05-06 15:44:46 -07:00
Matt Nadareski
36f1aea509 Make entire options flow more robust 2020-05-06 15:33:28 -07:00
Matt Nadareski
6742444182 Start reorganizing options and internals 2020-05-06 14:24:37 -07:00
Matt Nadareski
e01fd37e6b Typo 2020-05-05 14:37:34 -07:00
Matt Nadareski
ca7071f82a Update Aaru version for WIP builds 2020-05-05 14:23:23 -07:00
Matt Nadareski
3cb67e3e65 Add Python2 CNF parsing, forgot DumpEnv fixes 2020-05-01 15:02:58 -07:00
Matt Nadareski
e67dd589b5 Fix options, add fallbacks, consolidate code 2020-05-01 14:59:29 -07:00
Matt Nadareski
74f491eaaa Forgot the flag 2020-04-21 17:03:45 -07:00
Matt Nadareski
9ebd28ef5a More dd stuff, including AppVeyor. Why? Because. 2020-04-21 16:09:37 -07:00
Matt Nadareski
2a58052bfd So, I added DD support 2020-04-21 15:52:57 -07:00
Matt Nadareski
5ed73aff2b Mock out the basics needed for DD 2020-04-21 14:39:39 -07:00
Matt Nadareski
5a6ad09004 No DIC firmware check temporarily (fixes https://github.com/SabreTools/DICUI/issues/199) 2020-04-21 11:24:22 -07:00
Matt Nadareski
6a43b74043 Fix file location in AppVeyor builds 2020-04-13 12:17:17 -07:00
Matt Nadareski
69dc91aa1a Update to 1.16 2020-04-13 12:03:58 -07:00
Matt Nadareski
f840db5143 Cleanup and Upgrades (#197)
* First part of cleanup

* Second part of cleanup

* Second part of cleanup, part two

* Second part of cleanup, part three

* Second part of cleanup, part four

* Third part of cleanup

* Fourth part of cleanup (nw)

* Rebranding

* Aaru-fication

* Try to fix .NET Core builds

* Strip out CD Check for false positives

* Update DIC to 20200403

* Add .NET 4.8 to automated builds

* Address a couple of TODOs

* Typo

* Aaru is up to date

* Fix AppVeyor

* Add new systems (fixes #196)

* Fix build

Co-authored-by: Matt Nadareski <mnadareski@mparticle.com>
2020-04-13 11:55:21 -07:00
Matt Nadareski
f33cd41ebb Update BurnOutSharp (fixes #191) 2020-02-18 14:27:44 -08:00
Matt Nadareski
da7896e62a Make it super apparent they're disabled 2020-02-18 11:43:49 -08:00
Matt Nadareski
08b5b4a1aa Condiitionally disable L1 for single-layer discs 2020-02-18 11:34:57 -08:00
Matt Nadareski
c397701818 Better check for submission info write (fixes #190 2020-02-18 11:25:34 -08:00
Matt Nadareski
617eb00f21 Fix PS regex matching (fixes #192) 2020-02-18 11:15:26 -08:00
Matt Nadareski
751fd0cf1e Check PSX.EXE date second (fixes #193) 2020-02-18 11:05:15 -08:00
Matt Nadareski
84c64e6073 Add Chef as Check flag 2020-02-06 23:54:49 -08:00
Matt Nadareski
ff5bc464ab Non-dumping commands shouldn't cause issues 2020-02-04 13:25:50 -08:00
Matt Nadareski
b09a87c7e4 Partial datfile and cuesheet support for Chef 2020-02-04 00:44:12 -08:00
Matt Nadareski
4032b48e63 Calculate start better 2020-02-04 00:41:52 -08:00
Matt Nadareski
12fad35065 Fix Chef flag handling for debug, verbose, and version 2020-02-04 00:20:13 -08:00
Matt Nadareski
c28be2da72 Update Creator version... again 2020-02-04 00:16:19 -08:00
Matt Nadareski
122cc0d20b Support similarly named parameters for DiscImageChef 2020-02-03 14:17:42 -08:00
Matt Nadareski
89db32dd53 Preliminary Support for DiscImageChef (#188)
* Professional cook

* Accuracy improvements

* Better yet

* Spicy

* Simplify and reduce

* You eye note

* More info for validation

* Read you

* Verbose

* Typo

* Custom fix

* Of note

* Clearly the same

* Creator update

* Creator MCN flag gone

* Missed a spot

* Fix issues after code walkthrough
2020-02-03 14:02:24 -08:00
Matt Nadareski
59040ae0f0 Update naming schemes 2020-01-30 14:47:23 -08:00
Matt Nadareski
d8035745a9 Add button to save console output 2020-01-29 13:28:27 -08:00
Matt Nadareski
c73e13c1f0 Update packages, better LibCrypt handling (fixes #134) 2020-01-29 12:21:41 -08:00
Matt Nadareski
af75c7c949 Add .NET Core 3.0 note to README 2020-01-29 10:18:43 -08:00
Matt Nadareski
896eb28308 Upgrade EDC check for PS1 (fixes #148) 2020-01-29 10:17:56 -08:00
Matt Nadareski
6ca3d39e5f Manual protect scan can handle SmartE weirdness 2020-01-28 21:25:26 -08:00
Matt Nadareski
a7af0d0d7b Add P* serials for PlayStation (fixes #187) 2020-01-27 22:38:07 -08:00
Matt Nadareski
04f5a7ea8d Add remote version check (fixes #45) 2020-01-27 22:27:40 -08:00
Matt Nadareski
5d82cc5622 Add option to reset drive after dump (fixes #143) 2020-01-27 13:33:30 -08:00
Matt Nadareski
2f37f51c0c Simplify serial matching for PS1/PS2 (fixes #186) 2020-01-27 11:58:08 -08:00
Matt Nadareski
4690db61e1 Add CleanRip formatting support (fixes #185) 2020-01-27 03:06:11 -08:00
Matt Nadareski
672ec42903 Don't overwrite region if it already exists 2020-01-27 01:38:52 -08:00
Matt Nadareski
cb39599a46 Get region from PS1/2 executable name (fixes #175) 2020-01-27 01:34:39 -08:00
Matt Nadareski
80b5ff3920 Fix options UI, add ignore fixed drives setting (fixes #182) 2020-01-27 00:42:58 -08:00
Matt Nadareski
f27d3a91d3 Fix Check build 2020-01-27 00:04:17 -08:00
Matt Nadareski
b18357bff5 Better eject location 2020-01-27 00:03:03 -08:00
Matt Nadareski
e3cd5b7082 Move eject to before user input (fixes #173) 2020-01-26 23:59:05 -08:00
Matt Nadareski
16d7a6224b Fix drive speed selection (fixes #177) 2020-01-26 23:18:20 -08:00
Matt Nadareski
f8e32ec06c Multi-select language dropdown in submission info (fixes #170) 2020-01-26 23:06:52 -08:00
Matt Nadareski
7073d4e298 Allow testing of credentials from UI (fixes #171) 2020-01-26 22:59:03 -08:00
Matt Nadareski
f7f464920b Fix ampersand tests 2020-01-26 22:45:59 -08:00
Matt Nadareski
e677dc20e9 Add missing default setting 2020-01-26 22:45:46 -08:00
Matt Nadareski
9871eb9928 Save path fixing until the very end (fixes #176) 2020-01-26 22:24:10 -08:00
Matt Nadareski
12b43cf688 Remove ampersand filtering (fixes #180) 2020-01-26 22:06:38 -08:00
Matt Nadareski
7dd6db66ee GD-ROM LD area header extraction 2019-12-23 13:07:23 -08:00
Matt Nadareski
d7726a070e Update to DIC 20191223 2019-12-23 10:28:42 -08:00
Matt Nadareski
fbc9d04782 Advanced Support (#172)
* Who doesn't like drives?

* Add another TODO

* Use built in stuff, it's quicker

* More special handling for floppies, easier this time

* Fix broken test

* Set active drive priority, String -> string

* Track reason for no scanning

* Update DIC version and release notes
2019-11-17 01:06:41 -05:00
Matt Nadareski
24b08dd245 Update BurnOutSharp 2019-10-25 20:37:08 -07:00
Matt Nadareski
c7ebe69b05 Update BurnOutSharp 2019-10-24 13:25:28 -07:00
Matt Nadareski
22ffda4b5a Fix copy protection scan by preferring 32-bit 2019-10-19 02:42:01 -07:00
Matt Nadareski
b89c051d7d DICUI 1.14 Release 2019-10-01 11:57:15 -07:00
Matt Nadareski
8b41f03472 Update to new DIC version (#167)
* Remove `.` handling code since it should be in DIC now

* Add support for 3 new flags

* Fix dot tests

* Fix PS4 autodetect (Fixes #164)

* Fix PS4 version finding

* Update DIC archive path

* Add support for disk command (unused by default)
2019-10-01 11:53:44 -07:00
Matt Nadareski
700ff50eef Upgrades, people! (#166)
* First step: 4.6.2

* Make Check and Library .NET Core 3.0 compatible

* Enable 4.6.2 and 4.7.2 on DICUI

* DICUI .NET Core 3.0

* Solution items

* New Appeyor paths, environment

* Upgrade DICUI.Test for all frameworks too

* Fix .NET Core 3.0 difference in path handling
2019-10-01 11:36:01 -07:00
Matt Nadareski
95ac7236ff Fix PS4 version finding 2019-09-29 22:23:45 -07:00
Matt Nadareski
755f1b8e95 Root directories are weird (Fixes #163) 2019-09-29 21:28:20 -07:00
Matt Nadareski
d431a4c87f Fix PS4 autodetect (Fixes #164) 2019-09-29 21:18:02 -07:00
Matt Nadareski
04348d2d58 PSX.EXE is a valid filename to find 2019-09-17 20:59:10 -07:00
Matt Nadareski
6f5214c1a4 Even More Info (#161)
* Add (currently) hidden setting for disc info

* Show setting in Options menu

* Fix new window

* New place for combo box items

* Fixes to outputs

* Add category, fix a few things

* Actually use the custom converter

* Allow tabs in ringcode
2019-09-17 20:50:44 -07:00
Hennadiy Brych
1fe12be0b5 PSX date fixes (#157)
* Sony PlayStation date extraction fixed, 3 separate issues:
1. 190x year instead of 200x
2. Failure to get date for some titles where SYSTEM.CNF BOOT record doesn't have backslash after "cdrom:"
3. Date shift by one day due to Utc offset applied
Relaxed directory path requirements to allow dot and ampersand. DiscImageCreator had to be changed in order to support this, will be submitted separately.

* Reverted extra path characters modification.

* Reverted to UTC time
2019-08-28 00:02:13 -07:00
Matt Nadareski
9c9ca16366 Persist paths unless changing drive letters (fixes #154) 2019-08-12 14:08:55 -07:00
Matt Nadareski
1702db71d1 Add new Redump regions (GC; Uk,Au; U,Ca) 2019-08-05 11:07:51 -07:00
Matt Nadareski
0ba7ccdb9a HTML decode (fixes #150) 2019-07-30 00:19:02 -07:00
Matt Nadareski
3ccf65190e Migrate to archive.org 2019-07-18 11:26:17 -07:00
Matt Nadareski
6b9ee3d5ed Update AppVeyor config 2019-07-18 11:20:09 -07:00
Matt Nadareski
3ee0355034 Add speculative values for VideoNow dumping 2019-07-18 10:42:23 -07:00
Matt Nadareski
7074fa1e2c Update to newest DIC, create new release 2019-07-01 20:50:38 -07:00
Matt Nadareski
44b91a6611 Add initial type dectection, hopefully fixes #145 2019-06-17 14:36:54 -07:00
Matt Nadareski
b2185dbed9 Better ways of getting settings (defaults, existence, etc) 2019-05-26 22:30:06 -07:00
Matt Nadareski
ec4b69b6e6 Multiline outputs should lack prefix (force DAT multiline) 2019-05-26 00:57:06 -07:00
Matt Nadareski
b6db873a00 Settings and more (fixes #141) 2019-05-26 00:48:26 -07:00
Matt Nadareski
db9222b737 Accidential layerbreak infos 2019-05-24 14:26:11 -07:00
Matt Nadareski
b7bfa3ba28 Readonly Regex, Rather 2019-05-24 11:18:49 -07:00
Matt Nadareski
ab25687119 Better UMD support 2019-05-24 11:17:59 -07:00
Matt Nadareski
9bfa0a01ad I clearly remember how Remove works 2019-05-23 22:00:36 -07:00
Matt Nadareski
19c99fb7fe I'm having fun 2019-05-22 22:06:43 -07:00
Matt Nadareski
913244459e Oops, forgot a couple 2019-05-22 21:49:55 -07:00
Matt Nadareski
c66df99e22 Add a few more fun things that might get UI features later 2019-05-22 21:33:31 -07:00
Matt Nadareski
03f4a6e2e4 csproj cleanup 2019-05-22 16:13:47 -07:00
Matt Nadareski
31a243eeb4 Update changelist (been too long) 2019-05-22 16:07:15 -07:00
Matt Nadareski
043c0eff1c BluRay has a default layerbreak and known sizes 2019-05-22 15:49:15 -07:00
Matt Nadareski
ff44313e13 Buffs for potential future work 2019-05-21 00:22:16 -07:00
Matt Nadareski
e06ed31b21 Round 2, fight 2019-05-21 00:07:24 -07:00
Matt Nadareski
aeaec9e805 Add currently unused dump status 2019-05-20 23:39:37 -07:00
Matt Nadareski
6df0c76939 Stupid default namespace 2019-05-20 23:35:04 -07:00
Matt Nadareski
72efffcec4 Cleanup brigade 2019-05-20 22:14:53 -07:00
Matt Nadareski
930e4f7514 Only allow trailing spaces if there's no trailing directory marker 2019-05-20 15:08:41 -07:00
Matt Nadareski
85f71032b5 Fix trailing space handling for output directory 2019-05-20 15:00:41 -07:00
Matt Nadareski
ead530e2ec Code prettification 2019-05-20 00:12:06 -07:00
Matt Nadareski
dc14b1ea52 Both successes, not overwrite 2019-05-19 23:59:16 -07:00
Matt Nadareski
4628be2855 Simplify per-type and per-system code 2019-05-19 23:50:37 -07:00
Matt Nadareski
1f24c08770 Move some logic to SubmissionInfo, fix PS1/PS2/PS4 2019-05-19 22:13:57 -07:00
Matt Nadareski
4889b9d0bf Fix EXE parsing for PS1/PS2 discs 2019-05-14 21:05:05 -07:00
Matt Nadareski
b3d7422a66 Minor cleanup of fields and comments 2019-05-14 20:54:08 -07:00
Matt Nadareski
a0000528de Build date as string again 2019-05-14 20:50:17 -07:00
Matt Nadareski
2be46ec7a1 Trim fields that may have trailing whitespace 2019-05-14 01:56:39 -07:00
Matt Nadareski
f2510a08a4 Barcode, another low-hanging fruit 2019-05-14 01:51:48 -07:00
Matt Nadareski
2787377250 Add ability to get matching IDs from Redump 2019-05-14 01:46:10 -07:00
Matt Nadareski
e61ca31a3f Remove unused Forms version 2019-05-13 23:51:00 -07:00
Matt Nadareski
1b735b5d06 Migrate to SubmissionInfo object, add JSON output 2019-05-13 21:55:08 -07:00
Matt Nadareski
ad3d26cdea Better naming of blank-labeled discs 2019-05-12 21:18:07 -07:00
Matt Nadareski
f372ec4ccd Add new system, better submissioninfo, future 2019-05-11 22:32:22 -07:00
Matt Nadareski
44e75a50d7 Mass add of unused systems 2019-05-06 12:29:19 -07:00
Matt Nadareski
6f0ea2de3e Formats cleanup before adding more 2019-05-06 10:52:07 -07:00
Matt Nadareski
b4f0f7f4b3 Ensure all GD-ROM systems have LD optinos as well 2019-05-05 23:57:11 -07:00
Matt Nadareski
5027d67731 Add new VideoNow systems, cleanup 2019-04-27 17:11:39 -07:00
Matt Nadareski
6c8016345a Use full file path in Check (to correct relative paths) 2019-04-20 22:03:23 -07:00
Matt Nadareski
7ba04e63c7 Support JagCD (may need /ms still) 2019-04-11 21:12:36 -07:00
Matt Nadareski
77dc1d0029 Fix X360 DMI offset 2019-04-04 14:33:26 -07:00
Matt Nadareski
2b6538707f Add DMI extraction for XBOX/369 (fixes #139) 2019-04-04 01:09:51 -07:00
Matt Nadareski
902e4e5715 Make DICUI.Check better 2019-04-04 00:37:17 -07:00
Matt Nadareski
28f6d50e5a Better error checking; more DICUI.Check updates 2019-03-30 21:44:57 -07:00
Matt Nadareski
d8ed7d6ad7 Fix CD dumping with newest release; DICUI.Check minor update 2019-03-30 21:32:10 -07:00
Matt Nadareski
078d7d0ea3 Fix failing test 2019-03-27 17:37:03 -07:00
Matt Nadareski
5895a66c7a Update to DIC 20190326 2019-03-27 17:32:31 -07:00
Matt Nadareski
5ccff836e2 Add DVD for Panasonic M2 (fixes #138) 2019-02-26 11:57:12 -08:00
Matt Nadareski
b2c2e8c4d9 Reverse mould SID fields order (fixes #137) 2019-02-26 11:55:14 -08:00
Matt Nadareski
890959cfe0 Add DICUI.Check, standalone output parsing tool 2019-02-10 14:47:53 -08:00
Matt Nadareski
8729c7f20c Add new template to CD/GD as well 2019-01-28 21:15:07 -08:00
Matt Nadareski
74826909f4 Bump version to 1.12 2019-01-27 22:25:53 -08:00
Matt Nadareski
eb4904afb4 Add support for SCD/MCD headers (fixes #133) 2019-01-27 21:42:03 -08:00
Matt Nadareski
06975316b9 Add Panasonic M2 and Hasbro VideoNow to supported discs 2019-01-27 21:31:05 -08:00
Matt Nadareski
e0be24e54f Update links after organizational shift 2019-01-27 20:45:10 -08:00
Matt Nadareski
eb3cbc44a7 Add missing PVD text (fixes #128) 2019-01-27 16:30:15 -08:00
Matt Nadareski
d6f39bfd4c Ped-ant-ic (fixes #132) 2019-01-27 16:27:59 -08:00
Matt Nadareski
576fa07a2c Fix dumping status in Winforms 2018-11-07 20:27:35 -08:00
Matt Nadareski
ae2b26f84e Capitalization that's been bothering me 2018-11-05 20:30:39 -08:00
Matt Nadareski
1d9a2c9be7 Make sure that detected disc type is shown; cleanup 2018-11-05 20:27:01 -08:00
Matt Nadareski
538c0bba09 Fix output of the media type box in Winforms 2018-11-05 15:30:45 -08:00
Matt Nadareski
57c31cad48 Update AppVeyor build number 2018-11-05 14:51:14 -08:00
Matt Nadareski
093db36f44 Make Floppy Disk a supported format again 2018-11-05 14:51:00 -08:00
Matt Nadareski
fd4db3f8f0 Make SCM optional for Audio-CD (fixes #127) 2018-11-05 14:48:25 -08:00
Matt Nadareski
856a3161be Fix missing file extension 2018-10-22 11:09:28 -07:00
Matt Nadareski
dfafe9789a Update AppVeyor for new DIC version 2018-10-22 11:06:25 -07:00
Matt Nadareski
9f750d01c4 Correct submissionInfo output order (fixes #125) 2018-10-22 10:44:29 -07:00
Matt Nadareski
705ee82cbb Audio CDs don't have the _EdcEcc file right now 2018-10-22 10:41:10 -07:00
Matt Nadareski
d1f80efa41 Add DIC merge command and skip sector flag 2018-10-22 10:32:48 -07:00
Matt Nadareski
f88e6617e5 Fix some of the descriptions in submissionInfo (partial #115) 2018-10-15 17:22:42 -07:00
Matt Nadareski
1401422b08 Add DVD for PS3 (fixes #124) 2018-10-15 17:19:48 -07:00
Matt Nadareski
4539066af0 Update version numbers for 1.11 2018-10-08 12:53:59 -07:00
Matt Nadareski
ec23502275 Abstract out non-UI code to separate DLL (#122)
* Abstract out non-UI code to separate DLL

This change seems rather large, but it's mostly just moving anything that is not directly driving the UI to its own, separate library. This will make it easier at a later date for any UI improvements or changes as well as making the code much more discrete. One TODO has been added as a result of this change.

* Remove MessageBox from library

This change removes the last UI-driven elements from the library. This now makes the library (in theory) UI-agnostic and removes the TODO.

* Options is more UI-related

* Fix Nuget references in csproj

* Add Winforms UI

This is a clone of the current WPF UI but implemented in Windows Forms. WPF is not currently supported by Mono for Linux and macOS, so this can provide an alternative to those users who want to run on those systems instead. This also adds a second artifact for the Winforms build.
2018-10-08 11:06:22 -07:00
Matt Nadareski
331f875e7f Add better progress logging for dumps (fixes #121) 2018-09-24 12:17:57 -07:00
Matt Nadareski
e68f88062f Fix mundane test 2018-09-20 22:39:07 -07:00
Matt Nadareski
d1bb8184d1 Path correction 2018-09-20 22:18:07 -07:00
Matt Nadareski
b7b3f36402 Fix build 2018-09-20 21:30:13 -07:00
Matt Nadareski
859af8efea Format wars 2018-09-20 21:25:22 -07:00
Matt Nadareski
8594d7884c Update terminology (fixes #119) 2018-09-19 20:50:57 -07:00
Matt Nadareski
946dc929f1 Add profile for low-density part of GDROM
Currently, it's easier to just say "it's a CD" and call it a day
2018-09-19 20:49:33 -07:00
Matt Nadareski
1e5b1a205e Update DIC version for AppVeyor 2018-09-19 20:42:49 -07:00
Matt Nadareski
3271cd0ea3 Fix Saturn output info (fixes #118) 2018-09-10 18:27:13 -07:00
Matt Nadareski
647bab8cd3 Merge branch 'master' of https://github.com/reignstumble/DICUI 2018-09-10 16:44:49 -07:00
Matt Nadareski
89b2438dac Threefold fixes
1) Fix the issue where custom parameters that include the `/s 2` flag crash the program
2) Make editing custom parameters a little more intutive (still need to figure out either a way in-documentation or in-UI to explain the checkbox a little better). This part of the change needs to be more tested but there aren't many people using the current feature anyway.
3) Fix the random "Error count: -1" issue that some users were seeing (ended up being a result of more things being in the `_mainError.txt` file than originally anticipated)
2018-09-10 16:43:25 -07:00
Whovian9369
1dcd36d640 Change printable string misspellings relating to DIC (#117)
* Update DumpEnvironment.cs

Change Disk to Disc in printable strings of DumpEnvironment.cs

* Update OptionsWindow.xaml

Change Disk to Disc in printable strings of OptionsWindow.xaml
2018-09-03 13:16:03 -07:00
Matt Nadareski
2f63ebcd29 Fix unprotected DVD info; hopefully fix error count parsing 2018-09-03 00:38:52 -07:00
Matt Nadareski
316953f38c Add UmdImageCreator output parsing 2018-09-02 22:15:40 -07:00
Matt Nadareski
62813ac701 Fix tests 2018-09-01 18:03:48 -07:00
Matt Nadareski
22a936e60b XBOX CDs are not XISO formatted 2018-09-01 17:57:26 -07:00
Matt Nadareski
6412205fa3 Unknown sector ranges aren't recorded 2018-09-01 17:43:38 -07:00
Matt Nadareski
75c18ea3c4 Update for even more DIC changes 2018-09-01 17:02:09 -07:00
Matt Nadareski
6bdb93208e Add DVD-Video protection info 2018-08-29 14:40:35 -07:00
Matt Nadareski
588ed19692 Update README.md
The link for AppVeyor now goes directly to the artifacts page for easier downloading. Hopefully this reduces the issues that are seen with new users.
2018-08-28 21:44:07 -07:00
Matt Nadareski
d56ab5ab37 Update to new version of DIC in AppVeyor 2018-08-28 21:42:01 -07:00
Matt Nadareski
819d18112e My fault, this try/catch doesn't need to be here 2018-08-28 21:29:24 -07:00
Matt Nadareski
2e79cbb0db Fix XBOX/X360 outputs 2018-08-28 21:01:52 -07:00
Matt Nadareski
f024611de8 Take advantage of new XGD3 support in DIC (fixes #71) 2018-08-28 17:31:45 -07:00
Matt Nadareski
48e6283c37 Add PS4 PIC extraction (fixes #106) 2018-08-19 01:25:52 -07:00
Matt Nadareski
fec70f17bc ...wrong path 2018-08-19 00:50:58 -07:00
Matt Nadareski
257f7ab7df Last fix: subfoldering 2018-08-19 00:48:18 -07:00
Matt Nadareski
c1ed3aca02 Path is important 2018-08-19 00:43:32 -07:00
Matt Nadareski
ace6a78236 Attempt to fix AppVeyor 2018-08-19 00:41:34 -07:00
Matt Nadareski
f1e9f1d433 Add PS4 version finding, minor fixes (fixes #105) 2018-08-19 00:19:57 -07:00
Matt Nadareski
e83190c091 Writeback for updated path values (#111) 2018-08-17 20:22:08 -07:00
Matt Nadareski
82eabc12ca Try to fix output path checks (fixes #111) 2018-08-17 20:16:13 -07:00
Matt Nadareski
76b549d773 Add try/catch around dump (fixes #110) 2018-08-17 20:08:50 -07:00
Matt Nadareski
227688d7dc Fix PS2 VER spacing (fixes #112) 2018-08-17 19:24:55 -07:00
Matt Nadareski
4c9e2359af Update system info 2018-08-14 21:59:41 -07:00
Matt Nadareski
d179d7f464 Recognize new swap commands 2018-08-14 21:54:41 -07:00
Matt Nadareski
fff19e75fd Fix XBOX security sector format 2018-08-02 12:38:29 -07:00
Matt Nadareski
2175ccfb26 Update CHANGELIST 2018-07-29 12:44:46 -07:00
Matt Nadareski
621923bdba Add new Redump-recongized system 2018-07-24 19:27:45 -07:00
Matt Nadareski
3c216aa309 More accurate detection 2018-07-21 17:02:51 -07:00
Matt Nadareski
d85186e5cd Fix copy protection output again
This doesn't warrant a full PR
2018-07-19 23:40:17 -07:00
Matt Nadareski
1a94ad3d23 Don't try to get fancy 2018-07-19 15:34:16 -07:00
Matt Nadareski
91776d939c AppVeyor and Test (#103)
* Fixes to AppVeyor

* Fix tests
2018-07-19 15:31:25 -07:00
Matt Nadareski
3d058e633f Unblock and Update (#102)
* RAINBOW

* Add to FormatOutputData

* Update BurnOutSharp

* Fix parameter creation

* Minor improvements

* Update package versions
2018-07-19 14:11:58 -07:00
Matt Nadareski
94c0564fe8 Goodbye drive speed finders (#101)
* Goodbye drive speed finders

* More accurate scaling

* Ensure disposal
2018-07-17 16:42:38 -07:00
Matt Nadareski
87ecc7588e Update BurnOutSharp 2018-07-17 10:41:29 -07:00
Matt Nadareski
8a5c114a7e External and Fix (#100)
* Progress report updates log

* Logging additions

* Minor update

* Set halfway point only if nothing can be found

* Better population metrics

* QOL improvements

* More tiny logging updates

* Autoscroll

* Be consistent

* Update packages, use submodules

* Use NuGet

* Not 100% sure why I did it that way...

* Update README.md

* Make copy protect scan prompt an option

* Enhanced-CD might be Win/Mac

* Add unconfirmed formats

* Enhance!
2018-07-17 10:21:37 -07:00
ReignStumble
e307cb217f Fix DPI issue 2018-07-16 22:41:05 -04:00
Matt Nadareski
b8c56bea58 Whole Cleanup (#97)
* Cleanup TODOs

* Remove more TODOs

* Add case-insensitive dictionary for protection scans

* Namespace cleanups

* Space

* Minor fixes

* Oops

* Change layerbreak detect value
2018-07-15 22:26:22 -07:00
Matt Nadareski
564a6af9b8 Protection, Progress, and (Possibly) Empty Drives (#93)
* Include but mark inactive drives (fixes #87)

* Add more checks for the active flag

* Force users to choose on dump

* Fix quotes

* Update SolidShield 1

* Select first active by default

* Add TODO

* Add dumping progress indicator

* Clamp runtime

* Add first textfile scans; EA CdKey

* Slightly better executable detection

* Better file type detection

* Get as much into magic number checks as possible

* Nicer formatting

* Trailing slash

* Add back for benefit of the doubt

* Remove dead code

* Safer cabinet scanning

* Remove iXcomp; Add Unshield

* Fix build, add more comments

* Post-merge cleanup

* Add a new log line

* Make protection scan mechanics easier to see

* Add a space
2018-07-13 16:40:40 -07:00
Jacopo Santoni
5bcde889b3 Log Window enhancements (#95)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* created LogWindow and first tests of output gathering

* integrated all the proof of concept related to stream fetching of DIC output into LogWindow

* working on LogWindow\ncreated more efficient output matching management, improved handling of process termination

* created Tasks file\nmoved some methods there, created DumpEnvironment to manage are arguments

* moved additional methods to Tasks to make it more modular

* changed StartDumping flow to avoid returning if extra tools are not found

* moved main dump workflow into Tasks class

* created specific DumpResult class for better error reporting/management

* fixes

* fixes Dispatcher thread issue with progress bar

* continued refactor
- split EnsureDiscInformation into an additional EnsureCorrectInformationForSystemAndMediaType
- proof of concept of using custom extensions to enum types to give better functionality (and encapsulation)
- changed cmb_MediaType to keep a List<MediaType?> and got rid of Tuple<string, MediaType?>

* restored GetDiscType functionality

* fixed btn_StartStop enabled on EnsureCorrect... error

* fixed whitespace

* fixed indentation

* merged conflicts

* moved log window

* working on LogWindow resize/location behavior

* working on message logging in LogWindow

* added working integration between MenuItem for LogWindow and its visibility status, fixes issues with positioning and visibility, correctly bound Verbose flag to property

* finished integrating LogWindow, added some verbose logs

* added logs

* fixed include order

* added option to show log window at startup, removed useless QuietMode property from MainWindow

* added precise calculation of MainWindow position when LogWindow is shown and fixed topmost cheat
2018-07-13 16:38:50 -07:00
Jacopo Santoni
b39c96cdd2 Log Window (#94)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* created LogWindow and first tests of output gathering

* integrated all the proof of concept related to stream fetching of DIC output into LogWindow

* working on LogWindow\ncreated more efficient output matching management, improved handling of process termination

* created Tasks file\nmoved some methods there, created DumpEnvironment to manage are arguments

* moved additional methods to Tasks to make it more modular

* changed StartDumping flow to avoid returning if extra tools are not found

* moved main dump workflow into Tasks class

* created specific DumpResult class for better error reporting/management

* fixes

* fixes Dispatcher thread issue with progress bar

* continued refactor
- split EnsureDiscInformation into an additional EnsureCorrectInformationForSystemAndMediaType
- proof of concept of using custom extensions to enum types to give better functionality (and encapsulation)
- changed cmb_MediaType to keep a List<MediaType?> and got rid of Tuple<string, MediaType?>

* restored GetDiscType functionality

* fixed btn_StartStop enabled on EnsureCorrect... error

* fixed whitespace

* fixed indentation

* merged conflicts

* moved log window

* working on LogWindow resize/location behavior

* working on message logging in LogWindow

* added working integration between MenuItem for LogWindow and its visibility status, fixes issues with positioning and visibility, correctly bound Verbose flag to property

* finished integrating LogWindow, added some verbose logs

* added logs

* fixed include order

* added option to show log window at startup, removed useless QuietMode property from MainWindow
2018-07-13 15:48:35 -07:00
Matt Nadareski
2cc73a30f5 Protection Is Key (#92)
* Fix some protections, start adding IS scanning

* Fix IS CAB

* Update TODO
2018-07-10 10:23:52 -07:00
Matt Nadareski
be7d745d10 Create Parameters Class (#91)
* Update commented code

* Add Parameters class

This class is currently unused, but represents a set of parameters that can be passed to and from any given method. It has some copied/modified code from Validators.cs, for the time being due to the current overlap.

* Add better documentation

* Add and use DICFlag enumeration

* Port more things to Parameter-specific version

* Add commented code for later

* Added new options (#90)

* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* added 4 new options: quiet mode, paranoid mode, disable media type detect and c2 reread amount

* added default C2 reread tries to config

* fixed issues for PR

* removed commented leftover

* Update commented code

* Add Parameters class

This class is currently unused, but represents a set of parameters that can be passed to and from any given method. It has some copied/modified code from Validators.cs, for the time being due to the current overlap.

* Add better documentation

* Add and use DICFlag enumeration

* Port more things to Parameter-specific version

* Add commented code for later

* Use Parameters object

* Fix dumping process

* Cleanup Validators
2018-07-09 21:42:02 -07:00
Jacopo Santoni
d51adccab4 Added new options (#90)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* added 4 new options: quiet mode, paranoid mode, disable media type detect and c2 reread amount

* added default C2 reread tries to config

* fixed issues for PR

* removed commented leftover
2018-07-09 16:35:46 -07:00
Matt Nadareski
6141ce8539 Remove Disable Beep; Handle Error Case (#89)
* Disable beep not supported for these

* Remove testing code

This more or less ensures that the current run finishes properly

* Extra fixing for process issues

This, with the last 2 commits, make it so that geting disc speed works again. This was only found because in testing, many MANY instances of DiscImageCreator were made but never killed, polluting the process list. These issues should now be fixed.
2018-07-07 01:00:27 -07:00
Matt Nadareski
96d6e8bd2f Bits and Bobs (#88)
* Finally overhaul UI element naming

* Re-enable disk scan button

* Add cabfile scanning

* Fix csproj

* Fix TODO

* Add a couple more protections, disable one for now

* Fix SecuROM 7 detection

Thanks antimatter for helping test

* No beep for eject and speed check

* Fix Options after renames

* Ensure disc information in  couple places again
2018-07-07 00:08:22 -07:00
Matt Nadareski
c034df4266 More and more cleanup (#86)
* Namespace cleanups

* Make dump validation instanced

* Add note to Tasks

* Move stuff to DumpEnvironment

Most of the stuff in Tasks.cs acted on a single input parameter, namely a DumpEnvironment. Since that's the case, it was more logical to wrap those into DumpEnvironment and make them instance methods rather than keep them static. Due to this change, quite a few methods changed access, and some had to be marked internal due to wanting them to be tested separately.

* Gut Tasks

* One less thing in MainWindow

* Remove explicit cast

* Wrong check

* Create helper method

* Disable scan button on dump

* Remove unnecessary getters/setters

* Method name/description cleanup

* Address TODO

* Namespace cleanups

* Make dump validation instanced

* Add note to Tasks

* Move stuff to DumpEnvironment

Most of the stuff in Tasks.cs acted on a single input parameter, namely a DumpEnvironment. Since that's the case, it was more logical to wrap those into DumpEnvironment and make them instance methods rather than keep them static. Due to this change, quite a few methods changed access, and some had to be marked internal due to wanting them to be tested separately.

* Gut Tasks

* One less thing in MainWindow

* Remove explicit cast

* Wrong check

* Create helper method

* Disable scan button on dump

* Remove unnecessary getters/setters

* Method name/description cleanup

* Address TODO

* Clean up OnContentRendered

* Namespace cleanups

* Make dump validation instanced

* Add note to Tasks

* Move stuff to DumpEnvironment

Most of the stuff in Tasks.cs acted on a single input parameter, namely a DumpEnvironment. Since that's the case, it was more logical to wrap those into DumpEnvironment and make them instance methods rather than keep them static. Due to this change, quite a few methods changed access, and some had to be marked internal due to wanting them to be tested separately.

* Gut Tasks

* One less thing in MainWindow

* Remove explicit cast

* Wrong check

* Create helper method

* Disable scan button on dump

* Remove unnecessary getters/setters

* Method name/description cleanup

* Address TODO

* Clean up OnContentRendered

* Update event handlers
2018-07-05 21:34:04 -07:00
ReignStumble
fe04c6df73 Centered Window + No more resizable 2018-07-06 00:11:11 -04:00
ReignStumble
aff195eb71 Added Exit and About 2018-07-06 00:08:20 -04:00
ReignStumble
3aca999b63 New Menubar 2018-07-06 00:00:16 -04:00
ReignStumble
0e7ce76f01 Wait for UI to render before Populating 2018-07-05 23:43:01 -04:00
Matt Nadareski
6d72417512 Cleanups and mixups (#85)
Cleanups and Mixups
2018-07-05 16:52:34 -07:00
Matt Nadareski
ac89f06843 Separate out from Constants.cs 2018-07-05 13:33:08 -07:00
Matt Nadareski
39ce56d579 Post-merge cleanup 2018-07-05 13:18:01 -07:00
Matt Nadareski
9dbd30adba Merge DumpEnvironment/DumpInformation; Add Copy Protection Scan (#82)
* Merge DumpEnvironment and DumpInformation

* None of these need to be static

* Start adding protection checks

* Convert all BurnOut-related files from VB

* Placeholders for SafeDisc 3 and 4

* Fix some small things

* SubIntention not always output

* Make copy protect scan optional

* Try/catch on disc information to avoid crashes

* Add placeholders for Origin, Steam, UPlay

* Don't use copied method, for safety

* Protection cleanup

* More cleanup on SafeDisc v1/2

* Add area for cabfiles (for later); clean up more protections

* Comment out cabfile path, for now

* Underscores? Really?

* Namespace cleanup

* More Steam variations

* Add an actual UPlay installer name

* Skeleton for GFWL

* Add one for Origin

* More Steam, update note

* Add note kinda like GFWL

* Fix Origin installer name (IDK what I put there before...)

* Remove TODOs for ones with valid checks

* Add first GFWL installer name

* Move online services to last

Physical disc protections should ALWAYS come before an online service is mentioned. This makes sure that the physical media's information is not lost

* Add another TODO

* ...slightly longer comment

* Fix CDCops, start adding file lists

* More paths

* Even more paths

* Last paths for now

* Dictionary is smarter... so more work ahead

* Remind me why I'm doing this?

* Okay, recreated in dictionary now

* Reorganiation

* Alphabetization is fun

* Add unused call to new scan path

* Add TODO

* Remove TODO

* Remove TODO, comment out dummy files check

* Better state for no detected protection

* Make copy protect scan a Task so it can be used elsehwere later

* ScanEx -> Scan; cleanup

* Remove unused protection scan methods

These methods used to be a simple check for if a particular file existed. These are now captured in the "CreateProtectionMapping" dictionary

* Remove SuffixInStr

* Characters to literals, decimal to hex, start of consolidation

* Add remaining string-only mappings, cleanup

* Update summaries, remove unused flag

* Trim out unnecessary variables

These variables used to be used when a DLL or an EXE was found to have something in it but other files were needed to check against for better output. This also assumed a monolithic output in what protections were found. Now that we just list everything and all files are scanned, this should catch everything that comes through

* Remove unused path-based protections

* Accidentally wiped out Sysiphus

* CD Check is... special

* No more internal vars; static shock

* Add region tags

* Remove unused Dummy Files check

* Add scan for copy protection without dump

* Better checking of files

* Better message box

* Add TODO

* Clearer language for SafeDisc

* Fix SafeDisc scanning

Thanks to eientei95 for helping find this one

* Slight EVORE cleanup

* Fix SolidShield

Thanks to eientei95 for helping to fix this one

* Add sector scan note

* Add user-requested TODO

* Refactor of ComboBox underlying types (#84)

* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* Refactored KnownSystem combobox management

- created KnownSystemComboBoxItem to manage both header and system items
- totally rewrote KnownSystem category (through KnownSystemCategory enum and markers)
- fixed null access in EnsureDiscInformation caused by null _drives
- rewrote cmb_SystemType management to use new classes

* - created Drive class to keep drive letters, volume label and is floppy flag altogether
- changed all the code to use the new Drive class in combobox and in DumpEnvironment

* fixed retrieval of value from cmb_KnownSystem combobox

* removed OrderedDictionary, not needed anymore

* Merge DumpEnvironment and DumpInformation

* None of these need to be static

* Start adding protection checks

* Convert all BurnOut-related files from VB

* Placeholders for SafeDisc 3 and 4

* Fix some small things

* SubIntention not always output

* Make copy protect scan optional

* Try/catch on disc information to avoid crashes

* Add placeholders for Origin, Steam, UPlay

* Don't use copied method, for safety

* Protection cleanup

* More cleanup on SafeDisc v1/2

* Add area for cabfiles (for later); clean up more protections

* Comment out cabfile path, for now

* Underscores? Really?

* Namespace cleanup

* More Steam variations

* Add an actual UPlay installer name

* Skeleton for GFWL

* Add one for Origin

* More Steam, update note

* Add note kinda like GFWL

* Fix Origin installer name (IDK what I put there before...)

* Remove TODOs for ones with valid checks

* Add first GFWL installer name

* Move online services to last

Physical disc protections should ALWAYS come before an online service is mentioned. This makes sure that the physical media's information is not lost

* Add another TODO

* ...slightly longer comment

* Fix CDCops, start adding file lists

* More paths

* Even more paths

* Last paths for now

* Dictionary is smarter... so more work ahead

* Remind me why I'm doing this?

* Okay, recreated in dictionary now

* Reorganiation

* Alphabetization is fun

* Add unused call to new scan path

* Add TODO

* Remove TODO

* Remove TODO, comment out dummy files check

* Better state for no detected protection

* Make copy protect scan a Task so it can be used elsehwere later

* ScanEx -> Scan; cleanup

* Remove unused protection scan methods

These methods used to be a simple check for if a particular file existed. These are now captured in the "CreateProtectionMapping" dictionary

* Remove SuffixInStr

* Characters to literals, decimal to hex, start of consolidation

* Add remaining string-only mappings, cleanup

* Update summaries, remove unused flag

* Trim out unnecessary variables

These variables used to be used when a DLL or an EXE was found to have something in it but other files were needed to check against for better output. This also assumed a monolithic output in what protections were found. Now that we just list everything and all files are scanned, this should catch everything that comes through

* Remove unused path-based protections

* Accidentally wiped out Sysiphus

* CD Check is... special

* No more internal vars; static shock

* Add region tags

* Remove unused Dummy Files check

* Add scan for copy protection without dump

* Better checking of files

* Better message box

* Add TODO

* Clearer language for SafeDisc

* Fix SafeDisc scanning

Thanks to eientei95 for helping find this one

* Slight EVORE cleanup

* Fix SolidShield

Thanks to eientei95 for helping to fix this one

* Add sector scan note

* Add user-requested TODO
2018-07-05 12:58:20 -07:00
Jacopo Santoni
48de63513e Refactor of ComboBox underlying types (#84)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* Refactored KnownSystem combobox management

- created KnownSystemComboBoxItem to manage both header and system items
- totally rewrote KnownSystem category (through KnownSystemCategory enum and markers)
- fixed null access in EnsureDiscInformation caused by null _drives
- rewrote cmb_SystemType management to use new classes

* - created Drive class to keep drive letters, volume label and is floppy flag altogether
- changed all the code to use the new Drive class in combobox and in DumpEnvironment

* fixed retrieval of value from cmb_KnownSystem combobox

* removed OrderedDictionary, not needed anymore
2018-07-05 11:30:52 -07:00
Matt Nadareski
56eaf7c2c5 Add Test Project (#79)
* Add Test project; Migrate so there's not an external folder

* Add Test project; Migrate so that there's not an external folder

* Fix build; Add remaining test classes (skeletons)

* Fix more paths

* Make unit tests runnable, fix issue found by unit tests

* Add more tests, fix more things found by tests

* Add skeleton for ValidatorsTest, fix OrderedDictionary

* Add UIElementsTest

* Add new test classes, slightly update Result

* Implement DumpEnvirionment tests; fix minor things from testing

* Add Tasks testing skeletons; Reorder methods and change access modifiers for some

* Update notes for DumpInformation tests

* Implement a couple Validators tests; fix ValidateParameters

* Implement DetermineFlagsTest

* Make one test more readable

* More cleanup around ValidateParameters

* Split check

* WiiU is not supported by /raw command

* Add TODO question

* Update TODO with a plan
2018-06-28 19:41:18 -07:00
Matt Nadareski
c1bd30e008 Streamline Callbacks and Ensure MediaType (#78)
* Separate drive speeds; better ensuring of media type

This is the first attempt at separating out the drive speeds into separate categories. At the moment, it seems to be working just fine for CD and DVD.
This also includes some updated code to better ensure that the detected (or selected if the user has changed it) media type is kept between all of the regular changes

* Multiple medium

* Add more drive speed prototyping

* Strip out redundant calls

* Separate drive speeds; better ensuring of media type

This is the first attempt at separating out the drive speeds into separate categories. At the moment, it seems to be working just fine for CD and DVD.
This also includes some updated code to better ensure that the detected (or selected if the user has changed it) media type is kept between all of the regular changes

* Multiple medium

* Add more drive speed prototyping

* Strip out redundant calls

* Streamline merge

* Comment patrol; keep BluRay a first-class citizen

* HD-DVD is supported?!
2018-06-27 23:21:52 -07:00
Jacopo Santoni
4176f22d79 Added preferred dump speed option (#77)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* added max dump speed option
- added slider element into OptionsWindow
- moved drive speeds to UIElements from MainWindow
- added setting to Options which is saved

still need to understand how to manage DVD/CD-ROM different speeds

* kept working on max dump speed settings

- rewrote max speed list management in Options
- added second slider in OptionsWindow
- added callback from OptionsWindow to MainWindow on updated options

* final tweaks before PR

* renamed maxDumpSpeed to preferredDumpSpeed

* restored SetCurrentDiscType functionality

* fixes for PR

* fixes for PR
2018-06-27 20:06:19 -07:00
Matt Nadareski
1e16e952cf Update for 1.07 2018-06-27 10:35:40 -07:00
Matt Nadareski
9f493ae27a One last fix for drive speed finding
DIC can sometimes report that a drive has a 0x speed, causing this automatic finder to fail in those cases
2018-06-27 10:35:29 -07:00
Matt Nadareski
2021230836 Quick fix to account for possibly odd speeds 2018-06-27 00:21:53 -07:00
Matt Nadareski
12ba5702fd Minor updates
This mostly takes care of adding a ton of comments and descriptions. Other minor changes include:
- Removing last remnants of psxt001z
- Capitalizing public variables
- Fixing the build that I stupidly broke
- Ensuring whitespace-named discs are handled
- Simplified DumpEnvironment init
2018-06-26 21:08:26 -07:00
Jacopo Santoni
2cf083eb89 Refactor of dump procedure into smaller components (#76)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* created Tasks file\nmoved some methods there, created DumpEnvironment to manage are arguments

* moved additional methods to Tasks to make it more modular

* changed StartDumping flow to avoid returning if extra tools are not found

* moved main dump workflow into Tasks class

* created specific DumpResult class for better error reporting/management

* fixes

* continued refactor
- split EnsureDiscInformation into an additional EnsureCorrectInformationForSystemAndMediaType
- proof of concept of using custom extensions to enum types to give better functionality (and encapsulation)
- changed cmb_MediaType to keep a List<MediaType?> and got rid of Tuple<string, MediaType?>

* restored GetDiscType functionality

* fixed btn_StartStop enabled on EnsureCorrect... error

* fixed whitespace

* fixed indentation

* fixes for PR
2018-06-26 20:18:37 -07:00
Matt Nadareski
c1f22d47dc Remove Redundant Calls and PSX Automation (#75)
* Work with callbacks to increase perf

* Make EDC field for PSX automatic

* Make AntiMod field for PSX automatic

* Make LibCrypt field for PSX automatic

* Remove psxt001z

Now that we have confirmed that DIC outputs the required information for libcrypt, we no longer need the external call to psxt001z to confirm (output was unused anyway)

* We need the subIntention data for LibCrypt

* Let's avoid null for now

* Set default speed in case of error

* Check the layerbreak better

* Remove extraneous header checking

* Add SubIntention field if it exists, always

* Add LibCrypt flag by default for PSX

* SubIntention needs a newline
2018-06-26 10:35:58 -07:00
Matt Nadareski
11287d081d Add system requirements (fixes #72) 2018-06-25 10:18:06 -07:00
Matt Nadareski
8652af5697 Usability Updates (#73)
* Checkpoint

* Add notes

* Rename Validation -> Validators

* Move data to Data folder

* Get current disk type

* Automatically detect disc type

* Comment out WIP code

* Add more prototype

The hope is that having this unhooked prototype code will get either myself or another contributor the right inspiration to get it going properly
2018-06-25 10:00:06 -07:00
Matt Nadareski
69561cb1a0 Bits and Pieces (#70)
* DiscType -> MediaType

* Fix bulk find/replace

* Add OrderedDictionary

* Stage 1 of moving off of Tuples

* Add CHANGELIST.md

* Stage 2 of Tuple removal

* String replacement for output paths

* Stage 3 of Tuple removal

* Slight reordering
2018-06-21 11:46:14 -07:00
Matt Nadareski
d587d2b4b3 Add system and media type to output submission info (#65)
* Add new constants to the template

* Slight reordering

* Add new fields to the output

* Whitespace!

* Minor formatting cleanup

* Add TODO

* Whitepace, 2.0
2018-06-20 22:00:26 -07:00
Matt Nadareski
a19418e46f Remove sg-raw (#64)
* Remove sg-raw from OptionsWindow.xaml.cs

* Remove sg-raw from OptionsWindow.xaml

* Remove sg-raw from Options.cs

* Remove sg-raw from MainWindow.xaml.cs

* Missed the other rows

* Remove sg-raw from App.config
2018-06-20 12:44:39 -07:00
Jacopo Santoni
de22ead07c Enhancement of options management (#63)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox

* added OptionsFrame that will manage all applicaton settings and implemented path browse buttons

* removed old properties code, added Options to manage all the program options, implemented interactions with OptionsWindow

* fixed margins automatically inserted by MSVS

* fixed margins automatically inserted by MSVS

* removed empty method placed by XAML designer

* added closed callback for OptionsWindows, tweaks

* fixed button in toolbar
2018-06-20 12:30:31 -07:00
Matt Nadareski
f777869103 Formats and Types (#58)
* Combine single/dual-layer disc types

* Add silly formats

* Minor tweaks to disc type population

* Make codepath slightly less complex

* Fix null error on dump

* Update strings that looked wrong in output
2018-06-20 11:38:12 -07:00
reignstumble
aec1131271 Update README.md 2018-06-20 12:23:01 -04:00
Jacopo Santoni
308fad3ed2 Separated of System and Disc Type (#56)
* Split type combobox into system combobox and disc type combobox

* corrected indentation for xaml file

* fixed merge with head

* fixed format

* fixed issues for PR, added KnownSystem.CUSTOM

* removed Updater.cs which ended by error in commit

* fixed GetOuptutName() for new drive/system combobox
2018-06-18 12:43:07 -07:00
121 changed files with 43934 additions and 4152 deletions

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
[*.cs]
# SYSLIB1045: Convert to 'GeneratedRegexAttribute'.
dotnet_diagnostic.SYSLIB1045.severity = silent

View File

@@ -0,0 +1,28 @@
---
name: Feature Request
about: For when you know better than me what you want
title: "[Request]"
labels: enhancement
assignees: mnadareski
---
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
If none of those apply, then continue...
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

22
.github/ISSUE_TEMPLATE/informational.md vendored Normal file
View File

@@ -0,0 +1,22 @@
---
name: Info
about: Something you need to tell me
title: "[Info]"
labels: question
assignees: mnadareski
---
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
If none of those apply, then continue...
**Is your information related to one of the dumping programs supported or something that isn't a bug in the code? Please describe.**
A clear and concise description of what the information is. Ex. With the latest build of DumpingProgram, it [...]
**Additional context**
Add any other context or screenshots about the information here.

49
.github/ISSUE_TEMPLATE/issue-report.md vendored Normal file
View File

@@ -0,0 +1,49 @@
---
name: Issue Report
about: Tell me what's wrong, seriously
title: "[Problem]"
labels: bug
assignees: mnadareski
---
**Before You Submit**
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the issue has already been addressed.
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
- Check multiple discs to help narrow down the issue
- Check the Options to see if changing any of those affects your issue.
If all of those fail, then continue...
**Version**
What version are you using?
- [ ] Stable release (version here)
- [ ] WIP release (version here)
**Build**
What runtime version are you using?
- [ ] .NET 6.0 running on (Operating System)
- [ ] .NET 8.0 running on (Operating System)
**Describe the issue**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

0
.gitmodules vendored Normal file
View File

27
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,27 @@
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
"version": "0.2.0",
"configurations": [
{
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/MPF.Check/bin/Debug/net6.0/MPF.Check.dll",
"args": [],
"cwd": "${workspaceFolder}/MPF.Check",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach",
"processId": "${command:pickProcess}"
}
]
}

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

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

28
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,28 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/MPF.sln",
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"${workspaceFolder}/MPF/MPF.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="dicPath" value="Programs\DiscImageCreator.exe"/>
<add key="psxt001zPath" value="psxt001z.exe"/>
<add key="sgRawPath" value="sg_raw.exe"/>
<add key="subdumpPath" value="subdump.exe"/>
<add key="defaultOutputPath" value="ISO"/>
</appSettings>
</configuration>

View File

@@ -1,9 +0,0 @@
<Application x:Class="DICUI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DICUI"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>

View File

@@ -1,17 +0,0 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
namespace DICUI
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
}
}

1218
CHANGELIST.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,119 +0,0 @@
namespace DICUI
{
/// <summary>
/// Text for UI elements
/// </summary>
public static class UIElements
{
public const string StartDumping = "Start Dumping";
public const string StopDumping = "Stop Dumping";
}
/// <summary>
/// Top-level commands for DiscImageCreator
/// </summary>
public static class DICCommands
{
public const string Audio = "audio";
public const string BluRay = "bd";
public const string Close = "close";
public const string CompactDisc = "cd";
public const string Data = "data";
public const string DigitalVideoDisc = "dvd";
public const string DriveSpeed = "ls";
public const string Eject = "eject";
public const string Floppy = "fd";
public const string GDROM = "gd";
public const string MDS = "mds";
public const string Reset = "reset";
public const string Start = "start";
public const string Stop = "stop";
public const string Sub = "sub";
public const string Swap = "swap";
public const string XBOX = "xbox";
}
/// <summary>
/// Dumping flags for DiscImageCreator
/// </summary>
public static class DICFlags
{
public const string AddOffset = "/a";
public const string AMSF = "/p";
public const string BEOpcode = "/be";
public const string C2Opcode = "/c2";
public const string CMI = "/c";
public const string D8Opcode = "/d8";
public const string DisableBeep = "/q";
public const string ForceUnitAccess = "/f";
public const string MCN = "/m";
public const string MultiSession = "/ms";
public const string NoFixSubP = "/np";
public const string NoFixSubQ = "/nq";
public const string NoFixSubQLibCrypt = "/nl";
public const string NoFixSubQSecuROM = "/ns";
public const string NoFixSubRtoW = "/nr";
public const string Raw = "/raw";
public const string Reverse = "/r";
public const string ScanAntiMod = "/am";
public const string ScanFileProtect = "/sf";
public const string ScanSectorProtect = "/ss";
public const string SeventyFour = "/74";
public const string SubchannelReadLevel = "/s";
}
/// <summary>
/// Template field values for submission info
/// </summary>
public static class Template
{
// Manual information
public const string TitleField = "Title";
public const string DiscNumberField = "Disc Number / Letter";
public const string DiscTitleField = "Disc Title";
public const string CategoryField = "Category";
public const string RegionField = "Region";
public const string LanguagesField = "Languages";
public const string DiscSerialField = "Disc Serial";
public const string BarcodeField = "Barcode";
public const string ISBNField = "ISBN";
public const string CommentsField = "Comments";
public const string ContentsField = "Contents";
public const string VersionField = "Version";
public const string EditionField = "Edition/Release";
public const string CopyProtectionField = "Copy Protection";
public const string MasteringRingField = "Mastering Ring";
public const string MasteringSIDField = "Mastering SID Code";
public const string MouldSIDField = "Mould SID Code";
public const string AdditionalMouldField = "Additional Mould";
public const string ToolstampField = "Toolstamp or Mastering Code";
// Automatic Information
public const string PVDField = "Primary Volume Descriptor (PVD)";
public const string DATField = "DAT";
public const string ErrorCountField = "Error Count";
public const string CuesheetField = "Cuesheet";
public const string SubIntentionField = "SubIntention (SecuROM)";
public const string WriteOffsetField = "WriteOffset";
public const string LayerbreakField = "Layerbreak";
public const string PlaystationEXEDateField = "EXE Date";
public const string PlayStationEDCField = "EDC"; // TODO: Not automatic yet
public const string PlayStationAntiModchipField = "Anti-modchip"; // TODO: Not automatic yet
public const string PlayStationLibCryptField = "LibCrypt"; // TODO: Not automatic yet
public const string SaturnHeaderField = "Header";
public const string SaturnBuildDateField = "Build Date";
public const string XBOXDMIHash = "DMI.bin Hashes";
public const string XBOXPFIHash = "PFI.bin Hashes";
public const string XBOXSSHash = "SS.bin Hashes";
public const string XBOXSSRanges = "Security Sector Ranges";
// Default values
public const string RequiredValue = "(REQUIRED)";
public const string RequiredIfExistsValue = "(REQUIRED, IF EXISTS)";
public const string OptionalValue = "(OPTIONAL)";
public const string YesNoValue = "Yes/No";
}
}

View File

@@ -1,135 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{7B1B75EB-8940-466F-BD51-76471A57F9BE}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>DICUI</RootNamespace>
<AssemblyName>DICUI</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<IsWebBootstrapper>false</IsWebBootstrapper>
<PublishUrl>C:\Users\admin\Desktop\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>1</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<PublishWizardCompleted>true</PublishWizardCompleted>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Icon.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<ManifestCertificateThumbprint>654CEE5FEAF46C8C1C369D7ED34DA157828A8D2F</ManifestCertificateThumbprint>
</PropertyGroup>
<PropertyGroup>
<ManifestKeyFile>WpfApp1_TemporaryKey.pfx</ManifestKeyFile>
</PropertyGroup>
<PropertyGroup>
<GenerateManifests>true</GenerateManifests>
</PropertyGroup>
<PropertyGroup>
<SignManifests>false</SignManifests>
</PropertyGroup>
<PropertyGroup />
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Management" />
<Reference Include="System.Management.Instrumentation" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="Constants.cs" />
<Compile Include="Utilities\DumpInformation.cs" />
<Compile Include="Utilities\Validation.cs" />
<Compile Include="Utilities\Converters.cs" />
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Enumerations.cs" />
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.6.1">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.6.1 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<Resource Include="Icon.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,25 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2036
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DICUI", "DICUI.csproj", "{7B1B75EB-8940-466F-BD51-76471A57F9BE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7B1B75EB-8940-466F-BD51-76471A57F9BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7B1B75EB-8940-466F-BD51-76471A57F9BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7B1B75EB-8940-466F-BD51-76471A57F9BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7B1B75EB-8940-466F-BD51-76471A57F9BE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {73C62E6A-6584-4D93-83B5-ECB1FBDB469B}
EndGlobalSection
EndGlobal

View File

@@ -1,155 +0,0 @@
namespace DICUI
{
/// <summary>
/// Known disc types
/// </summary>
public enum DiscType
{
NONE = 0,
CD,
DVD5,
DVD9,
GDROM,
HDDVD,
BD25,
BD50,
// Special Formats
GameCubeGameDisc,
WiiOpticalDisc,
WiiUOpticalDisc,
UMD,
// Keeping this separate since it's currently unsupported in the UI
Floppy = 99,
}
/// <summary>
/// Known systems
/// </summary>
/// <remarks>Ensure that Utilities methods are updated as well</remarks>
public enum KnownSystem
{
NONE = 0,
#region Consoles
BandaiPlaydiaQuickInteractiveSystem,
BandaiApplePippin,
CommodoreAmigaCD32,
CommodoreAmigaCDTV,
MattelHyperscan,
MicrosoftXBOX,
MicrosoftXBOX360XDG2,
MicrosoftXBOX360XDG3,
MicrosoftXBOXOne,
NECPCEngineTurboGrafxCD,
NECPCFX,
NintendoGameCube,
NintendoWii,
NintendoWiiU,
Panasonic3DOInteractiveMultiplayer,
PhilipsCDi,
SegaCDMegaCD,
SegaDreamcast,
SegaSaturn,
SNKNeoGeoCD,
SonyPlayStation,
SonyPlayStation2,
SonyPlayStation3,
SonyPlayStation4,
SonyPlayStationPortable,
VMLabsNuon,
VTechVFlashVSmilePro,
ZAPiTGamesGameWaveFamilyEntertainmentSystem,
#endregion
#region Computers
AcornArchimedes,
AppleMacintosh,
CommodoreAmigaCD,
FujitsuFMTowns,
IBMPCCompatible,
NECPC88,
NECPC98,
SharpX68000,
#endregion
#region Arcade
AmigaCUBOCD32,
AmericanLaserGames3DO,
Atari3DO,
Atronic,
AUSCOMSystem1,
BallyGameMagic,
CapcomCPSystemIII,
GlobalVRVarious,
GlobalVRVortek,
GlobalVRVortekV3,
ICEPCHardware,
IncredibleTechnologiesEagle,
IncredibleTechnologiesVarious,
KonamiFirebeat,
KonamiGVSystem,
KonamiM2,
KonamiPython,
KonamiPython2,
KonamiSystem573,
KonamiTwinkle,
KonamiVarious,
MeritIndustriesBoardwalk,
MeritIndustriesMegaTouchAurora,
MeritIndustriesMegaTouchForce,
MeritIndustriesMegaTouchION,
MeritIndustriesMegaTouchMaxx,
MeritIndustriesMegaTouchXL,
NamcoCapcomSystem256,
NamcoCapcomTaitoSystem246,
NamcoSegaNintendoTriforce,
NamcoSystem12,
NamcoSystem357,
NewJatreCDi,
NichibutsuHighRateSystem,
NichibutsuSuperCD,
NichibutsuXRateSystem,
PhotoPlayVarious,
RawThrillsVarious,
SegaChihiro,
SegaEuropaR,
SegaLindbergh,
SegaNaomi,
SegaNaomi2,
SegaNu,
SegaRingEdge,
SegaRingEdge2,
SegaRingWide,
SegaSTV,
SegaSystem32,
SeibuCATSSystem,
TABAustriaQuizard,
TandyMemorexVisualInformationSystem,
TsunamiTsuMoMultiGameMotionSystem,
#endregion
#region Other
AudioCD,
BDVideo,
DVDVideo,
EnhancedCD,
PalmOS,
PhilipsCDiDigitalVideo,
PhotoCD,
PlayStationGameSharkUpdates,
TaoiKTV,
TomyKissSite,
VideoCD,
#endregion
}
}

View File

@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
DICUI Copyright (C) 2018 ReignStumble
MPF Copyright (C) 2018 ReignStumble
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

6
MPF.Check/App.config Normal file
View File

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

View File

@@ -0,0 +1,44 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
<OutputType>Exe</OutputType>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.1</VersionPrefix>
<!-- Package Properties -->
<Title>MPF Check</Title>
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Validator for various dumping programs</Description>
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.2" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
</ItemGroup>
<ItemGroup>
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
</ItemGroup>
</Project>

116
MPF.Check/Program.cs Normal file
View File

@@ -0,0 +1,116 @@
using System;
using System.IO;
using BinaryObjectScanner;
using MPF.Core;
using MPF.Core.Data;
using MPF.Core.Utilities;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
namespace MPF.Check
{
public class Program
{
public static void Main(string[] args)
{
// Try processing the standalone arguments
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
if (standaloneProcessed != false)
{
if (standaloneProcessed == null)
DisplayHelp();
return;
}
// Try processing the common arguments
(bool success, MediaType mediaType, RedumpSystem? knownSystem, var error) = OptionsLoader.ProcessCommonArguments(args);
if (!success)
{
DisplayHelp(error);
return;
}
// Loop through and process options
(var options, var seedInfo, var 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>();
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
// Validate the supplied credentials
#if NETFRAMEWORK
(bool? _, string? message) = RedumpWebClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty);
#else
(bool? _, string? message) = RedumpHttpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
if (!string.IsNullOrEmpty(message))
Console.WriteLine(message);
// Loop through all the rest of the args
for (int i = startIndex; i < args.Length; i++)
{
// Check for a file
if (!File.Exists(args[i].Trim('"')))
{
DisplayHelp($"{args[i].Trim('"')} does not exist");
return;
}
// Get the full file path
string filepath = Path.GetFullPath(args[i].Trim('"'));
// Now populate an environment
Drive? drive = null;
if (!string.IsNullOrEmpty(path))
drive = Drive.Create(null, path!);
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, internalProgram: null, parameters: null);
// Finally, attempt to do the output dance
#if NET40
var resultTask = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress);
resultTask.Wait();
var result = resultTask.Result;
#else
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
Console.WriteLine(result.Message);
}
}
/// <summary>
/// Display help for MPF.Check
/// </summary>
/// <param name="error">Error string to prefix the help text with</param>
private static void DisplayHelp(string? error = null)
{
if (error != null)
Console.WriteLine(error);
Console.WriteLine("Usage:");
Console.WriteLine("MPF.Check.exe <mediatype> <system> [options] </path/to/output.cue/iso> ...");
Console.WriteLine();
Console.WriteLine("Standalone Options:");
Console.WriteLine("-h, -? Show this help text");
Console.WriteLine("-lm, --listmedia List supported media types");
Console.WriteLine("-ls, --listsystems List supported system types");
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");
Console.WriteLine();
Console.WriteLine("Check Options:");
var supportedArguments = OptionsLoader.PrintSupportedArguments();
foreach (string argument in supportedArguments)
{
Console.WriteLine(argument);
}
Console.WriteLine();
}
}
}

View File

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

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

@@ -0,0 +1,25 @@
using System;
using BinaryObjectScanner;
using MPF.Core.Data;
namespace MPF.Core
{
public static class ConsoleLogger
{
/// <summary>
/// Simple process counter to write to console
/// </summary>
public static void ProgressUpdated(object? sender, Result value)
{
Console.WriteLine(value.Message);
}
/// <summary>
/// Simple process counter to write to console
/// </summary>
public static void ProgressUpdated(object? sender, ProtectionProgress value)
{
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
}
}
}

View File

@@ -0,0 +1,298 @@
using System;
#if NET20 || NET35
using System.Collections.Generic;
#else
using System.Collections.Concurrent;
#endif
using System.IO;
using System.Reflection;
using MPF.Core.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Converters
{
public static class EnumConverter
{
#region Cross-enumeration conversions
/// <summary>
/// Convert drive type to internal version, if possible
/// </summary>
/// <param name="driveType">DriveType value to check</param>
/// <returns>InternalDriveType, if possible, null on error</returns>
public static InternalDriveType? ToInternalDriveType(this DriveType driveType)
{
return driveType switch
{
DriveType.CDRom => (InternalDriveType?)InternalDriveType.Optical,
DriveType.Fixed => (InternalDriveType?)InternalDriveType.HardDisk,
DriveType.Removable => (InternalDriveType?)InternalDriveType.Removable,
_ => null,
};
}
#endregion
#region Convert to Long Name
/// <summary>
/// Long name method cache
/// </summary>
#if NET20 || NET35
private static readonly Dictionary<Type, MethodInfo?> LongNameMethods = [];
#else
private static readonly ConcurrentDictionary<Type, MethodInfo?> LongNameMethods = [];
#endif
/// <summary>
/// Get the string representation of a generic enumerable value
/// </summary>
/// <param name="value">Enum value to convert</param>
/// <returns>String representation of that value if possible, empty string on error</returns>
public static string GetLongName(Enum value)
{
try
{
var sourceType = value.GetType();
sourceType = Nullable.GetUnderlyingType(sourceType) ?? sourceType;
if (!LongNameMethods.TryGetValue(sourceType, out var method))
{
method = typeof(Extensions).GetMethod("LongName", [typeof(Nullable<>).MakeGenericType(sourceType)]);
method ??= typeof(EnumConverter).GetMethod("LongName", [typeof(Nullable<>).MakeGenericType(sourceType)]);
#if NET20 || NET35
LongNameMethods[sourceType] = method;
#else
LongNameMethods.TryAdd(sourceType, method);
#endif
}
if (method != null)
return method.Invoke(null, new[] { value }) as string ?? string.Empty;
else
return string.Empty;
}
catch
{
// Converter is not implemented for the given type
return string.Empty;
}
}
/// <summary>
/// Get the string representation of the InternalProgram enum values
/// </summary>
/// <param name="prog">InternalProgram value to convert</param>
/// <returns>String representing the value, if possible</returns>
public static string LongName(this InternalProgram? prog)
{
return (prog) switch
{
#region Dumping support
InternalProgram.Aaru => "Aaru",
InternalProgram.DiscImageCreator => "DiscImageCreator",
InternalProgram.Redumper => "Redumper",
#endregion
#region Verification support only
InternalProgram.CleanRip => "CleanRip",
InternalProgram.DCDumper => "DCDumper",
InternalProgram.UmdImageCreator => "UmdImageCreator",
#endregion
InternalProgram.NONE => "Unknown",
_ => "Unknown",
};
}
#endregion
#region Convert From String
/// <summary>
/// Get the InternalProgram enum value for a given string
/// </summary>
/// <param name="internalProgram">String value to convert</param>
/// <returns>InternalProgram represented by the string, if possible</returns>
public static InternalProgram ToInternalProgram(string? internalProgram)
{
return (internalProgram?.ToLowerInvariant()) switch
{
// Dumping support
"aaru"
or "chef"
or "dichef"
or "discimagechef" => InternalProgram.Aaru,
"creator"
or "dic"
or "dicreator"
or "discimagecreator" => InternalProgram.DiscImageCreator,
"rd"
or "redumper" => InternalProgram.Redumper,
// Verification support only
"cleanrip"
or "cr" => InternalProgram.CleanRip,
"dc"
or "dcd"
or "dcdumper" => InternalProgram.DCDumper,
"uic"
or "umd"
or "umdcreator"
or "umdimagecreator" => InternalProgram.UmdImageCreator,
_ => InternalProgram.NONE,
};
}
/// <summary>
/// Get the MediaType enum value for a given string
/// </summary>
/// <param name="type">String value to convert</param>
/// <returns>MediaType represented by the string, if possible</returns>
public static MediaType ToMediaType(string type)
{
return (type.ToLowerInvariant()) switch
{
#region Punched Media
"aperture"
or "aperturecard"
or "aperture card" => MediaType.ApertureCard,
"jacquardloom"
or "jacquardloomcard"
or "jacquard loom card" => MediaType.JacquardLoomCard,
"magneticstripe"
or "magneticstripecard"
or "magnetic stripe card" => MediaType.MagneticStripeCard,
"opticalphone"
or "opticalphonecard"
or "optical phonecard" => MediaType.OpticalPhonecard,
"punchcard"
or "punchedcard"
or "punched card" => MediaType.PunchedCard,
"punchtape"
or "punchedtape"
or "punched tape" => MediaType.PunchedTape,
#endregion
#region Tape
"openreel"
or "openreeltape"
or "open reel tape" => MediaType.OpenReel,
"datacart"
or "datacartridge"
or "datatapecartridge"
or "data tape cartridge" => MediaType.DataCartridge,
"cassette"
or "cassettetape"
or "cassette tape" => MediaType.Cassette,
#endregion
#region Disc / Disc
"bd"
or "bdrom"
or "bd-rom"
or "bluray" => MediaType.BluRay,
"cd"
or "cdrom"
or "cd-rom" => MediaType.CDROM,
"dvd"
or "dvd5"
or "dvd-5"
or "dvd9"
or "dvd-9"
or "dvdrom"
or "dvd-rom" => MediaType.DVD,
"fd"
or "floppy"
or "floppydisk"
or "floppy disk"
or "floppy diskette" => MediaType.FloppyDisk,
"floptical" => MediaType.Floptical,
"gd"
or "gdrom"
or "gd-rom" => MediaType.GDROM,
"hddvd"
or "hd-dvd"
or "hddvdrom"
or "hd-dvd-rom" => MediaType.HDDVD,
"hdd"
or "harddisk"
or "hard disk" => MediaType.HardDisk,
"bernoullidisk"
or "iomegabernoullidisk"
or "bernoulli disk"
or "iomega bernoulli disk" => MediaType.IomegaBernoulliDisk,
"jaz"
or "iomegajaz"
or "iomega jaz" => MediaType.IomegaJaz,
"zip"
or "zipdisk"
or "iomegazip"
or "iomega zip" => MediaType.IomegaZip,
"ldrom"
or "lvrom"
or "ld-rom"
or "lv-rom"
or "laserdisc"
or "laservision"
or "ld-rom / lv-rom" => MediaType.LaserDisc,
"64dd"
or "n64dd"
or "64dddisk"
or "n64dddisk"
or "64dd disk"
or "n64dd disk" => MediaType.Nintendo64DD,
"fds"
or "famicom"
or "nfds"
or "nintendofamicom"
or "famicomdisksystem"
or "famicom disk system"
or "famicom disk system disk" => MediaType.NintendoFamicomDiskSystem,
"gc"
or "gamecube"
or "nintendogamecube"
or "nintendo gamecube"
or "gamecube disc"
or "gamecube game disc" => MediaType.NintendoGameCubeGameDisc,
"wii"
or "nintendowii"
or "nintendo wii"
or "nintendo wii disc"
or "wii optical disc" => MediaType.NintendoWiiOpticalDisc,
"wiiu"
or "nintendowiiu"
or "nintendo wiiu"
or "nintendo wiiu disc"
or "wiiu optical disc"
or "wii u optical disc" => MediaType.NintendoWiiUOpticalDisc,
"umd" => MediaType.UMD,
#endregion
// Unsorted Formats
"cartridge" => MediaType.Cartridge,
"ced"
or "rcaced"
or "rca ced"
or "videodisc"
or "rca videodisc" => MediaType.CED,
_ => MediaType.NONE,
};
}
#endregion
}
}

View File

@@ -0,0 +1,69 @@
using System.Collections.Generic;
using System.Linq;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
/// <summary>
/// Constant values for UI
/// </summary>
public static class Interface
{
// Button values
public const string StartDumping = "Start Dumping";
public const string StopDumping = "Stop Dumping";
// Byte arrays for signatures
public static readonly byte[] SaturnSectorZeroStart = [0x53, 0x45, 0x47, 0x41, 0x20, 0x53, 0x45, 0x47, 0x41, 0x53, 0x41, 0x54, 0x55, 0x52, 0x4E, 0x20];
// Lists of known drive speed ranges
#if NET20 || NET35 || NET40
public static IList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
public static IList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
public static IList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
public static IList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
public static IList<int> Unknown { get; } = new List<int> { 1 };
#else
public 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 };
public static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
public static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
public static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
public static IReadOnlyList<int> Unknown { get; } = new List<int> { 1 };
#endif
/// <summary>
/// Get list of all drive speeds for a given MediaType
/// </summary>
/// <param name="type">MediaType? that represents the current item</param>
/// <returns>Read-only list of drive speeds</returns>
#if NET20 || NET35 || NET40
public static IList<int> GetSpeedsForMediaType(MediaType? type)
#else
public static IReadOnlyList<int> GetSpeedsForMediaType(MediaType? type)
#endif
{
return type switch
{
MediaType.CDROM
or MediaType.GDROM => CD,
MediaType.DVD
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => DVD,
MediaType.HDDVD => HDDVD,
MediaType.BluRay => BD,
_ => Unknown,
};
}
}
/// <summary>
/// Template field values for submission info
/// </summary>
public static class Template
{
public const string RequiredValue = "(REQUIRED)";
public const string RequiredIfExistsValue = "(REQUIRED, IF EXISTS)";
public const string OptionalValue = "(OPTIONAL)";
public const string DiscNotDetected = "Disc Not Detected";
}
}

680
MPF.Core/Data/Drive.cs Normal file
View File

@@ -0,0 +1,680 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if NET462_OR_GREATER || NETCOREAPP
using Microsoft.Management.Infrastructure;
using Microsoft.Management.Infrastructure.Generic;
#endif
using MPF.Core.Converters;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
/// <summary>
/// Represents information for a single drive
/// </summary>
/// <remarks>
/// TODO: Can the Aaru models be used instead of the ones I've created here?
/// </remarks>
public class Drive
{
#region Fields
/// <summary>
/// Represents drive type
/// </summary>
public InternalDriveType? InternalDriveType { get; set; }
/// <summary>
/// Drive partition format
/// </summary>
public string? DriveFormat { get; private set; } = null;
/// <summary>
/// Windows drive path
/// </summary>
public string? Name { get; private set; } = null;
/// <summary>
/// Represents if Windows has marked the drive as active
/// </summary>
public bool MarkedActive { get; private set; } = false;
/// <summary>
/// Represents the total size of the drive
/// </summary>
public long TotalSize { get; private set; } = default;
/// <summary>
/// Media label as read by Windows
/// </summary>
/// <remarks>The try/catch is needed because Windows will throw an exception if the drive is not marked as active</remarks>
public string? VolumeLabel { get; private set; } = null;
#endregion
#region Derived Fields
/// <summary>
/// Media label as read by Windows, formatted to avoid odd outputs
/// If no volume label present, use PSX or PS2 serial if valid
/// Otherwise, use "track" as volume label
/// </summary>
public string? FormattedVolumeLabel
{
get
{
string? volumeLabel = Template.DiscNotDetected;
if (!this.MarkedActive)
return volumeLabel;
if (!string.IsNullOrEmpty(this.VolumeLabel))
{
volumeLabel = this.VolumeLabel;
}
else
{
// No Volume Label found, fallback to something sensible
switch (this.GetRedumpSystem(null))
{
case RedumpSystem.SonyPlayStation:
case RedumpSystem.SonyPlayStation2:
InfoTool.GetPlayStationExecutableInfo(this.Name, out string? serial, out _, out _);
volumeLabel = serial ?? "track";
break;
default:
volumeLabel = "track";
break;
}
}
foreach (char c in Path.GetInvalidFileNameChars())
volumeLabel = volumeLabel?.Replace(c, '_');
return volumeLabel;
}
}
/// <summary>
/// Read-only access to the drive letter
/// </summary>
/// <remarks>Should only be used in UI applications</remarks>
public char? Letter => this.Name?[0] ?? '\0';
#endregion
/// <summary>
/// Protected constructor
/// </summary>
protected Drive() { }
/// <summary>
/// Create a new Drive object from a drive type and device path
/// </summary>
/// <param name="driveType">InternalDriveType value representing the drive type</param>
/// <param name="devicePath">Path to the device according to the local machine</param>
public static Drive? Create(InternalDriveType? driveType, string devicePath)
{
// Create a new, empty drive object
var drive = new Drive()
{
InternalDriveType = driveType,
};
// If we have an invalid device path, return null
if (string.IsNullOrEmpty(devicePath))
return null;
// Sanitize a Windows-formatted long device path
if (devicePath.StartsWith("\\\\.\\"))
devicePath = devicePath.Substring("\\\\.\\".Length);
// Create and validate the drive info object
var driveInfo = new DriveInfo(devicePath);
if (driveInfo == null || driveInfo == default)
return null;
// Fill in the rest of the data
drive.PopulateFromDriveInfo(driveInfo);
return drive;
}
/// <summary>
/// Populate all fields from a DriveInfo object
/// </summary>
/// <param name="driveInfo">DriveInfo object to populate from</param>
private void PopulateFromDriveInfo(DriveInfo? driveInfo)
{
// If we have an invalid DriveInfo, just return
if (driveInfo == null || driveInfo == default)
return;
// Populate the data fields
this.Name = driveInfo.Name;
this.MarkedActive = driveInfo.IsReady;
if (this.MarkedActive)
{
this.DriveFormat = driveInfo.DriveFormat;
this.TotalSize = driveInfo.TotalSize;
this.VolumeLabel = driveInfo.VolumeLabel;
}
else
{
this.DriveFormat = string.Empty;
this.TotalSize = default;
this.VolumeLabel = string.Empty;
}
}
#region Public Functionality
/// <summary>
/// Create a list of active drives matched to their volume labels
/// </summary>
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
/// <returns>Active drives, matched to labels, if possible</returns>
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
{
var drives = GetDriveList(ignoreFixedDrives);
drives = [.. drives.OrderBy(i => i == null ? "\0" : i.Name)];
return drives;
}
/// <summary>
/// Get the current media type from drive letter
/// </summary>
/// <param name="system"></param>
/// <returns></returns>
public (MediaType?, string?) GetMediaType(RedumpSystem? system)
{
// Take care of the non-optical stuff first
switch (this.InternalDriveType)
{
case Data.InternalDriveType.Floppy:
return (MediaType.FloppyDisk, null);
case Data.InternalDriveType.HardDisk:
return (MediaType.HardDisk, null);
case Data.InternalDriveType.Removable:
return (MediaType.FlashDrive, null);
}
// Some systems should default to certain media types
switch (system)
{
// CD
case RedumpSystem.Panasonic3DOInteractiveMultiplayer:
case RedumpSystem.PhilipsCDi:
case RedumpSystem.SegaDreamcast:
case RedumpSystem.SegaSaturn:
case RedumpSystem.SonyPlayStation:
case RedumpSystem.VideoCD:
return (MediaType.CDROM, null);
// DVD
case RedumpSystem.DVDAudio:
case RedumpSystem.DVDVideo:
case RedumpSystem.MicrosoftXbox:
case RedumpSystem.MicrosoftXbox360:
return (MediaType.DVD, null);
// HD-DVD
case RedumpSystem.HDDVDVideo:
return (MediaType.HDDVD, null);
// Blu-ray
case RedumpSystem.BDVideo:
case RedumpSystem.MicrosoftXboxOne:
case RedumpSystem.MicrosoftXboxSeriesXS:
case RedumpSystem.SonyPlayStation3:
case RedumpSystem.SonyPlayStation4:
case RedumpSystem.SonyPlayStation5:
return (MediaType.BluRay, null);
// GameCube
case RedumpSystem.NintendoGameCube:
return (MediaType.NintendoGameCubeGameDisc, null);
// Wii
case RedumpSystem.NintendoWii:
return (MediaType.NintendoWiiOpticalDisc, null);
// WiiU
case RedumpSystem.NintendoWiiU:
return (MediaType.NintendoWiiUOpticalDisc, null);
// PSP
case RedumpSystem.SonyPlayStationPortable:
return (MediaType.UMD, null);
}
// Handle optical media by size and filesystem
if (this.TotalSize >= 0 && this.TotalSize <= 800_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
return (MediaType.CDROM, null);
else if (this.TotalSize > 800_000_000 && this.TotalSize <= 8_540_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
return (MediaType.DVD, null);
else if (this.TotalSize > 8_540_000_000)
return (MediaType.BluRay, null);
return (null, "Could not determine media type!");
}
/// <summary>
/// Get the current system from drive
/// </summary>
/// <param name="defaultValue"></param>
/// <returns></returns>
public RedumpSystem? GetRedumpSystem(RedumpSystem? defaultValue)
{
// If we can't read the media in that drive, we can't do anything
if (string.IsNullOrEmpty(this.Name) || !Directory.Exists(this.Name))
return defaultValue;
// We're going to assume for floppies, HDDs, and removable drives
if (this.InternalDriveType != Data.InternalDriveType.Optical)
return RedumpSystem.IBMPCcompatible;
// Check volume labels first
RedumpSystem? systemFromLabel = GetRedumpSystemFromVolumeLabel(this.VolumeLabel);
if (systemFromLabel != null)
return systemFromLabel;
// Get a list of files for quicker checking
#region Arcade
// funworld Photo Play
if (File.Exists(Path.Combine(this.Name, "PP.INF"))
&& Directory.Exists(Path.Combine(this.Name, "PPINC")))
{
return RedumpSystem.funworldPhotoPlay;
}
#endregion
#region Consoles
// Bandai Playdia Quick Interactive System
try
{
#if NET20 || NET35
List<string> files = [.. Directory.GetFiles(this.Name, "*", SearchOption.TopDirectoryOnly)];
#else
List<string> files = Directory.EnumerateFiles(this.Name, "*", SearchOption.TopDirectoryOnly).ToList();
#endif
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
{
return RedumpSystem.BandaiPlaydiaQuickInteractiveSystem;
}
}
catch { }
// Bandai Pippin
if (File.Exists(Path.Combine(this.Name, "PippinAuthenticationFile")))
{
return RedumpSystem.BandaiPippin;
}
// Commodore CDTV/CD32
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(this.Name, "S"), "STARTUP-SEQUENCE")))
#else
if (File.Exists(Path.Combine(this.Name, "S", "STARTUP-SEQUENCE")))
#endif
{
if (File.Exists(Path.Combine(this.Name, "CDTV.TM")))
return RedumpSystem.CommodoreAmigaCDTV;
else
return RedumpSystem.CommodoreAmigaCD32;
}
// Mattel Fisher-Price iXL
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(this.Name, "iXL"), "iXLUpdater.exe")))
#else
if (File.Exists(Path.Combine(this.Name, "iXL", "iXLUpdater.exe")))
#endif
{
return RedumpSystem.MattelFisherPriceiXL;
}
// Microsoft Xbox 360
try
{
if (Directory.Exists(Path.Combine(this.Name, "$SystemUpdate"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
#endif
&& this.TotalSize <= 500_000_000)
{
return RedumpSystem.MicrosoftXbox360;
}
}
catch { }
// Microsoft Xbox One
try
{
if (Directory.Exists(Path.Combine(this.Name, "MSXC"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "MSXC")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "MSXC")).Any())
#endif
{
return RedumpSystem.MicrosoftXboxOne;
}
}
catch { }
// Sega Dreamcast
if (File.Exists(Path.Combine(this.Name, "IP.BIN")))
{
return RedumpSystem.SegaDreamcast;
}
// Sega Mega-CD / Sega-CD
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "IP.BIN"))
|| File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "SP.BIN"))
|| File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "SP_AS.BIN"))
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
#else
if (File.Exists(Path.Combine(this.Name, "_BOOT", "IP.BIN"))
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP.BIN"))
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP_AS.BIN"))
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
#endif
{
return RedumpSystem.SegaMegaCDSegaCD;
}
// Sony PlayStation and Sony PlayStation 2
string psxExePath = Path.Combine(this.Name, "PSX.EXE");
string systemCnfPath = Path.Combine(this.Name, "SYSTEM.CNF");
if (File.Exists(systemCnfPath))
{
// Check for either BOOT or BOOT2
var systemCnf = new IniFile(systemCnfPath);
if (systemCnf.ContainsKey("BOOT"))
return RedumpSystem.SonyPlayStation;
else if (systemCnf.ContainsKey("BOOT2"))
return RedumpSystem.SonyPlayStation2;
}
else if (File.Exists(psxExePath))
{
return RedumpSystem.SonyPlayStation;
}
// Sony PlayStation 3
try
{
if (Directory.Exists(Path.Combine(this.Name, "PS3_GAME"))
|| Directory.Exists(Path.Combine(this.Name, "PS3_UPDATE"))
|| File.Exists(Path.Combine(this.Name, "PS3_DISC.SFB")))
{
return RedumpSystem.SonyPlayStation3;
}
}
catch { }
// Sony PlayStation 4
// There are more possible paths that could be checked.
// There are some entries that can be found on most PS4 discs:
// "/app/GAME_SERIAL/app.pkg"
// "/bd/param.sfo"
// "/license/rif"
// There are also extra files that can be found on some discs:
// "/patch/GAME_SERIAL/patch.pkg" can be found in Redump entry 66816.
// Originally on disc as "/patch/CUSA11302/patch.pkg".
// Is used as an on-disc update for the base game app without needing to get update from the internet.
// "/addcont/GAME_SERIAL/CONTENT_ID/ac.pkg" can be found in Redump entry 97619.
// Originally on disc as "/addcont/CUSA00288/FFXIVEXPS400001A/ac.pkg".
#if NET20 || NET35
if (File.Exists(Path.Combine(Path.Combine(Path.Combine(this.Name, "PS4"), "UPDATE"), "PS4UPDATE.PUP")))
#else
if (File.Exists(Path.Combine(this.Name, "PS4", "UPDATE", "PS4UPDATE.PUP")))
#endif
{
return RedumpSystem.SonyPlayStation4;
}
// V.Tech V.Flash / V.Smile Pro
if (File.Exists(Path.Combine(this.Name, "0SYSTEM")))
{
return RedumpSystem.VTechVFlashVSmilePro;
}
#endregion
#region Computers
// Sharp X68000
if (File.Exists(Path.Combine(this.Name, "COMMAND.X")))
{
return RedumpSystem.SharpX68000;
}
#endregion
#region Video Formats
// BD-Video
if (Directory.Exists(Path.Combine(this.Name, "BDMV")))
{
// Technically BD-Audio has this as well, but it's hard to split that out right now
return RedumpSystem.BDVideo;
}
// DVD-Audio and DVD-Video
try
{
if (Directory.Exists(Path.Combine(this.Name, "AUDIO_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
#endif
{
return RedumpSystem.DVDAudio;
}
else if (Directory.Exists(Path.Combine(this.Name, "VIDEO_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
#endif
{
return RedumpSystem.DVDVideo;
}
}
catch { }
// HD-DVD-Video
try
{
if (Directory.Exists(Path.Combine(this.Name, "HVDVD_TS"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
#endif
{
return RedumpSystem.HDDVDVideo;
}
}
catch { }
// Photo CD
try
{
if (Directory.Exists(Path.Combine(this.Name, "PHOTO_CD"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "PHOTO_CD")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "PHOTO_CD")).Any())
#endif
{
return RedumpSystem.PhotoCD;
}
}
catch { }
// VCD
try
{
if (Directory.Exists(Path.Combine(this.Name, "VCD"))
#if NET20 || NET35
&& Directory.GetFiles(Path.Combine(this.Name, "VCD")).Any())
#else
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VCD")).Any())
#endif
{
return RedumpSystem.VideoCD;
}
}
catch { }
#endregion
// Default return
return defaultValue;
}
/// <summary>
/// Get the current system from the drive volume label
/// </summary>
/// <returns>The system based on volume label, null if none detected</returns>
public static RedumpSystem? GetRedumpSystemFromVolumeLabel(string? volumeLabel)
{
// If the volume label is empty, we can't do anything
if (string.IsNullOrEmpty(volumeLabel))
return null;
// Audio CD
if (volumeLabel!.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.AudioCD;
// Microsoft Xbox
if (volumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox;
else if (volumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox;
// Microsoft Xbox 360
if (volumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox360;
else if (volumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.MicrosoftXbox360;
// Microsoft Xbox 360 - Too overly broad even if a lot of discs use this
//if (volumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase))
// return RedumpSystem.MicrosoftXbox360; // Also for Xbox One?
//if (volumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
// return RedumpSystem.MicrosoftXbox360;
// Sega Mega-CD / Sega-CD
if (volumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SegaMegaCDSegaCD;
// Sony PlayStation 3
if (volumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SonyPlayStation3;
// Sony PlayStation 4
if (volumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SonyPlayStation4;
// Sony PlayStation 5
if (volumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase))
return RedumpSystem.SonyPlayStation5;
return null;
}
/// <summary>
/// Refresh the current drive information based on path
/// </summary>
public void RefreshDrive()
{
var driveInfo = DriveInfo.GetDrives().FirstOrDefault(d => d?.Name == this.Name);
this.PopulateFromDriveInfo(driveInfo);
}
#endregion
#region Helpers
/// <summary>
/// Get all current attached Drives
/// </summary>
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
/// <returns>List of drives, null on error</returns>
/// <remarks>
/// https://stackoverflow.com/questions/3060796/how-to-distinguish-between-usb-and-floppy-devices?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
/// https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx
/// </remarks>
private static List<Drive> GetDriveList(bool ignoreFixedDrives)
{
var desiredDriveTypes = new List<DriveType>() { DriveType.CDRom };
if (!ignoreFixedDrives)
{
desiredDriveTypes.Add(DriveType.Fixed);
desiredDriveTypes.Add(DriveType.Removable);
}
// TODO: Reduce reliance on `DriveInfo`
// https://github.com/aaru-dps/Aaru/blob/5164a154e2145941472f2ee0aeb2eff3338ecbb3/Aaru.Devices/Windows/ListDevices.cs#L66
// Create an output drive list
var drives = new List<Drive>();
// Get all standard supported drive types
try
{
drives = DriveInfo.GetDrives()
.Where(d => desiredDriveTypes.Contains(d.DriveType))
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name) ?? new Drive())
.ToList();
}
catch
{
return drives;
}
// Find and update all floppy drives
#if NET462_OR_GREATER || NETCOREAPP
try
{
CimSession session = CimSession.Create(null);
var collection = session.QueryInstances("root\\CIMV2", "WQL", "SELECT * FROM Win32_LogicalDisk");
foreach (CimInstance instance in collection)
{
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 = (properties["Caption"].Value as string ?? string.Empty)[0];
drives.ForEach(d => { if (d?.Name != null && d.Name[0] == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
}
}
}
catch
{
// No-op
}
#endif
return drives;
}
#endregion
}
}

View File

@@ -0,0 +1,62 @@
namespace MPF.Core.Data
{
/// <summary>
/// Available hashing types
/// </summary>
public enum Hash
{
CRC32,
#if NET6_0_OR_GREATER
CRC64,
#endif
MD5,
SHA1,
SHA256,
SHA384,
SHA512,
#if NET6_0_OR_GREATER
XxHash32,
XxHash64,
#endif
}
/// <summary>
/// Drive type for dumping
/// </summary>
public enum InternalDriveType
{
Optical,
Floppy,
HardDisk,
Removable,
}
/// <summary>
/// Program that is being used to dump media
/// </summary>
public enum InternalProgram
{
NONE = 0,
// Dumping support
Aaru,
DiscImageCreator,
Redumper,
// Verification support only
CleanRip,
DCDumper,
UmdImageCreator,
}
/// <summary>
/// Log level for output
/// </summary>
public enum LogLevel
{
USER,
VERBOSE,
ERROR,
SECRET,
}
}

294
MPF.Core/Data/IniFile.cs Normal file
View File

@@ -0,0 +1,294 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace MPF.Core.Data
{
public class IniFile : IDictionary<string, string>
{
private Dictionary<string, string> _keyValuePairs = [];
public string this[string key]
{
get
{
_keyValuePairs ??= [];
key = key.ToLowerInvariant();
if (_keyValuePairs.TryGetValue(key, out string? val))
return val;
return string.Empty;
}
set
{
_keyValuePairs ??= [];
key = key.ToLowerInvariant();
_keyValuePairs[key] = value;
}
}
/// <summary>
/// Create an empty INI file
/// </summary>
public IniFile()
{
}
/// <summary>
/// Populate an INI file from path
/// </summary>
public IniFile(string path)
{
this.Parse(path);
}
/// <summary>
/// Populate an INI file from stream
/// </summary>
public IniFile(Stream stream)
{
this.Parse(stream);
}
/// <summary>
/// Add or update a key and value to the INI file
/// </summary>
public void AddOrUpdate(string key, string value)
{
_keyValuePairs[key.ToLowerInvariant()] = value;
}
/// <summary>
/// Remove a key from the INI file
/// </summary>
public void Remove(string key)
{
_keyValuePairs.Remove(key.ToLowerInvariant());
}
/// <summary>
/// Read an INI file based on the path
/// </summary>
public bool Parse(string path)
{
// If we don't have a file, we can't read it
if (!File.Exists(path))
return false;
using var fileStream = File.OpenRead(path);
return Parse(fileStream);
}
/// <summary>
/// Read an INI file from a stream
/// </summary>
public bool Parse(Stream stream)
{
// If the stream is invalid or unreadable, we can't process it
if (stream == null || !stream.CanRead || stream.Position >= stream.Length - 1)
return false;
// Keys are case-insensitive by default
try
{
using var sr = new StreamReader(stream);
string section = string.Empty;
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
// Empty lines are skipped
if (string.IsNullOrEmpty(line))
{
// No-op, we don't process empty lines
}
// Comments start with ';'
else if (line!.StartsWith(";"))
{
// No-op, we don't process comments
}
// Section titles are surrounded by square brackets
else if (line.StartsWith("["))
{
section = line.TrimStart('[').TrimEnd(']');
}
// Valid INI lines are in the format key=value
else if (line.Contains('='))
{
// Split the line by '=' for key-value pairs
string[] data = line.Split('=');
// If the value field contains an '=', we need to put them back in
string key = data[0].Trim();
string value = string.Join("=", data.Skip(1).ToArray()).Trim();
// Section names are prepended to the key with a '.' separating
if (!string.IsNullOrEmpty(section))
key = $"{section}.{key}";
// Set or overwrite keys in the returned dictionary
_keyValuePairs[key.ToLowerInvariant()] = value;
}
// All other lines are ignored
}
}
catch
{
// We don't care what the error was, just catch and return
return false;
}
return true;
}
/// <summary>
/// Write an INI file to a path
/// </summary>
public bool Write(string path)
{
// If we don't have a valid dictionary with values, we can't write out
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
return false;
using var fileStream = File.OpenWrite(path);
return Write(fileStream);
}
/// <summary>
/// Write an INI file to a stream
/// </summary>
public bool Write(Stream stream)
{
// If we don't have a valid dictionary with values, we can't write out
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
return false;
// If the stream is invalid or unwritable, we can't output to it
if (stream == null || !stream.CanWrite || stream.Position >= stream.Length - 1)
return false;
try
{
// Order the dictionary by keys to link sections together
using var sw = new StreamWriter(stream);
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
string section = string.Empty;
foreach (var keyValuePair in orderedKeyValuePairs)
{
// Extract the key and value
string key = keyValuePair.Key;
string value = keyValuePair.Value;
// We assume '.' is a section name separator
if (key.Contains('.'))
{
// Split the key by '.'
string[] data = keyValuePair.Key.Split('.');
// If the key contains an '.', we need to put them back in
string newSection = data[0].Trim();
key = string.Join(".", data.Skip(1).ToArray()).Trim();
// If we have a new section, write it out
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
{
sw.WriteLine($"[{newSection}]");
section = newSection;
}
}
// Now write out the key and value in a standardized way
sw.WriteLine($"{key}={value}");
}
}
catch
{
// We don't care what the error was, just catch and return
return false;
}
return true;
}
#region IDictionary Impelementations
public ICollection<string> Keys => ((IDictionary<string, string>)_keyValuePairs).Keys;
public ICollection<string> Values => ((IDictionary<string, string>)_keyValuePairs).Values;
public int Count => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Count;
public bool IsReadOnly => ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).IsReadOnly;
public void Add(string key, string value)
{
((IDictionary<string, string>)_keyValuePairs).Add(key.ToLowerInvariant(), value);
}
bool IDictionary<string, string>.Remove(string key)
{
return ((IDictionary<string, string>)_keyValuePairs).Remove(key.ToLowerInvariant());
}
public bool TryGetValue(string key, out string value)
{
bool result = ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out var temp);
value = temp ?? string.Empty;
return result;
}
public void Add(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Add(newItem);
}
public void Clear()
{
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Clear();
}
public bool Contains(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Contains(newItem);
}
public bool ContainsKey(string key)
{
return _keyValuePairs.ContainsKey(key.ToLowerInvariant());
}
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex)
{
((ICollection<KeyValuePair<string, string>>)_keyValuePairs).CopyTo(array, arrayIndex);
}
public bool Remove(KeyValuePair<string, string> item)
{
var newItem = new KeyValuePair<string, string>(item.Key.ToLowerInvariant(), item.Value);
return ((ICollection<KeyValuePair<string, string>>)_keyValuePairs).Remove(newItem);
}
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
{
return ((IEnumerable<KeyValuePair<string, string>>)_keyValuePairs).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_keyValuePairs).GetEnumerator();
}
#endregion
}
}

704
MPF.Core/Data/Options.cs Normal file
View File

@@ -0,0 +1,704 @@
using System.Collections.Generic;
using MPF.Core.Converters;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Data
{
public class Options
{
/// <summary>
/// All settings in the form of a dictionary
/// </summary>
public Dictionary<string, string?> Settings { get; private set; }
/// <summary>
/// Indicate if the program is being run with a clean configuration
/// </summary>
public bool FirstRun
{
get { return GetBooleanSetting(Settings, "FirstRun", true); }
set { Settings["FirstRun"] = value.ToString(); }
}
#region Internal Program
/// <summary>
/// Path to Aaru
/// </summary>
public string? AaruPath
{
get { return GetStringSetting(Settings, "AaruPath", "Programs\\Aaru\\Aaru.exe"); }
set { Settings["AaruPath"] = value; }
}
/// <summary>
/// Path to DiscImageCreator
/// </summary>
public string? DiscImageCreatorPath
{
get { return GetStringSetting(Settings, "DiscImageCreatorPath", "Programs\\Creator\\DiscImageCreator.exe"); }
set { Settings["DiscImageCreatorPath"] = value; }
}
/// <summary>
/// Path to Redumper
/// </summary>
public string? RedumperPath
{
get { return GetStringSetting(Settings, "RedumperPath", "Programs\\Redumper\\redumper.exe"); }
set { Settings["RedumperPath"] = value; }
}
/// <summary>
/// Currently selected dumping program
/// </summary>
public InternalProgram InternalProgram
{
get
{
var valueString = GetStringSetting(Settings, "InternalProgram", InternalProgram.Redumper.ToString());
var valueEnum = EnumConverter.ToInternalProgram(valueString);
return valueEnum == InternalProgram.NONE ? InternalProgram.Redumper : valueEnum;
}
set
{
Settings["InternalProgram"] = value.ToString();
}
}
#endregion
#region UI Defaults
/// <summary>
/// Enable dark mode for UI elements
/// </summary>
public bool EnableDarkMode
{
get { return GetBooleanSetting(Settings, "EnableDarkMode", false); }
set { Settings["EnableDarkMode"] = value.ToString(); }
}
/// <summary>
/// Check for updates on startup
/// </summary>
public bool CheckForUpdatesOnStartup
{
get { return GetBooleanSetting(Settings, "CheckForUpdatesOnStartup", true); }
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>
public string? DefaultOutputPath
{
get { return GetStringSetting(Settings, "DefaultOutputPath", "ISO"); }
set { Settings["DefaultOutputPath"] = value; }
}
/// <summary>
/// Default system if none can be detected
/// </summary>
public RedumpSystem? DefaultSystem
{
get
{
var valueString = GetStringSetting(Settings, "DefaultSystem", null);
var valueEnum = Extensions.ToRedumpSystem(valueString ?? string.Empty);
return valueEnum;
}
set
{
Settings["DefaultSystem"] = value.LongName();
}
}
/// <summary>
/// Default output path for dumps
/// </summary>
/// <remarks>This is a hidden setting</remarks>
public bool ShowDebugViewMenuItem
{
get { return GetBooleanSetting(Settings, "ShowDebugViewMenuItem", false); }
set { Settings["ShowDebugViewMenuItem"] = value.ToString(); }
}
#endregion
#region Dumping Speeds
/// <summary>
/// Default CD dumping speed
/// </summary>
public int PreferredDumpSpeedCD
{
get { return GetInt32Setting(Settings, "PreferredDumpSpeedCD", 24); }
set { Settings["PreferredDumpSpeedCD"] = value.ToString(); }
}
/// <summary>
/// Default DVD dumping speed
/// </summary>
public int PreferredDumpSpeedDVD
{
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", 8); }
set { Settings["PreferredDumpSpeedBD"] = value.ToString(); }
}
#endregion
#region Aaru
/// <summary>
/// Enable debug output while dumping by default
/// </summary>
public bool AaruEnableDebug
{
get { return GetBooleanSetting(Settings, "AaruEnableDebug", false); }
set { Settings["AaruEnableDebug"] = value.ToString(); }
}
/// <summary>
/// Enable verbose output while dumping by default
/// </summary>
public bool AaruEnableVerbose
{
get { return GetBooleanSetting(Settings, "AaruEnableVerbose", false); }
set { Settings["AaruEnableVerbose"] = value.ToString(); }
}
/// <summary>
/// Enable force dumping of media by default
/// </summary>
public bool AaruForceDumping
{
get { return GetBooleanSetting(Settings, "AaruForceDumping", true); }
set { Settings["AaruForceDumping"] = value.ToString(); }
}
/// <summary>
/// Default number of sector/subchannel rereads
/// </summary>
public int AaruRereadCount
{
get { return GetInt32Setting(Settings, "AaruRereadCount", 5); }
set { Settings["AaruRereadCount"] = value.ToString(); }
}
/// <summary>
/// Strip personal data information from Aaru metadata by default
/// </summary>
public bool AaruStripPersonalData
{
get { return GetBooleanSetting(Settings, "AaruStripPersonalData", false); }
set { Settings["AaruStripPersonalData"] = value.ToString(); }
}
#endregion
#region DiscImageCreator
/// <summary>
/// Enable multi-sector read flag by default
/// </summary>
public bool DICMultiSectorRead
{
get { return GetBooleanSetting(Settings, "DICMultiSectorRead", false); }
set { Settings["DICMultiSectorRead"] = value.ToString(); }
}
/// <summary>
/// Include a default multi-sector read value
/// </summary>
public int DICMultiSectorReadValue
{
get { return GetInt32Setting(Settings, "DICMultiSectorReadValue", 0); }
set { Settings["DICMultiSectorReadValue"] = value.ToString(); }
}
/// <summary>
/// Enable overly-secure dumping flags by default
/// </summary>
/// <remarks>
/// Split this into component parts later. Currently does:
/// - Scan sector protection and set subchannel read level to 2 for CD
/// - Set scan file protect flag for DVD
/// </remarks>
public bool DICParanoidMode
{
get { return GetBooleanSetting(Settings, "DICParanoidMode", false); }
set { Settings["DICParanoidMode"] = value.ToString(); }
}
/// <summary>
/// Enable the Quiet flag by default
/// </summary>
public bool DICQuietMode
{
get { return GetBooleanSetting(Settings, "DICQuietMode", false); }
set { Settings["DICQuietMode"] = value.ToString(); }
}
/// <summary>
/// Default number of C2 rereads
/// </summary>
public int DICRereadCount
{
get { return GetInt32Setting(Settings, "DICRereadCount", 20); }
set { Settings["DICRereadCount"] = value.ToString(); }
}
/// <summary>
/// Default number of DVD/HD-DVD/BD rereads
/// </summary>
public int DICDVDRereadCount
{
get { return GetInt32Setting(Settings, "DICDVDRereadCount", 10); }
set { Settings["DICDVDRereadCount"] = value.ToString(); }
}
/// <summary>
/// Reset drive after dumping (useful for older drives)
/// </summary>
public bool DICResetDriveAfterDump
{
get { return GetBooleanSetting(Settings, "DICResetDriveAfterDump", false); }
set { Settings["DICResetDriveAfterDump"] = value.ToString(); }
}
/// <summary>
/// Use the CMI flag for supported disc types
/// </summary>
public bool DICUseCMIFlag
{
get { return GetBooleanSetting(Settings, "DICUseCMIFlag", false); }
set { Settings["DICUseCMIFlag"] = value.ToString(); }
}
#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", true); }
set { Settings["RedumperEnableVerbose"] = value.ToString(); }
}
/// <summary>
/// Enable BE reading by default with Redumper
/// </summary>
public bool RedumperUseBEReading
{
get { return GetBooleanSetting(Settings, "RedumperUseBEReading", false); }
set { Settings["RedumperUseBEReading"] = value.ToString(); }
}
/// <summary>
/// Enable generic drive type by default with Redumper
/// </summary>
public bool RedumperUseGenericDriveType
{
get { return GetBooleanSetting(Settings, "RedumperUseGenericDriveType", false); }
set { Settings["RedumperUseGenericDriveType"] = 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>
/// Scan the disc for protection after dumping
/// </summary>
public bool ScanForProtection
{
get { return GetBooleanSetting(Settings, "ScanForProtection", true); }
set { Settings["ScanForProtection"] = value.ToString(); }
}
/// <summary>
/// Output all found protections to a separate file in the directory
/// </summary>
public bool OutputSeparateProtectionFile
{
get { return GetBooleanSetting(Settings, "OutputSeparateProtectionFile", true); }
set { Settings["OutputSeparateProtectionFile"] = value.ToString(); }
}
/// <summary>
/// Add placeholder values in the submission info
/// </summary>
public bool AddPlaceholders
{
get { return GetBooleanSetting(Settings, "AddPlaceholders", true); }
set { Settings["AddPlaceholders"] = value.ToString(); }
}
/// <summary>
/// Show the disc information window after dumping
/// </summary>
public bool PromptForDiscInformation
{
get { return GetBooleanSetting(Settings, "PromptForDiscInformation", true); }
set { Settings["PromptForDiscInformation"] = value.ToString(); }
}
/// <summary>
/// Pull all information from Redump if signed in
/// </summary>
public bool PullAllInformation
{
get { return GetBooleanSetting(Settings, "PullAllInformation", false); }
set { Settings["PullAllInformation"] = value.ToString(); }
}
/// <summary>
/// Enable tabs in all input fields
/// </summary>
public bool EnableTabsInInputFields
{
get { return GetBooleanSetting(Settings, "EnableTabsInInputFields", false); }
set { Settings["EnableTabsInInputFields"] = value.ToString(); }
}
/// <summary>
/// Limit outputs to Redump-supported values only
/// </summary>
public bool EnableRedumpCompatibility
{
get { return GetBooleanSetting(Settings, "EnableRedumpCompatibility", true); }
set { Settings["EnableRedumpCompatibility"] = value.ToString(); }
}
/// <summary>
/// Show disc eject reminder before the disc information window is shown
/// </summary>
public bool ShowDiscEjectReminder
{
get { return GetBooleanSetting(Settings, "ShowDiscEjectReminder", true); }
set { Settings["ShowDiscEjectReminder"] = value.ToString(); }
}
/// <summary>
/// Eject the disc after dumping
/// </summary>
public bool EjectAfterDump
{
get { return GetBooleanSetting(Settings, "EjectAfterDump", false); }
set { Settings["EjectAfterDump"] = value.ToString(); }
}
/// <summary>
/// Ignore fixed drives when populating the list
/// </summary>
public bool IgnoreFixedDrives
{
get { return GetBooleanSetting(Settings, "IgnoreFixedDrives", true); }
set { Settings["IgnoreFixedDrives"] = value.ToString(); }
}
/// <summary>
/// Show dumping tools in their own window instead of in the log
/// </summary>
public bool ToolsInSeparateWindow
{
get { return GetBooleanSetting(Settings, "ToolsInSeparateWindow", true); }
set { Settings["ToolsInSeparateWindow"] = value.ToString(); }
}
/// <summary>
/// Add the dump filename as a suffix to the auto-generated files
/// </summary>
public bool AddFilenameSuffix
{
get { return GetBooleanSetting(Settings, "AddFilenameSuffix", false); }
set { Settings["AddFilenameSuffix"] = value.ToString(); }
}
/// <summary>
/// Output the compressed JSON version of the submission info
/// </summary>
public bool OutputSubmissionJSON
{
get { return GetBooleanSetting(Settings, "OutputSubmissionJSON", false); }
set { Settings["OutputSubmissionJSON"] = value.ToString(); }
}
/// <summary>
/// Include log files in serialized JSON data
/// </summary>
public bool IncludeArtifacts
{
get { return GetBooleanSetting(Settings, "IncludeArtifacts", false); }
set { Settings["IncludeArtifacts"] = value.ToString(); }
}
/// <summary>
/// Compress output log files to reduce space
/// </summary>
public bool CompressLogFiles
{
get { return GetBooleanSetting(Settings, "CompressLogFiles", true); }
set { Settings["CompressLogFiles"] = value.ToString(); }
}
/// <summary>
/// Delete unnecessary files to reduce space
/// </summary>
public bool DeleteUnnecessaryFiles
{
get { return GetBooleanSetting(Settings, "DeleteUnnecessaryFiles", false); }
set { Settings["DeleteUnnecessaryFiles"] = value.ToString(); }
}
#endregion
#region Skip Options
/// <summary>
/// Skip detecting media type on disc scan
/// </summary>
public bool SkipMediaTypeDetection
{
get { return GetBooleanSetting(Settings, "SkipMediaTypeDetection", false); }
set { Settings["SkipMediaTypeDetection"] = value.ToString(); }
}
/// <summary>
/// Skip detecting known system on disc scan
/// </summary>
public bool SkipSystemDetection
{
get { return GetBooleanSetting(Settings, "SkipSystemDetection", false); }
set { Settings["SkipSystemDetection"] = value.ToString(); }
}
#endregion
#region Protection Scanning Options
/// <summary>
/// Scan archive contents during protection scanning
/// </summary>
public bool ScanArchivesForProtection
{
get { return GetBooleanSetting(Settings, "ScanArchivesForProtection", true); }
set { Settings["ScanArchivesForProtection"] = value.ToString(); }
}
/// <summary>
/// Scan for executable packers during protection scanning
/// </summary>
public bool ScanPackersForProtection
{
get { return GetBooleanSetting(Settings, "ScanPackersForProtection", false); }
set { Settings["ScanPackersForProtection"] = value.ToString(); }
}
/// <summary>
/// Include debug information with scan results
/// </summary>
public bool IncludeDebugProtectionInformation
{
get { return GetBooleanSetting(Settings, "IncludeDebugProtectionInformation", false); }
set { Settings["IncludeDebugProtectionInformation"] = value.ToString(); }
}
/// <summary>
/// Remove drive letters from protection scan output
/// </summary>
public bool HideDriveLetters
{
get { return GetBooleanSetting(Settings, "HideDriveLetters", false); }
set { Settings["HideDriveLetters"] = value.ToString(); }
}
#endregion
#region Logging Options
/// <summary>
/// Enable verbose and debug logs to be written
/// </summary>
public bool VerboseLogging
{
get { return GetBooleanSetting(Settings, "VerboseLogging", true); }
set { Settings["VerboseLogging"] = value.ToString(); }
}
/// <summary>
/// Have the log panel expanded by default on startup
/// </summary>
public bool OpenLogWindowAtStartup
{
get { return GetBooleanSetting(Settings, "OpenLogWindowAtStartup", true); }
set { Settings["OpenLogWindowAtStartup"] = value.ToString(); }
}
#endregion
#region Redump Login Information
public string? RedumpUsername
{
get { return GetStringSetting(Settings, "RedumpUsername", ""); }
set { Settings["RedumpUsername"] = value; }
}
// TODO: Figure out a way to keep this encrypted in some way, BASE64 to start?
public string? RedumpPassword
{
get
{
return GetStringSetting(Settings, "RedumpPassword", "");
}
set { Settings["RedumpPassword"] = value; }
}
/// <summary>
/// Determine if a complete set of Redump credentials might exist
/// </summary>
public bool HasRedumpLogin { get => !string.IsNullOrEmpty(RedumpUsername) && !string.IsNullOrEmpty(RedumpPassword); }
#endregion
/// <summary>
/// Constructor taking a dictionary for settings
/// </summary>
/// <param name="settings"></param>
public Options(Dictionary<string, string?>? settings = null)
{
this.Settings = settings ?? [];
}
/// <summary>
/// Constructor taking an existing Options object
/// </summary>
/// <param name="source"></param>
public Options(Options? source)
{
Settings = new Dictionary<string, string?>(source?.Settings ?? []);
}
/// <summary>
/// Accessor for the internal dictionary
/// </summary>
public string? this[string key]
{
get => this.Settings[key];
set => this.Settings[key] = value;
}
#region Helpers
/// <summary>
/// Get a Boolean setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private static bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
{
if (settings.ContainsKey(key))
{
if (bool.TryParse(settings[key], out bool value))
return value;
else
return defaultValue;
}
else
{
return defaultValue;
}
}
/// <summary>
/// Get an Int32 setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private static int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
{
if (settings.ContainsKey(key))
{
if (int.TryParse(settings[key], out int value))
return value;
else
return defaultValue;
}
else
{
return defaultValue;
}
}
/// <summary>
/// Get a String setting from a settings, dictionary
/// </summary>
/// <param name="settings">Dictionary representing the settings</param>
/// <param name="key">Setting key to get a value for</param>
/// <param name="defaultValue">Default value to return if no value is found</param>
/// <returns>Setting value if possible, default value otherwise</returns>
private static string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
{
if (settings.ContainsKey(key))
return settings[key];
else
return defaultValue;
}
#endregion
}
}

View File

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

59
MPF.Core/Data/Result.cs Normal file
View File

@@ -0,0 +1,59 @@
using System;
namespace MPF.Core.Data
{
/// <summary>
/// Generic success/failure result object, with optional message
/// </summary>
public class Result : System.EventArgs
{
/// <summary>
/// Internal representation of success
/// </summary>
private readonly bool success;
/// <summary>
/// Optional message for the result
/// </summary>
public string Message { get; private set; }
private Result(bool success, string message)
{
this.success = success;
this.Message = message;
}
/// <summary>
/// Create a default success result with no message
/// </summary>
public static Result Success() => new(true, "");
/// <summary>
/// Create a success result with a custom message
/// </summary>
/// <param name="message">String to add as a message</param>
public static Result Success(string? message) => new(true, message ?? string.Empty);
/// <summary>
/// Create a default failure result with no message
/// </summary>
/// <returns></returns>
public static Result Failure() => new(false, "");
/// <summary>
/// Create a failure result with a custom message
/// </summary>
/// <param name="message">String to add as a message</param>
public static Result Failure(string? message) => new(false, message ?? string.Empty);
internal static Result Success(object value)
{
throw new NotImplementedException();
}
/// <summary>
/// Results can be compared to boolean values based on the success value
/// </summary>
public static implicit operator bool(Result result) => result.success;
}
}

559
MPF.Core/DumpEnvironment.cs Normal file
View File

@@ -0,0 +1,559 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MPF.Core.Data;
using MPF.Core.Modules;
using MPF.Core.Utilities;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core
{
/// <summary>
/// Represents the state of all settings to be used during dumping
/// </summary>
public class DumpEnvironment
{
#region Output paths
/// <summary>
/// Base output file path to write files to
/// </summary>
public string OutputPath { get; private set; }
#endregion
#region UI information
/// <summary>
/// Drive object representing the current drive
/// </summary>
public Drive? Drive { get; private set; }
/// <summary>
/// Currently selected system
/// </summary>
public RedumpSystem? System { get; private set; }
/// <summary>
/// Currently selected media type
/// </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 Data.Options Options { get; private set; }
/// <summary>
/// Parameters object representing what to send to the internal program
/// </summary>
public BaseParameters? Parameters { get; private set; }
#endregion
#region Event Handlers
/// <summary>
/// Generic way of reporting a message
/// </summary>
#if NET20 || NET35 || NET40
public EventHandler<BaseParameters.StringEventArgs>? ReportStatus;
#else
public EventHandler<string>? ReportStatus;
#endif
/// <summary>
/// Queue of items that need to be logged
/// </summary>
private ProcessingQueue<string>? outputQueue;
/// <summary>
/// Event handler for data returned from a process
/// </summary>
#if NET20 || NET35 || NET40
private void OutputToLog(object? proc, BaseParameters.StringEventArgs args) => outputQueue?.Enqueue(args.Value);
#else
private void OutputToLog(object? proc, string args) => outputQueue?.Enqueue(args);
#endif
/// <summary>
/// Process the outputs in the queue
/// </summary>
#if NET20 || NET35 || NET40
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, new BaseParameters.StringEventArgs { Value = nextOutput });
#else
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, nextOutput);
#endif
#endregion
/// <summary>
/// Constructor for a full DumpEnvironment object from user information
/// </summary>
/// <param name="options"></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(Data.Options options,
string outputPath,
Drive? drive,
RedumpSystem? system,
MediaType? type,
InternalProgram? internalProgram,
string? parameters)
{
// Set options object
Options = options;
// Output paths
OutputPath = InfoTool.NormalizeOutputPaths(outputPath, false);
// UI information
Drive = drive;
System = system ?? options.DefaultSystem;
Type = type ?? MediaType.NONE;
InternalProgram = internalProgram ?? options.InternalProgram;
// Dumping program
SetParameters(parameters);
}
#region Public Functionality
/// <summary>
/// Set the parameters object based on the internal program and parameters string
/// </summary>
/// <param name="parameters">String representation of the parameters</param>
public void SetParameters(string? parameters)
{
Parameters = InternalProgram switch
{
// Dumping support
InternalProgram.Aaru => new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath },
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath },
InternalProgram.Redumper => new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath },
// Verification support only
InternalProgram.CleanRip => new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null },
InternalProgram.DCDumper => null, // TODO: Create correct parameter type when supported
InternalProgram.UmdImageCreator => new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null },
// This should never happen, but it needs a fallback
_ => new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath },
};
// Set system and type
if (Parameters != null)
{
Parameters.System = System;
Parameters.Type = Type;
}
}
/// <summary>
/// Get the full parameter string for either DiscImageCreator or Aaru
/// </summary>
/// <param name="driveSpeed">Nullable int representing the drive speed</param>
/// <returns>String representing the params, null on error</returns>
public string? GetFullParameters(int? driveSpeed)
{
// Populate with the correct params for inputs (if we're not on the default option)
if (System != null && Type != MediaType.NONE)
{
// If drive letter is invalid, skip this
if (Drive == null)
return null;
// Set the proper parameters
Parameters = InternalProgram switch
{
InternalProgram.Aaru => new Modules.Aaru.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
InternalProgram.Redumper => new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
// This should never happen, but it needs a fallback
_ => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
};
// Generate and return the param string
return Parameters.GenerateParameters();
}
return null;
}
#endregion
#region Dumping
/// <summary>
/// Cancel an in-progress dumping process
/// </summary>
public void CancelDumping() => Parameters?.KillInternalProgram();
/// <summary>
/// Eject the disc using DiscImageCreator
/// </summary>
public async Task<string?> EjectDisc() =>
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
/// <summary>
/// Reset the current drive using DiscImageCreator
/// </summary>
public async Task<string?> ResetDrive() =>
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Reset);
/// <summary>
/// Execute the initial invocation of the dumping programs
/// </summary>
/// <param name="progress">Optional result progress callback</param>
#if NET40
public Result Run(IProgress<Result>? progress = null)
#else
public async Task<Result> Run(IProgress<Result>? progress = null)
#endif
{
// If we don't have parameters
if (Parameters == null)
return Result.Failure("Error! Current configuration is not supported!");
// Check that we have the basics for dumping
Result result = IsValidForDump();
if (!result)
return result;
// Invoke output processing, if needed
if (!Options.ToolsInSeparateWindow)
{
outputQueue = new ProcessingQueue<string>(ProcessOutputs);
if (Parameters.ReportStatus != null)
Parameters.ReportStatus += OutputToLog;
}
// Execute internal tool
progress?.Report(Result.Success($"Executing {InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
var directoryName = Path.GetDirectoryName(OutputPath);
if (!string.IsNullOrEmpty(directoryName))
Directory.CreateDirectory(directoryName);
#if NET40
var executeTask = Task.Factory.StartNew(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
executeTask.Wait();
#else
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
#endif
progress?.Report(Result.Success($"{InternalProgram} has finished!"));
// Remove event handler if needed
if (!Options.ToolsInSeparateWindow)
{
outputQueue?.Dispose();
Parameters.ReportStatus -= OutputToLog;
}
return result;
}
/// <summary>
/// Verify that the current environment has a complete dump and create submission info is possible
/// </summary>
/// <param name="resultProgress">Optional result progress callback</param>
/// <param name="protectionProgress">Optional protection progress callback</param>
/// <param name="processUserInfo">Optional user prompt to deal with submission information</param>
/// <param name="seedInfo">A seed SubmissionInfo object that contains user data</param>
/// <returns>Result instance with the outcome</returns>
public async Task<Result> VerifyAndSaveDumpOutput(
IProgress<Result>? resultProgress = null,
IProgress<BinaryObjectScanner.ProtectionProgress>? protectionProgress = null,
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null,
SubmissionInfo? seedInfo = null)
{
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
// Get the output directory and filename separately
var outputDirectory = Path.GetDirectoryName(OutputPath);
var outputFilename = Path.GetFileName(OutputPath);
// Check to make sure that the output had all the correct files
(bool foundFiles, List<string> missingFiles) = Parameters.FoundAllFiles(outputDirectory, outputFilename, false);
if (!foundFiles)
{
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
return Result.Failure("Error! Please check output directory as dump may be incomplete!");
}
// Extract the information from the output files
resultProgress?.Report(Result.Success("Extracting output information from output files..."));
var submissionInfo = await SubmissionInfoTool.ExtractOutputInformation(
OutputPath,
Drive,
System,
Type,
Options,
Parameters,
resultProgress,
protectionProgress);
resultProgress?.Report(Result.Success("Extracting information complete!"));
// Inject seed submission info data, if necessary
if (seedInfo != null)
{
resultProgress?.Report(Result.Success("Injecting user-supplied information..."));
Builder.InjectSubmissionInformation(submissionInfo, seedInfo);
resultProgress?.Report(Result.Success("Information injection complete!"));
}
// Eject the disc automatically if configured to
if (Options.EjectAfterDump == true)
{
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive?.Name}"));
await EjectDisc();
}
// Reset the drive automatically if configured to
if (InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
{
resultProgress?.Report(Result.Success($"Resetting drive {Drive?.Name}"));
await ResetDrive();
}
// Get user-modifiable information if confugured to
if (Options.PromptForDiscInformation && processUserInfo != null)
{
resultProgress?.Report(Result.Success("Waiting for additional disc information..."));
bool? filledInfo;
(filledInfo, submissionInfo) = processUserInfo(submissionInfo);
if (filledInfo == true)
resultProgress?.Report(Result.Success("Additional disc information added!"));
else
resultProgress?.Report(Result.Success("Disc information skipped!"));
}
// Process special fields for site codes
resultProgress?.Report(Result.Success("Processing site codes..."));
Formatter.ProcessSpecialFields(submissionInfo);
resultProgress?.Report(Result.Success("Processing complete!"));
// Format the information for the text output
resultProgress?.Report(Result.Success("Formatting information..."));
(var formattedValues, var formatResult) = Formatter.FormatOutputData(submissionInfo, Options.EnableRedumpCompatibility);
if (formattedValues == null)
resultProgress?.Report(Result.Success(formatResult));
else
resultProgress?.Report(Result.Failure(formatResult));
// Get the filename suffix for auto-generated files
var filenameSuffix = Options.AddFilenameSuffix ? Path.GetFileNameWithoutExtension(outputFilename) : null;
// Write the text output
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, formattedValues);
if (txtSuccess)
resultProgress?.Report(Result.Success(txtResult));
else
resultProgress?.Report(Result.Failure(txtResult));
// Write the copy protection output
if (submissionInfo?.CopyProtection?.FullProtections != null && submissionInfo.CopyProtection.FullProtections.Any())
{
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
{
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo, Options.HideDriveLetters);
if (scanSuccess)
resultProgress?.Report(Result.Success("Writing complete!"));
else
resultProgress?.Report(Result.Failure("Writing could not complete!"));
}
}
// Write the JSON output, if required
if (Options.OutputSubmissionJSON)
{
resultProgress?.Report(Result.Success($"Writing information to !submissionInfo.json{(Options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, submissionInfo, Options.IncludeArtifacts);
if (jsonSuccess)
resultProgress?.Report(Result.Success("Writing complete!"));
else
resultProgress?.Report(Result.Failure("Writing could not complete!"));
}
// Compress the logs, if required
if (Options.CompressLogFiles)
{
resultProgress?.Report(Result.Success("Compressing log files..."));
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, Parameters);
if (compressSuccess)
resultProgress?.Report(Result.Success(compressResult));
else
resultProgress?.Report(Result.Failure(compressResult));
}
// Delete unnecessary files, if required
if (Options.DeleteUnnecessaryFiles)
{
resultProgress?.Report(Result.Success("Deleting unnecessary files..."));
(bool deleteSuccess, string deleteResult) = InfoTool.DeleteUnnecessaryFiles(outputDirectory, outputFilename, Parameters);
if (deleteSuccess)
resultProgress?.Report(Result.Success(deleteResult));
else
resultProgress?.Report(Result.Failure(deleteResult));
}
resultProgress?.Report(Result.Success("Submission information process complete!"));
return Result.Success();
}
/// <summary>
/// Checks if the parameters are valid
/// </summary>
/// <returns>True if the configuration is valid, false otherwise</returns>
internal bool ParametersValid()
{
// Missing drive means it can never be valid
if (Drive == null)
return false;
bool parametersValid = Parameters?.IsValid() ?? false;
bool floppyValid = !(Drive.InternalDriveType == InternalDriveType.Floppy ^ Type == MediaType.FloppyDisk);
// TODO: HardDisk being in the Removable category is a hack, fix this later
bool removableDiskValid = !((Drive.InternalDriveType == InternalDriveType.Removable || Drive.InternalDriveType == InternalDriveType.HardDisk)
^ (Type == MediaType.CompactFlash || Type == MediaType.SDCard || Type == MediaType.FlashDrive || Type == MediaType.HardDisk));
return parametersValid && floppyValid && removableDiskValid;
}
/// <summary>
/// Run internal program async with an input set of parameters
/// </summary>
/// <param name="parameters"></param>
/// <returns>Standard output from commandline window</returns>
private static async Task<string> ExecuteInternalProgram(BaseParameters parameters)
{
Process childProcess;
#if NET40
string output = await Task.Factory.StartNew(() =>
#else
string output = await Task.Run(() =>
#endif
{
childProcess = new Process()
{
StartInfo = new ProcessStartInfo()
{
FileName = parameters.ExecutablePath!,
Arguments = parameters.GenerateParameters()!,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
},
};
childProcess.Start();
childProcess.WaitForExit(1000);
// Just in case, we want to push a button 5 times to clear any errors
for (int i = 0; i < 5; i++)
childProcess.StandardInput.WriteLine("Y");
string stdout = childProcess.StandardOutput.ReadToEnd();
childProcess.Dispose();
return stdout;
});
return output;
}
/// <summary>
/// Validate the current environment is ready for a dump
/// </summary>
/// <returns>Result instance with the outcome</returns>
private Result IsValidForDump()
{
// Validate that everything is good
if (Parameters == null || !ParametersValid())
return Result.Failure("Error! Current configuration is not supported!");
// Fix the output paths, just in case
OutputPath = InfoTool.NormalizeOutputPaths(OutputPath, false);
// Validate that the output path isn't on the dumping drive
if (Drive?.Name != null && OutputPath.StartsWith(Drive.Name))
return Result.Failure("Error! Cannot output to same drive that is being dumped!");
// Validate that the required program exists
if (!File.Exists(Parameters.ExecutablePath))
return Result.Failure($"Error! {Parameters.ExecutablePath} does not exist!");
// Validate that the dumping drive doesn't contain the executable
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath!);
if (Drive?.Name != null && fullExecutablePath.StartsWith(Drive.Name))
return Result.Failure("Error! Cannot dump same drive that executable resides on!");
// Validate that the current configuration is supported
return Tools.GetSupportStatus(System, Type);
}
/// <summary>
/// Validate that DIscImageCreator is able to be found
/// </summary>
/// <returns>True if DiscImageCreator is found properly, false otherwise</returns>
private bool RequiredProgramsExist()
{
// Validate that the path is configured
if (string.IsNullOrEmpty(Options.DiscImageCreatorPath))
return false;
// Validate that the required program exists
if (!File.Exists(Options.DiscImageCreatorPath))
return false;
return true;
}
/// <summary>
/// Run a standalone DiscImageCreator command
/// </summary>
/// <param name="command">Command string to run</param>
/// <returns>The output of the command on success, null on error</returns>
private async Task<string?> RunStandaloneDiscImageCreatorCommand(string command)
{
// Validate that DiscImageCreator is all set
if (!RequiredProgramsExist())
return null;
// Validate we're not trying to eject a non-optical
if (Drive == null || Drive.InternalDriveType != InternalDriveType.Optical)
return null;
CancelDumping();
var parameters = new Modules.DiscImageCreator.Parameters(string.Empty)
{
BaseCommand = command,
DrivePath = Drive.Name,
ExecutablePath = Options.DiscImageCreatorPath,
};
return await ExecuteInternalProgram(parameters);
}
#endregion
}
}

350
MPF.Core/Hashing/Hasher.cs Normal file
View File

@@ -0,0 +1,350 @@
using System;
using System.Collections.Generic;
using System.IO;
#if NET6_0_OR_GREATER
using System.IO.Hashing;
#endif
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
using MPF.Core.Data;
namespace MPF.Core.Hashing
{
public sealed class Hasher : IDisposable
{
#region Properties
/// <summary>
/// Hash type associated with the current state
/// </summary>
#if NETFRAMEWORK || NETCOREAPP3_1
public Hash HashType { get; private set; }
#else
public Hash HashType { get; init; }
#endif
/// <summary>
/// Current hash in bytes
/// </summary>
public byte[]? CurrentHashBytes
{
get
{
return (_hasher) switch
{
HashAlgorithm ha => ha.Hash,
NonCryptographicHashAlgorithm ncha => ncha.GetCurrentHash().Reverse().ToArray(),
_ => null,
};
}
}
/// <summary>
/// Current hash as a string
/// </summary>
public string? CurrentHashString => ByteArrayToString(CurrentHashBytes);
#endregion
#region Private Fields
/// <summary>
/// Internal hasher being used for processing
/// </summary>
/// <remarks>May be either a HashAlgorithm or NonCryptographicHashAlgorithm</remarks>
private object? _hasher;
#endregion
#region Constructors
/// <summary>
/// Constructor
/// </summary>
/// <param name="hashType">Hash type to instantiate</param>
public Hasher(Hash hashType)
{
this.HashType = hashType;
GetHasher();
}
/// <summary>
/// Generate the correct hashing class based on the hash type
/// </summary>
private void GetHasher()
{
_hasher = HashType switch
{
Hash.CRC32 => new Crc32(),
#if NET6_0_OR_GREATER
Hash.CRC64 => new Crc64(),
#endif
Hash.MD5 => MD5.Create(),
Hash.SHA1 => SHA1.Create(),
Hash.SHA256 => SHA256.Create(),
Hash.SHA384 => SHA384.Create(),
Hash.SHA512 => SHA512.Create(),
#if NET6_0_OR_GREATER
Hash.XxHash32 => new XxHash32(),
Hash.XxHash64 => new XxHash64(),
#endif
_ => null,
};
}
/// <inheritdoc/>
public void Dispose()
{
if (_hasher is IDisposable disposable)
disposable.Dispose();
}
#endregion
#region Static Hashing
/// <summary>
/// Get hashes from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <returns>True if hashing was successful, false otherwise</returns>
public static bool GetFileHashes(string filename, out long size, out string? crc32, out string? md5, out string? sha1)
{
// Set all initial values
crc32 = null; md5 = null; sha1 = null;
// Get all file hashes
var fileHashes = GetFileHashes(filename, out size);
if (fileHashes == null)
return false;
// Assign the file hashes and return
crc32 = fileHashes[Hash.CRC32];
md5 = fileHashes[Hash.MD5];
sha1 = fileHashes[Hash.SHA1];
return true;
}
/// <summary>
/// Get hashes from an input file path
/// </summary>
/// <param name="filename">Path to the input file</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<Hash, string?>? GetFileHashes(string filename, out long size)
{
// If the file doesn't exist, we can't do anything
if (!File.Exists(filename))
{
size = -1;
return null;
}
// Set the file size
size = new FileInfo(filename).Length;
// Open the input file
var input = File.OpenRead(filename);
// Return the hashes from the stream
return GetStreamHashes(input);
}
/// <summary>
/// Get hashes from an input Stream
/// </summary>
/// <param name="input">Stream to hash</param>
/// <returns>Dictionary containing hashes on success, null on error</returns>
public static Dictionary<Hash, string?>? GetStreamHashes(Stream input)
{
// Create the output dictionary
var hashDict = new Dictionary<Hash, string?>();
try
{
// Get a list of hashers to run over the buffer
var hashers = new Dictionary<Hash, Hasher>
{
{ Hash.CRC32, new Hasher(Hash.CRC32) },
#if NET6_0_OR_GREATER
{ Hash.CRC64, new Hasher(Hash.CRC64) },
#endif
{ Hash.MD5, new Hasher(Hash.MD5) },
{ Hash.SHA1, new Hasher(Hash.SHA1) },
{ Hash.SHA256, new Hasher(Hash.SHA256) },
{ Hash.SHA384, new Hasher(Hash.SHA384) },
{ Hash.SHA512, new Hasher(Hash.SHA512) },
#if NET6_0_OR_GREATER
{ Hash.XxHash32, new Hasher(Hash.XxHash32) },
{ Hash.XxHash64, new Hasher(Hash.XxHash64) },
#endif
};
// Initialize the hashing helpers
var loadBuffer = new ThreadLoadBuffer(input);
int buffersize = 3 * 1024 * 1024;
byte[] buffer0 = new byte[buffersize];
byte[] buffer1 = new byte[buffersize];
/*
Please note that some of the following code is adapted from
RomVault. This is a modified version of how RomVault does
threaded hashing. As such, some of the terminology and code
is the same, though variable names and comments may have
been tweaked to better fit this code base.
*/
// Pre load the first buffer
long refsize = input.Length;
int next = refsize > buffersize ? buffersize : (int)refsize;
input.Read(buffer0, 0, next);
int current = next;
refsize -= next;
bool bufferSelect = true;
while (current > 0)
{
// Trigger the buffer load on the second buffer
next = refsize > buffersize ? buffersize : (int)refsize;
if (next > 0)
loadBuffer.Trigger(bufferSelect ? buffer1 : buffer0, next);
byte[] buffer = bufferSelect ? buffer0 : buffer1;
#if NET20 || NET35
// Run hashers sequentially on each chunk
foreach (var h in hashers)
{
h.Value.Process(buffer, current);
}
#else
// Run hashers in parallel on each chunk
Parallel.ForEach(hashers, h => h.Value.Process(buffer, current));
#endif
// Wait for the load buffer worker, if needed
if (next > 0)
loadBuffer.Wait();
// Setup for the next hashing step
current = next;
refsize -= next;
bufferSelect = !bufferSelect;
}
// Finalize all hashing helpers
loadBuffer.Finish();
#if NET20 || NET35
foreach (var h in hashers)
{
h.Value.Terminate();
}
#else
Parallel.ForEach(hashers, h => h.Value.Terminate());
#endif
// Get the results
hashDict[Hash.CRC32] = hashers[Hash.CRC32].CurrentHashString;
#if NET6_0_OR_GREATER
hashDict[Hash.CRC64] = hashers[Hash.CRC64].CurrentHashString;
#endif
hashDict[Hash.MD5] = hashers[Hash.MD5].CurrentHashString;
hashDict[Hash.SHA1] = hashers[Hash.SHA1].CurrentHashString;
hashDict[Hash.SHA256] = hashers[Hash.SHA256].CurrentHashString;
hashDict[Hash.SHA384] = hashers[Hash.SHA384].CurrentHashString;
hashDict[Hash.SHA512] = hashers[Hash.SHA512].CurrentHashString;
#if NET6_0_OR_GREATER
hashDict[Hash.XxHash32] = hashers[Hash.XxHash32].CurrentHashString;
hashDict[Hash.XxHash64] = hashers[Hash.XxHash64].CurrentHashString;
#endif
// Dispose of the hashers
loadBuffer.Dispose();
foreach (var hasher in hashers.Values)
{
hasher.Dispose();
}
return hashDict;
}
catch (IOException)
{
return null;
}
finally
{
input.Dispose();
}
}
#endregion
#region Hashing
/// <summary>
/// Process a buffer of some length with the internal hash algorithm
/// </summary>
public void Process(byte[] buffer, int size)
{
switch (_hasher)
{
case HashAlgorithm ha:
ha.TransformBlock(buffer, 0, size, null, 0);
break;
case NonCryptographicHashAlgorithm ncha:
#if NET20 || NET35 || NET40
byte[] bufferSpan = new byte[size];
Array.Copy(buffer, bufferSpan, size);
#else
var bufferSpan = new ReadOnlySpan<byte>(buffer, 0, size);
#endif
ncha.Append(bufferSpan);
break;
}
}
/// <summary>
/// Finalize the internal hash algorigthm
/// </summary>
/// <remarks>NonCryptographicHashAlgorithm implementations do not need finalization</remarks>
public void Terminate()
{
byte[] emptyBuffer = [];
switch (_hasher)
{
case HashAlgorithm ha:
ha.TransformFinalBlock(emptyBuffer, 0, 0);
break;
}
}
#endregion
#region Helpers
/// <summary>
/// Convert a byte array to a hex string
/// </summary>
/// <param name="bytes">Byte array to convert</param>
/// <returns>Hex string representing the byte array</returns>
/// <link>http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa</link>
private static string? ByteArrayToString(byte[]? bytes)
{
// If we get null in, we send null out
if (bytes == null)
return null;
try
{
string hex = BitConverter.ToString(bytes);
return hex.Replace("-", string.Empty).ToLowerInvariant();
}
catch
{
return null;
}
}
#endregion
}
}

View File

@@ -0,0 +1,184 @@
#if NETFRAMEWORK || NETCOREAPP3_1 || NET5_0
/*
Copyright (c) 2012-2015 Eugene Larchenko (spct@mail.ru)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
using System;
//namespace OptimizedCRC
namespace MPF.Core.Hashing
{
/// <summary>
/// Shell class to trick older versions into using CRC-32 properly
/// </summary>
internal abstract class NonCryptographicHashAlgorithm
{
#if NET20 || NET35 || NET40
/// <summary>
/// When overridden in a derived class, appends the contents of source to
/// the data already processed for the current hash computation.
/// </summary>
/// <param name="source">The data to process.</param>
public abstract void Append(byte[] source);
#else
/// <summary>
/// When overridden in a derived class, appends the contents of source to
/// the data already processed for the current hash computation.
/// </summary>
/// <param name="source">The data to process.</param>
public abstract void Append(ReadOnlySpan<byte> source);
#endif
/// <summary>
/// Gets the current computed hash value without modifying accumulated state.
/// </summary>
/// <returns>The hash value for the data already provided.</returns>
public abstract byte[] GetCurrentHash();
}
/// <remarks>
/// Some changes have been made to this code to make it more similar to the System.IO.Hashing implementations
/// </remarks>
internal class Crc32 : NonCryptographicHashAlgorithm, IDisposable
{
private const uint kCrcPoly = 0xEDB88320;
private const uint kInitial = 0xFFFFFFFF;
private const int CRC_NUM_TABLES = 8;
private static readonly uint[] Table;
static Crc32()
{
unchecked
{
Table = new uint[256 * CRC_NUM_TABLES];
int i;
for (i = 0; i < 256; i++)
{
uint r = (uint)i;
for (int j = 0; j < 8; j++)
{
r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
}
Table[i] = r;
}
for (; i < 256 * CRC_NUM_TABLES; i++)
{
uint r = Table[i - 256];
Table[i] = Table[r & 0xFF] ^ (r >> 8);
}
}
}
public uint UnsignedValue;
public Crc32()
{
Init();
}
/// <summary>
/// Reset CRC
/// </summary>
public void Init()
{
UnsignedValue = kInitial;
}
/// <inheritdoc/>
public override byte[] GetCurrentHash()
{
return BitConverter.GetBytes(~UnsignedValue);
}
/// <inheritdoc/>
#if NET20 || NET35 || NET40
public override void Append(byte[] source)
{
Update(source, 0, source.Length);
}
#else
public override void Append(ReadOnlySpan<byte> source)
{
byte[] sourceBytes = source.ToArray();
Update(sourceBytes, 0, sourceBytes.Length);
}
#endif
private void Update(byte[] data, int offset, int count)
{
_ = new ArraySegment<byte>(data, offset, count); // check arguments
if (count == 0)
{
return;
}
var table = Table;
uint crc = UnsignedValue;
for (; (offset & 7) != 0 && count != 0; count--)
{
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
}
if (count >= 8)
{
/*
* Idea from 7-zip project sources (http://7-zip.org/sdk.html)
*/
int end = (count - 8) & ~7;
count -= end;
end += offset;
while (offset != end)
{
crc ^= (uint)(data[offset] + (data[offset + 1] << 8) + (data[offset + 2] << 16) + (data[offset + 3] << 24));
uint high = (uint)(data[offset + 4] + (data[offset + 5] << 8) + (data[offset + 6] << 16) + (data[offset + 7] << 24));
offset += 8;
crc = table[(byte)crc + 0x700]
^ table[(byte)(crc >>= 8) + 0x600]
^ table[(byte)(crc >>= 8) + 0x500]
^ table[/*(byte)*/(crc >> 8) + 0x400]
^ table[(byte)(high) + 0x300]
^ table[(byte)(high >>= 8) + 0x200]
^ table[(byte)(high >>= 8) + 0x100]
^ table[/*(byte)*/(high >> 8) + 0x000];
}
}
while (count-- != 0)
{
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
}
UnsignedValue = crc;
}
public void Dispose()
{
UnsignedValue = 0;
}
}
}
#endif

View File

@@ -0,0 +1,81 @@
using System;
using System.IO;
using System.Threading;
//namespace Compress.ThreadReaders
namespace MPF.Core.Hashing
{
public sealed class ThreadLoadBuffer : IDisposable
{
private readonly AutoResetEvent _waitEvent;
private readonly AutoResetEvent _outEvent;
private readonly Thread _tWorker;
private byte[]? _buffer;
private int _size;
private readonly Stream _ds;
private bool _finished;
public bool errorState;
public int SizeRead;
public ThreadLoadBuffer(Stream ds)
{
_waitEvent = new AutoResetEvent(false);
_outEvent = new AutoResetEvent(false);
_finished = false;
_ds = ds;
errorState = false;
_tWorker = new Thread(MainLoop);
_tWorker.Start();
}
public void Dispose()
{
_waitEvent.Close();
_outEvent.Close();
}
private void MainLoop()
{
while (true)
{
_waitEvent.WaitOne();
if (_finished)
{
break;
}
try
{
if (_buffer != null)
SizeRead = _ds.Read(_buffer, 0, _size);
}
catch (Exception)
{
errorState = true;
}
_outEvent.Set();
}
}
public void Trigger(byte[] buffer, int size)
{
_buffer = buffer;
_size = size;
_waitEvent.Set();
}
public void Wait()
{
_outEvent.WaitOne();
}
public void Finish()
{
_finished = true;
_waitEvent.Set();
_tWorker.Join();
}
}
}

1945
MPF.Core/InfoTool.cs Normal file

File diff suppressed because it is too large Load Diff

67
MPF.Core/MPF.Core.csproj Normal file
View File

@@ -0,0 +1,67 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.1</VersionPrefix>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Common code for all MPF implementations</Description>
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="MPF.Test" />
</ItemGroup>
<!-- Support for old .NET versions -->
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="MinAsyncBridge" Version="0.12.4" />
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
<PackageReference Include="IndexRange" Version="1.0.3" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="Microsoft.Net.Http" Version="2.2.29" />
</ItemGroup>
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.2" GeneratePathProperty="true">
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="psxt001z.Library" Version="0.21.0-beta3" />
<PackageReference Include="SabreTools.Models" Version="1.3.0" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
<PackageReference Include="SabreTools.Serialization" Version="1.3.1" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
<PackageReference Include="LibIRD" Version="0.6.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,491 @@
namespace MPF.Core.Modules.Aaru
{
/// <summary>
/// Top-level commands for Aaru
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
// Archive Family
public const string ArchivePrefixShort = "arc";
public const string ArchivePrefixLong = "archive";
public const string ArchiveInfo = "info";
// Database Family
public const string DatabasePrefixShort = "db";
public const string DatabasePrefixLong = "database";
public const string DatabaseStats = "stats";
public const string DatabaseUpdate = "update";
// Device Family
public const string DevicePrefixShort = "dev";
public const string DevicePrefixLong = "device";
public const string DeviceInfo = "info";
public const string DeviceList = "list";
public const string DeviceReport = "report";
// Filesystem Family
public const string FilesystemPrefixShort = "fi";
public const string FilesystemPrefixShortAlt = "fs";
public const string FilesystemPrefixLong = "filesystem";
public const string FilesystemExtract = "extract";
public const string FilesystemInfo = "info";
public const string FilesystemListShort = "ls";
public const string FilesystemListLong = "list";
public const string FilesystemOptions = "options";
// Image Family
public const string ImagePrefixShort = "i";
public const string ImagePrefixLong = "image";
public const string ImageChecksumShort = "chk";
public const string ImageChecksumLong = "checksum";
public const string ImageCompareShort = "cmp";
public const string ImageCompareLong = "compare";
public const string ImageConvert = "convert";
public const string ImageCreateSidecar = "create-sidecar";
public const string ImageDecode = "decode";
public const string ImageEntropy = "entropy";
public const string ImageInfo = "info";
public const string ImageOptions = "options";
public const string ImagePrint = "print";
public const string ImageVerify = "verify";
// Media Family
public const string MediaPrefixShort = "m";
public const string MediaPrefixLong = "media";
public const string MediaDump = "dump";
public const string MediaInfo = "info";
public const string MediaScan = "scan";
// Standalone Commands
public const string Configure = "configure";
public const string Formats = "formats";
public const string ListEncodings = "list-encodings";
public const string ListNamespaces = "list-namespaces";
public const string Remote = "remote";
}
/// <summary>
/// Supported encodings for Aaru
/// </summary>
/// TODO: Use to verify encoding settings
public static class EncodingStrings
{
public const string ArabicMac = "x-mac-arabic";
public const string AtariASCII = "atascii";
public const string CentralEuropeanMac = "x-mac-ce";
public const string CommodorePETSCII = "petscii";
public const string CroatianMac = "x-mac-croatian";
public const string CyrillicMac = "x-mac-cryillic";
public const string FarsiMac = "x-mac-farsi";
public const string GreekMac = "x-mac-greek";
public const string HebrewMac = "x-mac-hebrew";
public const string RomanianMac = "x-mac-romanian";
public const string SinclairZXSpectrum = "spectrum";
public const string SinclairZX80 = "zx80";
public const string SinclairZX81 = "zx81";
public const string TurkishMac = "x-mac-turkish";
public const string UkrainianMac = "x-mac-ukrainian";
public const string Unicode = "utf-16";
public const string UnicodeBigEndian = "utf-16BE";
public const string UnicodeUTF32BigEndian = "utf-32BE";
public const string UnicodeUTF32 = "utf-32";
public const string UnicodeUTF7 = "utf-7";
public const string UnicodeUTF8 = "utf-8";
public const string USASCII = "us-ascii";
public const string WesternEuropeanAppleII = "apple2";
public const string WesternEuropeanAppleIIc = "apple2c";
public const string WesternEuropeanAppleIIe = "apple2e";
public const string WesternEuropeanAppleIIgs = "apple2gs";
public const string WesternEuropeanAppleLisa = "lisa";
public const string WesternEuropeanAtariST = "atarist";
public const string WesternEuropeanGEM = "gem";
public const string WesternEuropeanGEOS = "geos";
public const string WesternEuropeanISO = "iso-8859-1";
public const string WesternEuropeanMac = "macintosh";
public const string WesternEuropeanRadix50 = "radix50";
}
/// <summary>
/// Dumping flags for Aaru
/// </summary>
public static class FlagStrings
{
// Boolean flags
public const string Adler32Short = "-a";
public const string Adler32Long = "--adler32";
public const string ClearLong = "--clear";
public const string ClearAllLong = "--clear-all";
public const string CRC16Long = "--crc16";
public const string CRC32Short = "-c";
public const string CRC32Long = "--crc32";
public const string CRC64Long = "--crc64";
public const string DebugShort = "-d";
public const string DebugLong = "--debug";
public const string DiskTagsShort = "-f";
public const string DiskTagsLong = "--disk-tags";
public const string DuplicatedSectorsShort = "-p";
public const string DuplicatedSectorsLong = "--duplicated-sectors";
public const string EjectLong = "--eject";
public const string ExtendedAttributesShort = "-x";
public const string ExtendedAttributesLong = "--xattrs";
public const string FilesystemsShort = "-f";
public const string FilesystemsLong = "--filesystems";
public const string FirstPregapLong = "--first-pregap";
public const string FixOffsetLong = "--fix-offset";
public const string FixSubchannelLong = "--fix-subchannel";
public const string FixSubchannelCrcLong = "--fix-subchannel-crc";
public const string FixSubchannelPositionLong = "--fix-subchannel-position";
public const string Fletcher16Long = "--fletcher16";
public const string Fletcher32Long = "--fletcher32";
public const string ForceShort = "-f";
public const string ForceLong = "--force";
public const string GenerateSubchannelsLong = "--generate-subchannels";
public const string HelpShort = "-h";
public const string HelpShortAlt = "-?";
public const string HelpLong = "--help";
public const string LongFormatShort = "-l";
public const string LongFormatLong = "--long-format";
public const string LongSectorsShort = "-r";
public const string LongSectorsLong = "--long-sectors";
public const string MD5Short = "-m";
public const string MD5Long = "--md5";
public const string MetadataLong = "--metadata";
public const string PartitionsShort = "-p";
public const string PartitionsLong = "--partitions";
public const string PauseLong = "--pause";
public const string PersistentLong = "--persistent";
public const string PrivateLong = "--private";
public const string ResumeShort = "-r";
public const string ResumeLong = "--resume";
public const string RetrySubchannelLong = "--retry-subchannel";
public const string SectorTagsShort = "-p";
public const string SectorTagsLong = "--sector-tags";
public const string SeparatedTracksShort = "-t";
public const string SeparatedTracksLong = "--separated-tracks";
public const string SHA1Short = "-s";
public const string SHA1Long = "--sha1";
public const string SHA256Long = "--sha256";
public const string SHA384Long = "--sha384";
public const string SHA512Long = "--sha512";
public const string SkipCdiReadyHoleLong = "--skip-cdiready-hole";
public const string SpamSumShort = "-f";
public const string SpamSumLong = "--spamsum";
public const string StopOnErrorShort = "-s";
public const string StopOnErrorLong = "--stop-on-error";
public const string StoreEncryptedLong = "--store-encrypted";
public const string TapeShort = "-t";
public const string TapeLong = "--tape";
public const string TitleKeysLong = "--title-keys";
public const string TrapDiscShort = "-t";
public const string TrapDiscLong = "--trap-disc";
public const string TrimLong = "--trim";
public const string UseBufferedReadsLong = "--use-buffered-reads";
public const string VerboseShort = "-v";
public const string VerboseLong = "--verbose";
public const string VerifyDiscShort = "-w";
public const string VerifyDiscLong = "--verify-disc";
public const string VerifySectorsShort = "-s";
public const string VerifySectorsLong = "--verify-sectors";
public const string VersionLong = "--version";
public const string WholeDiscShort = "-w";
public const string WholeDiscLong = "--whole-disc";
// Int8 flags
public const string SpeedLong = "--speed";
// Int16 flags
public const string RetryPassesShort = "-p";
public const string RetryPassesLong = "--retry-passes";
public const string WidthShort = "-w";
public const string WidthLong = "--width";
// Int32 flags
public const string BlockSizeShort = "-b";
public const string BlockSizeLong = "--block-size";
public const string CountShort = "-c";
public const string CountLong = "--count";
public const string MaxBlocksLong = "--max-blocks";
public const string MediaLastSequenceLong = "--media-lastsequence";
public const string MediaSequenceLong = "--media-sequence";
public const string SkipShort = "-k";
public const string SkipLong = "--skip";
// Int64 flags
public const string LengthShort = "-l"; // or "all"
public const string LengthLong = "--length"; // or "all"
public const string StartShort = "-s";
public const string StartLong = "--start";
// String flags
public const string CommentsLong = "--comments";
public const string CreatorLong = "--creator";
public const string DriveManufacturerLong = "--drive-manufacturer";
public const string DriveModelLong = "--drive-model";
public const string DriveRevisionLong = "--drive-revision";
public const string DriveSerialLong = "--drive-serial";
public const string EncodingShort = "-e";
public const string EncodingLong = "--encoding";
public const string FormatConvertShort = "-p";
public const string FormatConvertLong = "--format";
public const string FormatDumpShort = "-t";
public const string FormatDumpLong = "--format";
public const string GeometryShort = "-g";
public const string GeometryLong = "--geometry";
public const string ImgBurnLogShort = "-b";
public const string ImgBurnLogLong = "--ibg-log";
public const string MediaBarcodeLong = "--media-barcode";
public const string MediaManufacturerLong = "--media-manufacturer";
public const string MediaModelLong = "--media-model";
public const string MediaPartNumberLong = "--media-partnumber";
public const string MediaSerialLong = "--media-serial";
public const string MediaTitleLong = "--media-title";
public const string MHDDLogShort = "-m";
public const string MHDDLogLong = "--mhdd-log";
public const string NamespaceShort = "-n";
public const string NamespaceLong = "--namespace";
public const string OptionsShort = "-O";
public const string OptionsLong = "--options";
public const string OutputPrefixShort = "-w";
public const string OutputPrefixLong = "--output-prefix";
public const string ResumeFileShort = "-r";
public const string ResumeFileLong = "--resume-file";
public const string SubchannelLong = "--subchannel";
public const string XMLSidecarShort = "-x";
public const string XMLSidecarLong = "--cicm-xml";
}
/// <summary>
/// Supported formats for Aaru
/// </summary>
/// TODO: Use to verify format settings
public static class FormatStrings
{
// Supported filters
public const string AppleDouble = "AppleDouble";
public const string AppleSingle = "AppleSingle";
public const string BZip2 = "BZip2";
public const string GZip = "GZip";
public const string LZip = "LZip";
public const string MacBinary = "MacBinary";
public const string NoFilter = "No filter";
public const string PCExchange = "PCExchange";
public const string XZ = "XZ";
// Read-only media image formats
public const string AppleDiskArchivalRetrievalTool = "Apple Disk Archival/Retrieval Tool";
public const string AppleNewDiskImageFormat = "Apple New Disk Image Format";
public const string AppleNIB = "Apple NIB";
public const string BlindWrite4 = "BlindWrite 4";
public const string BlindWrite5 = "BlindWrite 5";
public const string CPCEMUDiskFileAndExtendedCPCDiskFile = "CPCEMU Disk-File and Extended CPC Disk-File";
public const string D2FDiskImage = "d2f disk image";
public const string D88DiskImage = "D88 Disk Image";
public const string DIMDiskImage = "DIM Disk Image";
public const string DiscFerret = "DiscFerret";
public const string DiscJuggler = "DiscJuggler";
public const string DreamcastGDIImage = "Dreamcast GDI image";
public const string DunfieldsIMD = "Dunfield's IMD";
public const string HDCopyDiskImage = "HD-Copy disk image";
public const string KryoFluxSTREAM = "KryoFlux STREAM";
public const string MAMECompressedHunksOfData = "MAME Compressed Hunks of Data";
public const string MicrosoftVHDX = "Microsoft VHDX";
public const string NeroBurningROMImage = "Nero Burning ROM image";
public const string PartCloneDiskImage = "PartClone disk image";
public const string PartimageDiskImage = "Partimage disk image";
public const string SpectrumFloppyDiskImage = "Spectrum Floppy Disk Image";
public const string SuperCardPro = "SuperCardPro";
public const string SydexCopyQM = "Sydex CopyQM";
public const string SydexTeleDisk = "Sydex TeleDisk";
// Read/write media image formats
public const string AaruFormat = "Aaru Format";
public const string ACTApricotDiskImage = "ACT Apricot Disk Image";
public const string Alcohol120MediaDescriptorStructure = "Alcohol 120% Media Descriptor Structure";
public const string Anex86DiskImage = "Anex86 Disk Image";
public const string Apple2InterleavedDiskImage = "Apple ][Interleaved Disk Image";
public const string Apple2IMG = "Apple 2IMG";
public const string AppleDiskCopy42 = "Apple DiskCopy 4.2";
public const string AppleUniversalDiskImageFormat = "Apple Universal Disk Image Format";
public const string BasicLisaUtility = "Basic Lisa Utility";
public const string CDRDAOTocfile = "CDRDAO tocfile";
public const string CDRWinCuesheet = "CDRWin cuesheet";
public const string CisCopyDiskImageDCFile = "CisCopy Disk Image(DC-File)";
public const string CloneCD = "CloneCD";
public const string CopyTape = "CopyTape";
public const string DigitalResearchDiskCopy = "Digital Research DiskCopy";
public const string IBMSaveDskF = "IBM SaveDskF";
public const string MAXIDiskImage = "MAXI Disk image";
public const string ParallelsDiskImage = "Parallels disk image";
public const string QEMUCopyOnWriteDiskImage = "QEMU Copy-On-Write disk image";
public const string QEMUCopyOnWriteDiskImageV2 = "QEMU Copy-On-Write disk image v2";
public const string QEMUEnhancedDiskImage = "QEMU Enhanced Disk image";
public const string RawDiskImage = "Raw Disk Image";
public const string RayAracheliansDiskIMage = "Ray Arachelian's Disk IMage";
public const string RSIDEHardDiskImage = "RS-IDE Hard Disk Image";
public const string T98HardDiskImage = "T98 Hard Disk Image";
public const string T98NextNHDr0DiskImage = "T98-Next NHD r0 Disk Image";
public const string Virtual98DiskImage = "Virtual98 Disk Image";
public const string VirtualBoxDiskImage = "VirtualBox Disk Image";
public const string VirtualPC = "VirtualPC";
public const string VMwareDiskImage = "VMware disk image";
// Supported filesystems for identification and information only
public const string AcornAdvancedDiscFilingSystem = "Acorn Advanced Disc Filing System";
public const string AlexanderOsipovDOSFileSystem = "Alexander Osipov DOS file system";
public const string AmigaDOSFilesystem = "Amiga DOS filesystem";
public const string AppleFileSystem = "Apple File System";
public const string AppleHFSPlusFilesystem = "Apple HFS+ filesystem";
public const string AppleHierarchicalFileSystem = "Apple Hierarchical File System";
public const string AppleProDOSFilesystem = "Apple ProDOS filesystem";
public const string AtheOSFilesystem = "AtheOS Filesystem";
public const string BeFilesystem = "Be Filesystem";
public const string BSDFastFileSystem = "BSD Fast File System(aka UNIX File System, UFS)";
public const string BTreeFileSystem = "B-tree file system";
public const string CommodoreFileSystem = "Commodore file system";
public const string CramFilesystem = "Cram filesystem";
public const string DumpEightPlugin = "dump(8) Plugin";
public const string ECMA67 = "ECMA-67";
public const string ExtentFileSystemPlugin = "Extent File System Plugin";
public const string F2FSPlugin = "F2FS Plugin";
public const string Files11OnDiskStructure = "Files-11 On-Disk Structure";
public const string FossilFilesystemPlugin = "Fossil Filesystem Plugin";
public const string HAMMERFilesystem = "HAMMER Filesystem";
public const string HighPerformanceOpticalFileSystem = "High Performance Optical File System";
public const string HPLogicalInterchangeFormatPlugin = "HP Logical Interchange Format Plugin";
public const string JFSPlugin = "JFS Plugin";
public const string LinuxExtendedFilesystem = "Linux extended Filesystem";
public const string LinuxExtendedFilesystem234 = "Linux extended Filesystem 2, 3 and 4";
public const string LocusFilesystemPlugin = "Locus Filesystem Plugin";
public const string MicroDOSFileSystem = "MicroDOS file system";
public const string MicrosoftExtendedFileAllocationTable = "Microsoft Extended File Allocation Table";
public const string MinixFilesystem = "Minix Filesystem";
public const string NewTechnologyFileSystem = "New Technology File System(NTFS)";
public const string NILFS2Plugin = "NILFS2 Plugin";
public const string NintendoOpticalFilesystems = "Nintendo optical filesystems";
public const string OS2HighPerformanceFileSystem = "OS/2 High Performance File System";
public const string OS9RandomBlockFilePlugin = "OS-9 Random Block File Plugin";
public const string PCEngineCDPlugin = "PC Engine CD Plugin";
public const string PCFXPlugin = "PC-FX Plugin";
public const string ProfessionalFileSystem = "Professional File System";
public const string QNX4Plugin = "QNX4 Plugin";
public const string QNX6Plugin = "QNX6 Plugin";
public const string ReiserFilesystemPlugin = "Reiser Filesystem Plugin";
public const string Reiser4FilesystemPlugin = "Reiser4 Filesystem Plugin";
public const string ResilientFileSystemPlugin = "Resilient File System plugin";
public const string RT11FileSystem = "RT-11 file system";
public const string SmartFileSystem = "SmartFileSystem";
public const string SolarOSFilesystem = "Solar_OS filesystem";
public const string SquashFilesystem = "Squash filesystem";
public const string UNICOSFilesystemPlugin = "UNICOS Filesystem Plugin";
public const string UniversalDiskFormat = "Universal Disk Format";
public const string UNIXBootFilesystem = "UNIX Boot filesystem";
public const string UNIXSystemVFilesystem = "UNIX System V filesystem";
public const string VeritasFilesystem = "Veritas filesystem";
public const string VMwareFilesystem = "VMware filesystem";
public const string XFSFilesystemPlugin = "XFS Filesystem Plugin";
public const string XiaFilesystem = "Xia filesystem";
public const string ZFSFilesystemPlugin = "ZFS Filesystem Plugin";
// Supported filesystems that can read their contents
public const string AppleDOSFileSystem = "Apple DOS File System";
public const string AppleLisaFileSystem = "Apple Lisa File System";
public const string AppleMacintoshFileSystem = "Apple Macintosh File System";
public const string CPMFileSystem = "CP/M File System";
public const string FATXFilesystemPlugin = "FATX Filesystem Plugin";
public const string ISO9660Filesystem = "ISO9660 Filesystem";
public const string MicrosoftFileAllocationTable = "Microsoft File Allocation Table";
public const string OperaFilesystemPlugin = "Opera Filesystem Plugin";
public const string UCSDPascalFilesystem = "U.C.S.D.Pascal filesystem";
// Supported partitioning schemes
public const string AcornFileCorePartitions = "Acorn FileCore partitions";
public const string ACTApricotPartitions = "ACT Apricot partitions";
public const string AmigaRigidDiskBlock = "Amiga Rigid Disk Block";
public const string ApplePartitionMap = "Apple Partition Map";
public const string AtariPartitions = "Atari partitions";
public const string BSDDisklabel = "BSD disklabel";
public const string DECDisklabel = "DEC disklabel";
public const string DragonFlyBSD64bitDisklabel = "DragonFly BSD 64-bit disklabel";
public const string GUIDPartitionTable = "GUID Partition Table";
public const string Human68kPartitions = "Human 68k partitions";
public const string MasterBootRecord = "Master Boot Record";
public const string NECPC9800PartitionTable = "NEC PC-9800 partition table";
public const string NeXTDisklabel = "NeXT Disklabel";
public const string Plan9PartitionTable = "Plan9 partition table";
public const string RioKarmaPartitioning = "Rio Karma partitioning";
public const string SGIDiskVolumeHeader = "SGI Disk Volume Header";
public const string SunDisklabel = "Sun Disklabel";
public const string UNIXHardwired = "UNIX hardwired";
public const string UNIXVTOC = "UNIX VTOC";
public const string XboxPartitioning = "Xbox partitioning";
public const string XENIX = "XENIX";
}
/// <summary>
/// Supported namespaces for Aaru
/// </summary>
/// TODO: Use to verify namespace settings
public static class NamespaceStrings
{
// Namespaces for Apple Lisa File System
public const string LisaOfficeSystem = "office";
public const string LisaPascalWorkshop = "workshop"; // Default
// Namespaces for ISO9660 Filesystem
public const string JolietVolumeDescriptor = "joliet"; // Default
public const string PrimaryVolumeDescriptor = "normal";
public const string PrimaryVolumeDescriptorwithEncoding = "romeo";
public const string RockRidge = "rrip";
public const string PrimaryVolumeDescriptorVersionSuffix = "vms";
// Namespaces for Microsoft File Allocation Table
public const string DOS83UpperCase = "dos";
public const string LFNWhenAvailableWithFallback = "ecs"; // Default
public const string LongFileNames = "lfn";
public const string WindowsNT83MixedCase = "nt";
public const string OS2Extended = "os2";
}
/// <summary>
/// Supported options for Aaru
/// </summary>
/// TODO: Use to verify option settings
public static class OptionStrings
{
// Aaru format
public const string AaruCompress = "compress"; // boolean, default true;
public const string AaruDeduplicate = "deduplicate"; // boolean, default true
public const string AaruDictionary = "dictionary"; // number, default 33554432
public const string AaruMaxDDTSize = "max_ddt_size"; // number, default 256
public const string AaruMD5 = "md5"; // boolean, default false
public const string AaruSectorsPerBlock = "sectors_per_block"; // number, default 4096 [power of 2]
public const string AaruSHA1 = "sha1"; // boolean, default false
public const string AaruSHA256 = "sha256"; // boolean, default false
public const string AaruSpamSum = "spamsum"; // boolean, default false
// ACT Apricot Disk Image
public const string ACTApricotDiskImageCompress = "compress"; // boolean, default false
// Apple DiskCopy 4.2
public const string AppleDiskCopyMacOSX = "macosx"; // boolean, default false
// CDRDAO tocfile
public const string CDRDAOTocfileSeparate = "separate"; // boolean, default false
// CDRWin cuesheet
public const string CDRWinCuesheetSeparate = "separate"; // boolean, default false
// ISO9660 Filesystem
public const string ISO9660FSUseEvd = "use_evd"; // boolean, default false
public const string ISO9660FSUsePathTable = "use_path_table"; // boolean, default false
public const string ISO9660FSUseTransTbl = "use_trans_tbl"; // boolean, default false
// VMware disk image
public const string VMwareDiskImageAdapterType = "adapter_type"; // string, default ide [ide, lsilogic, buslogic, legacyESX]
public const string VMwareDiskImageHWVersion = "hwversion"; // number, default 4
public const string VMwareDiskImageSparse = "sparse"; // boolean, default false
public const string VMwareDiskImageSplit = "split"; // boolean, default false
}
}

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,395 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using MPF.Core.Converters;
using MPF.Core.Data;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.CleanRip
{
/// <summary>
/// Represents a generic set of CleanRip parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.CleanRip;
#endregion
/// <inheritdoc/>
public Parameters(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}-dumpinfo.txt"))
missingFiles.Add($"{basePath}-dumpinfo.txt");
if (!File.Exists($"{basePath}.bca"))
missingFiles.Add($"{basePath}.bca");
}
break;
default:
missingFiles.Add("Media and system combination not supported for CleanRip");
break;
}
return (!missingFiles.Any(), missingFiles);
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
{
// Ensure that required sections exist
info = Builder.EnsureAllSections(info);
// TODO: Determine if there's a CleanRip version anywhere
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
// Get the Datafile information
var datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
// Fill in the hash data
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
// Get the individual hash data, as per internal
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
{
info.SizeAndChecksums!.Size = size;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
// Dual-layer discs have the same size and layerbreak
if (size == 8511160320)
info.SizeAndChecksums.Layerbreak = 2084960;
}
// Extract info based generically on MediaType
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (File.Exists(basePath + ".bca"))
info.Extras!.BCA = GetBCA(basePath + ".bca");
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out var gcVersion, out var gcName, out var gcSerial))
{
info.CommonDiscInfo!.Region = gcRegion ?? info.CommonDiscInfo.Region;
info.VersionAndEditions!.Version = gcVersion ?? info.VersionAndEditions.Version;
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalName] = gcName ?? string.Empty;
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = gcSerial ?? string.Empty;
}
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= [];
if (File.Exists(basePath + ".bca"))
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty;
if (File.Exists(basePath + "-dumpinfo.txt"))
info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt")) ?? string.Empty;
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
var logFiles = new List<string>();
switch (this.Type)
{
case MediaType.DVD: // Only added here to help users; not strictly correct
case MediaType.NintendoGameCubeGameDisc:
case MediaType.NintendoWiiOpticalDisc:
if (File.Exists($"{basePath}-dumpinfo.txt"))
logFiles.Add($"{basePath}-dumpinfo.txt");
if (File.Exists($"{basePath}.bca"))
logFiles.Add($"{basePath}.bca");
break;
}
return logFiles;
}
#endregion
#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 files don't exist, we can't get info from it
if (!File.Exists(iso) || !File.Exists(dumpinfo))
return null;
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
using var sr = File.OpenText(dumpinfo);
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
return null;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrEmpty(line))
continue;
else 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()
{
Roms =
[
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>
/// <param name="bcaPath">Path to the BCA file associated with the dump</param>
/// <returns>BCA data as a hex string if possible, null on error</returns>
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
private static string? GetBCA(string bcaPath)
{
// If the file doesn't exist, we can't get the info
if (!File.Exists(bcaPath))
return null;
try
{
var hex = GetFullFile(bcaPath, true);
if (hex == null)
return null;
return Regex.Replace(hex, ".{32}", "$0\n");
}
catch
{
// We don't care what the error was right now
return null;
}
}
/// <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 string? GetCleanripDatfile(string iso, string dumpinfo)
{
// If the files don't exist, we can't get info from it
if (!File.Exists(iso) || !File.Exists(dumpinfo))
return null;
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
using var sr = File.OpenText(dumpinfo);
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
return null;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrEmpty(line))
continue;
else 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 $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get the extracted GC and Wii version
/// </summary>
/// <param name="dumpinfo">Path to discinfo file</param>
/// <param name="region">Output region, if possible</param>
/// <param name="version">Output internal version of the game</param>
/// <param name="name">Output internal name of the game</param>
/// <param name="serial">Output internal serial of the game</param>
/// <returns></returns>
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string? version, out string? name, out string? serial)
{
region = null; version = null; name = null; serial = null;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(dumpinfo))
return false;
try
{
// Make sure this file is a dumpinfo
using var sr = File.OpenText(dumpinfo);
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
return false;
// Read all lines and gather dat information
while (!sr.EndOfStream)
{
var line = sr.ReadLine()?.Trim();
if (string.IsNullOrEmpty(line))
{
continue;
}
else if (line!.StartsWith("Version"))
{
version = line.Substring("Version: ".Length);
}
else if (line.StartsWith("Internal Name"))
{
name = line.Substring("Internal Name: ".Length);
}
else if (line.StartsWith("Filename"))
{
serial = line.Substring("Filename: ".Length);
if (serial.EndsWith("-disc2"))
serial = serial.Replace("-disc2", string.Empty);
// char gameType = serial[0];
// string gameid = serial[1] + serial[2];
// string version = serial[4] + serial[5]
switch (serial[3])
{
case 'A':
region = Region.World;
break;
case 'D':
region = Region.Germany;
break;
case 'E':
region = Region.UnitedStatesOfAmerica;
break;
case 'F':
region = Region.France;
break;
case 'I':
region = Region.Italy;
break;
case 'J':
region = Region.Japan;
break;
case 'K':
region = Region.SouthKorea;
break;
case 'L':
region = Region.Europe; // Japanese import to Europe
break;
case 'M':
region = Region.Europe; // American import to Europe
break;
case 'N':
region = Region.UnitedStatesOfAmerica; // Japanese import to USA
break;
case 'P':
region = Region.Europe;
break;
case 'R':
region = Region.RussianFederation;
break;
case 'S':
region = Region.Spain;
break;
case 'Q':
region = Region.SouthKorea; // Korea with Japanese language
break;
case 'T':
region = Region.SouthKorea; // Korea with English language
break;
case 'X':
region = null; // Not a real region code
break;
}
}
}
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
#endregion
}
}

View File

@@ -0,0 +1,73 @@
using System.Xml.Serialization;
namespace MPF.Core.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

@@ -0,0 +1,77 @@
namespace MPF.Core.Modules.DiscImageCreator
{
/// <summary>
/// Top-level commands for DiscImageCreator
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
public const string Audio = "audio";
public const string BluRay = "bd";
public const string Close = "close";
public const string CompactDisc = "cd";
public const string Data = "data";
public const string DigitalVideoDisc = "dvd";
public const string Disk = "disk";
public const string DriveSpeed = "ls";
public const string Eject = "eject";
public const string Floppy = "fd";
public const string GDROM = "gd";
public const string MDS = "mds";
public const string Merge = "merge";
public const string Reset = "reset";
public const string SACD = "sacd";
public const string Start = "start";
public const string Stop = "stop";
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";
public const string XGD3Swap = "xgd3swap";
}
/// <summary>
/// Dumping flags for DiscImageCreator
/// </summary>
public static class FlagStrings
{
public const string AddOffset = "/a";
public const string AMSF = "/p";
public const string AtariJaguar = "/aj";
public const string BEOpcode = "/be";
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 NoFixSubP = "/np";
public const string NoFixSubQ = "/nq";
public const string NoFixSubQLibCrypt = "/nl";
public const string NoFixSubRtoW = "/nr";
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";
public const string ScanAntiMod = "/am";
public const string ScanFileProtect = "/sf";
public const string ScanSectorProtect = "/ss";
public const string SeventyFour = "/74";
public const string SkipSector = "/sk";
public const string SubchannelReadLevel = "/s";
public const string UseAnchorVolumeDescriptorPointer = "/avdp";
public const string VideoNow = "/vn";
public const string VideoNowColor = "/vnc";
public const string VideoNowXP = "/vnx";
}
}

View File

@@ -0,0 +1,100 @@
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.DiscImageCreator
{
public static class Converters
{
#region Cross-enumeration conversions
/// <summary>
/// Get the most common known system for a given MediaType
/// </summary>
/// <param name="baseCommand">Command value to check</param>
/// <returns>RedumpSystem if possible, null on error</returns>
public static RedumpSystem? ToRedumpSystem(string baseCommand)
{
return baseCommand switch
{
CommandStrings.Audio => (RedumpSystem?)RedumpSystem.AudioCD,
CommandStrings.CompactDisc
or CommandStrings.Data
or CommandStrings.DigitalVideoDisc
or CommandStrings.Disk
or CommandStrings.Floppy
or CommandStrings.Tape => (RedumpSystem?)RedumpSystem.IBMPCcompatible,
CommandStrings.GDROM
or CommandStrings.Swap => (RedumpSystem?)RedumpSystem.SegaDreamcast,
CommandStrings.BluRay => (RedumpSystem?)RedumpSystem.SonyPlayStation3,
CommandStrings.SACD => (RedumpSystem?)RedumpSystem.SuperAudioCD,
CommandStrings.XBOX
or CommandStrings.XBOXSwap => (RedumpSystem?)RedumpSystem.MicrosoftXbox,
CommandStrings.XGD2Swap
or CommandStrings.XGD3Swap => (RedumpSystem?)RedumpSystem.MicrosoftXbox360,
_ => null,
};
}
/// <summary>
/// Get the MediaType associated with a given base command
/// </summary>
/// <param name="baseCommand">Command value to check</param>
/// <returns>MediaType if possible, null on error</returns>
/// <remarks>This takes the "safe" route by assuming the larger of any given format</remarks>
public static MediaType? ToMediaType(string? baseCommand)
{
return baseCommand switch
{
CommandStrings.Audio
or CommandStrings.CompactDisc
or CommandStrings.Data
or CommandStrings.SACD => (MediaType?)MediaType.CDROM,
CommandStrings.GDROM
or CommandStrings.Swap => (MediaType?)MediaType.GDROM,
CommandStrings.DigitalVideoDisc
or CommandStrings.XBOX
or CommandStrings.XBOXSwap
or CommandStrings.XGD2Swap
or CommandStrings.XGD3Swap => (MediaType?)MediaType.DVD,
CommandStrings.BluRay => (MediaType?)MediaType.BluRay,
// Non-optical
CommandStrings.Floppy => (MediaType?)MediaType.FloppyDisk,
CommandStrings.Disk => (MediaType?)MediaType.HardDisk,
CommandStrings.Tape => (MediaType?)MediaType.DataCartridge,
_ => null,
};
}
/// <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)
{
return type switch
{
MediaType.CDROM
or MediaType.GDROM
or MediaType.Cartridge
or MediaType.HardDisk
or MediaType.CompactFlash
or MediaType.MMC
or MediaType.SDCard
or MediaType.FlashDrive => ".bin",
MediaType.DVD
or MediaType.HDDVD
or MediaType.BluRay
or MediaType.NintendoWiiOpticalDisc => ".iso",
MediaType.LaserDisc
or MediaType.NintendoGameCubeGameDisc => ".raw",
MediaType.NintendoWiiUOpticalDisc => ".wud",
MediaType.FloppyDisk => ".img",
MediaType.Cassette => ".wav",
_ => null,
};
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
namespace MPF.Core.Modules.Redumper
{
/// <summary>
/// Top-level commands for Redumper
/// </summary>
public static class CommandStrings
{
public const string NONE = "";
public const string CD = "cd";
public const string DVD = "dvd"; // Synonym for CD
public const string BluRay = "bd"; // Synonym for CD
public const string SACD = "sacd"; // Synonym for CD
public const string Rings = "rings";
public const string Dump = "dump";
public const string DumpNew = "dumpnew"; // Temporary command, to be removed later
public const string Refine = "refine";
public const string RefineNew = "refinenew"; // Temporary command, to be removed later
public const string Verify = "verify";
public const string DVDKey = "dvdkey";
public const string DVDIsoKey = "dvdisokey";
public const string Protection = "protection";
public const string Split = "split";
public const string Hash = "hash";
public const string Info = "info";
public const string Skeleton = "skeleton";
}
/// <summary>
/// Dumping flags for Redumper
/// </summary>
public static class FlagStrings
{
// General
public const string HelpLong = "--help";
public const string HelpShort = "-h";
public const string Version = "--version";
public const string Verbose = "--verbose";
public const string Debug = "--debug";
public const string 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 PlextorSkipLeadin = "--plextor-skip-leadin";
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 DumpWriteOffset = "--dump-write-offset";
public const string DumpReadSize = "--dump-read-size";
public const string OverreadLeadout = "--overread-leadout";
public const string LegacySubs = "--legacy-subs";
public const string DisableCDText = "--disable-cdtext";
}
}

View File

@@ -0,0 +1,28 @@
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.Redumper
{
public static class Converters
{
#region Cross-enumeration conversions
/// <summary>
/// Get the default extension for a given disc type
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>Valid extension (with leading '.'), null on error</returns>
public static string? Extension(MediaType? type)
{
return type switch
{
MediaType.CDROM => ".bin",
MediaType.DVD
or MediaType.HDDVD
or MediaType.BluRay => ".iso",
_ => null,
};
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,242 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Core.Converters;
using MPF.Core.Data;
using MPF.Core.Hashing;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Modules.UmdImageCreator
{
/// <summary>
/// Represents a generic set of UmdImageCreator parameters
/// </summary>
public class Parameters : BaseParameters
{
#region Metadata
/// <inheritdoc/>
public override InternalProgram InternalProgram => InternalProgram.UmdImageCreator;
#endregion
/// <inheritdoc/>
public Parameters(string? parameters) : base(parameters) { }
/// <inheritdoc/>
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
: base(system, type, drivePath, filename, driveSpeed, options)
{
}
#region BaseParameters Implementations
/// <inheritdoc/>
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
{
var missingFiles = new List<string>();
switch (this.Type)
{
case MediaType.UMD:
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
{
if (!File.Exists($"{basePath}_disc.txt"))
missingFiles.Add($"{basePath}_disc.txt");
if (!File.Exists($"{basePath}_mainError.txt"))
missingFiles.Add($"{basePath}_mainError.txt");
if (!File.Exists($"{basePath}_mainInfo.txt"))
missingFiles.Add($"{basePath}_mainInfo.txt");
if (!File.Exists($"{basePath}_volDesc.txt"))
missingFiles.Add($"{basePath}_volDesc.txt");
}
break;
default:
missingFiles.Add("Media and system combination not supported for UmdImageCreator");
break;
}
return (!missingFiles.Any(), missingFiles);
}
/// <inheritdoc/>
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
{
// Ensure that required sections exist
info = Builder.EnsureAllSections(info);
// TODO: Determine if there's a UMDImageCreator version anywhere
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
info.DumpingInfo.DumpingDate = InfoTool.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") ?? string.Empty;
if (Hasher.GetFileHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
{
// Get the Datafile information
var datafile = new Datafile
{
Games = [new Game { Roms = [new Rom { Name = string.Empty, Size = filesize.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1, }] }]
};
// Fill in the hash data
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
info.SizeAndChecksums!.Size = filesize;
info.SizeAndChecksums.CRC32 = crc32;
info.SizeAndChecksums.MD5 = md5;
info.SizeAndChecksums.SHA1 = sha1;
}
if (GetUMDAuxInfo(basePath + "_disc.txt", out var title, out DiscCategory? umdcat, out var umdversion, out var umdlayer, out long umdsize))
{
info.CommonDiscInfo!.Title = title ?? string.Empty;
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
info.VersionAndEditions!.Version = umdversion ?? string.Empty;
info.SizeAndChecksums!.Size = umdsize;
if (!string.IsNullOrEmpty(umdlayer))
info.SizeAndChecksums.Layerbreak = Int64.Parse(umdlayer ?? "-1");
}
break;
}
// Fill in any artifacts that exist, Base64-encoded, if we need to
if (includeArtifacts)
{
info.Artifacts ??= [];
if (File.Exists(basePath + "_disc.txt"))
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt")) ?? string.Empty;
if (File.Exists(basePath + "_drive.txt"))
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt")) ?? string.Empty;
if (File.Exists(basePath + "_mainError.txt"))
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt")) ?? string.Empty;
if (File.Exists(basePath + "_mainInfo.txt"))
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt")) ?? string.Empty;
if (File.Exists(basePath + "_volDesc.txt"))
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt")) ?? string.Empty;
}
}
/// <inheritdoc/>
public override List<string> GetLogFilePaths(string basePath)
{
var logFiles = new List<string>();
switch (this.Type)
{
case MediaType.UMD:
if (File.Exists($"{basePath}_disc.txt"))
logFiles.Add($"{basePath}_disc.txt");
if (File.Exists($"{basePath}_drive.txt"))
logFiles.Add($"{basePath}_drive.txt");
if (File.Exists($"{basePath}_mainError.txt"))
logFiles.Add($"{basePath}_mainError.txt");
if (File.Exists($"{basePath}_mainInfo.txt"))
logFiles.Add($"{basePath}_mainInfo.txt");
if (File.Exists($"{basePath}_volDesc.txt"))
logFiles.Add($"{basePath}_volDesc.txt");
break;
}
return logFiles;
}
#endregion
#region Information Extraction Methods
/// <summary>
/// Get the PVD from the input file, if possible
/// </summary>
/// <param name="mainInfo">_mainInfo.txt file location</param>
/// <returns>Newline-deliminated PVD if possible, null on error</returns>
private static string? GetPVD(string mainInfo)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(mainInfo))
return null;
try
{
// Make sure we're in the right sector
using var sr = File.OpenText(mainInfo);
while (sr.ReadLine()?.StartsWith("========== LBA[000016, 0x0000010]: Main Channel ==========") == false) ;
// Fast forward to the PVD
while (sr.ReadLine()?.StartsWith("0310") == false) ;
// Now that we're at the PVD, read each line in and concatenate
string pvd = "";
for (int i = 0; i < 6; i++)
pvd += sr.ReadLine() + "\n"; // 320-370
return pvd;
}
catch
{
// We don't care what the exception is right now
return null;
}
}
/// <summary>
/// Get the UMD auxiliary info from the outputted files, if possible
/// </summary>
/// <param name="disc">_disc.txt file location</param>
/// <returns>True on successful extraction of info, false otherwise</returns>
private static bool GetUMDAuxInfo(string disc, out string? title, out DiscCategory? umdcat, out string? umdversion, out string? umdlayer, out long umdsize)
{
title = null; umdcat = null; umdversion = null; umdlayer = null; umdsize = -1;
// If the file doesn't exist, we can't get info from it
if (!File.Exists(disc))
return false;
try
{
// Loop through everything to get the first instance of each required field
using var sr = File.OpenText(disc);
while (!sr.EndOfStream)
{
string? line = sr.ReadLine()?.Trim();
if (line == null)
break;
if (line.StartsWith("TITLE") && title == null)
title = line.Substring("TITLE: ".Length);
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
umdversion = line.Split(' ')[1];
else if (line.StartsWith("pspUmdTypes"))
umdcat = InfoTool.GetUMDCategory(line.Split(' ')[1]);
else if (line.StartsWith("L0 length"))
umdlayer = line.Split(' ')[2];
else if (line.StartsWith("FileSize:"))
umdsize = Int64.Parse(line.Split(' ')[1]);
}
// If the L0 length is the size of the full disc, there's no layerbreak
if (Int64.TryParse(umdlayer, out long umdlayerValue) && umdlayerValue * 2048 == umdsize)
umdlayer = null;
return true;
}
catch
{
// We don't care what the exception is right now
return false;
}
}
#endregion
}
}

398
MPF.Core/Protection.cs Normal file
View File

@@ -0,0 +1,398 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using psxt001z;
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'.
namespace MPF.Core
{
public static class Protection
{
/// <summary>
/// Run protection scan on a given path
/// </summary>
/// <param name="path">Path to scan for protection</param>
/// <param name="options">Options object that determines what to scan</param>
/// <param name="progress">Optional progress callback</param>
/// <returns>Set of all detected copy protections with an optional error string</returns>
public static async Task<(Dictionary<string, List<string>>?, string?)> RunProtectionScanOnPath(string path, Data.Options options, IProgress<BinaryObjectScanner.ProtectionProgress>? progress = null)
{
try
{
#if NET40
var found = await Task.Factory.StartNew(() =>
{
var scanner = new BinaryObjectScanner.Scanner(
options.ScanArchivesForProtection,
scanContents: true, // Hardcoded value to avoid issues
scanGameEngines: false, // Hardcoded value to avoid issues
options.ScanPackersForProtection,
scanPaths: true, // Hardcoded value to avoid issues
options.IncludeDebugProtectionInformation,
progress);
return scanner.GetProtections(path);
});
#else
var found = await Task.Run(() =>
{
var scanner = new BinaryObjectScanner.Scanner(
options.ScanArchivesForProtection,
scanContents: true, // Hardcoded value to avoid issues
scanGameEngines: false, // Hardcoded value to avoid issues
options.ScanPackersForProtection,
scanPaths: true, // Hardcoded value to avoid issues
options.IncludeDebugProtectionInformation,
progress);
return scanner.GetProtections(path);
});
#endif
// If nothing was returned, return
#if NET20 || NET35
if (found == null || found.Count == 0)
#else
if (found == null || found.IsEmpty)
#endif
return (null, null);
// Filter out any empty protections
var filteredProtections = found
#if NET20 || NET35
.Where(kvp => kvp.Value != null && kvp.Value.Count > 0)
#else
.Where(kvp => kvp.Value != null && !kvp.Value.IsEmpty)
#endif
.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value.OrderBy(s => s).ToList());
// Return the filtered set of protections
return (filteredProtections, null);
}
catch (Exception ex)
{
return (null, ex.ToString());
}
}
/// <summary>
/// Format found protections to a deduplicated, ordered string
/// </summary>
/// <param name="protections">Dictionary of file to list of protection mappings</param>
/// <returns>Detected protections, if any</returns>
public static string? FormatProtections(Dictionary<string, List<string>>? protections)
{
// If the filtered list is empty in some way, return
if (protections == null || !protections.Any())
return "None found [OMIT FROM SUBMISSION]";
// Get an ordered list of distinct found protections
var orderedDistinctProtections = protections
.SelectMany(kvp => kvp.Value)
.Distinct()
.OrderBy(p => p);
// Sanitize and join protections for writing
string protectionString = SanitizeFoundProtections(orderedDistinctProtections);
if (string.IsNullOrEmpty(protectionString))
return "None found [OMIT FROM SUBMISSION]";
return protectionString;
}
/// <summary>
/// Get the existence of an anti-modchip string from a PlayStation disc, if possible
/// </summary>
/// <param name="path">Path to scan for anti-modchip strings</param>
/// <returns>Anti-modchip existence if possible, false on error</returns>
public static async Task<bool> GetPlayStationAntiModchipDetected(string? path)
{
// If there is no valid path
if (string.IsNullOrEmpty(path))
return false;
#if NET40
return await Task.Factory.StartNew(() =>
{
try
{
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
{
try
{
byte[] fileContent = File.ReadAllBytes(file);
var protection = antiModchip.CheckContents(file, fileContent, false);
if (!string.IsNullOrEmpty(protection))
return true;
}
catch { }
}
}
catch { }
return false;
});
#else
return await Task.Run(() =>
{
try
{
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
#if NET20 || NET35
foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories))
#else
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
#endif
{
try
{
byte[] fileContent = File.ReadAllBytes(file);
var protection = antiModchip.CheckContents(file, fileContent, false);
if (!string.IsNullOrEmpty(protection))
return true;
}
catch { }
}
}
catch { }
return false;
});
#endif
}
/// <summary>
/// Get if LibCrypt data is detected in the subchannel file, if possible
/// </summary>
/// <param name="sub">.sub file location</param>
/// <returns>Status of the LibCrypt data, if possible</returns>
public static bool? GetLibCryptDetected(string sub)
{
// If the file doesn't exist, we can't get info from it
if (!File.Exists(sub))
return null;
return LibCrypt.DetectLibCrypt([sub]);
}
/// <summary>
/// Sanitize unnecessary protection duplication from output
/// </summary>
/// <param name="foundProtections">Enumerable of found protections</param>
public static string SanitizeFoundProtections(IEnumerable<string> foundProtections)
{
// EXCEPTIONS
if (foundProtections.Any(p => p.StartsWith("[Exception opening file")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("[Exception opening file"));
#if NET20 || NET35 || NET40 || NET452 || NET462
var tempList = new List<string> { "Exception occurred while scanning [RESCAN NEEDED]" };
tempList.AddRange(foundProtections);
foundProtections = tempList.OrderBy(p => p);
#else
foundProtections = foundProtections
.Prepend("Exception occurred while scanning [RESCAN NEEDED]")
.OrderBy(p => p);
#endif
}
// ActiveMARK
if (foundProtections.Any(p => p == "ActiveMARK 5") && foundProtections.Any(p => p == "ActiveMARK"))
foundProtections = foundProtections.Where(p => p != "ActiveMARK");
// Cactus Data Shield
if (foundProtections.Any(p => Regex.IsMatch(p, @"Cactus Data Shield [0-9]{3} .+", RegexOptions.Compiled)) && foundProtections.Any(p => p == "Cactus Data Shield 200"))
foundProtections = foundProtections.Where(p => p != "Cactus Data Shield 200");
// CD-Check
foundProtections = foundProtections.Where(p => p != "Executable-Based CD Check");
// CD-Cops
if (foundProtections.Any(p => p == "CD-Cops") && foundProtections.Any(p => p.StartsWith("CD-Cops") && p.Length > "CD-Cops".Length))
foundProtections = foundProtections.Where(p => p != "CD-Cops");
// CD-Key / Serial
foundProtections = foundProtections.Where(p => p != "CD-Key / Serial");
// Electronic Arts
if (foundProtections.Any(p => p == "EA CdKey Registration Module") && foundProtections.Any(p => p.StartsWith("EA CdKey Registration Module") && p.Length > "EA CdKey Registration Module".Length))
foundProtections = foundProtections.Where(p => p != "EA CdKey Registration Module");
if (foundProtections.Any(p => p == "EA DRM Protection") && foundProtections.Any(p => p.StartsWith("EA DRM Protection") && p.Length > "EA DRM Protection".Length))
foundProtections = foundProtections.Where(p => p != "EA DRM Protection");
// Games for Windows LIVE
if (foundProtections.Any(p => p == "Games for Windows LIVE") && foundProtections.Any(p => p.StartsWith("Games for Windows LIVE") && !p.Contains("Zero Day Piracy Protection") && p.Length > "Games for Windows LIVE".Length))
foundProtections = foundProtections.Where(p => p != "Games for Windows LIVE");
// Impulse Reactor
if (foundProtections.Any(p => p.StartsWith("Impulse Reactor Core Module")) && foundProtections.Any(p => p == "Impulse Reactor"))
foundProtections = foundProtections.Where(p => p != "Impulse Reactor");
// JoWood X-Prot
if (foundProtections.Any(p => p.StartsWith("JoWood X-Prot")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"JoWood X-Prot [0-9]\.[0-9]\.[0-9]\.[0-9]{2}", RegexOptions.Compiled)))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
.Where(p => p != "JoWood X-Prot v1.0-v1.3")
.Where(p => p != "JoWood X-Prot v1.4+")
.Where(p => p != "JoWood X-Prot v2");
}
else if (foundProtections.Any(p => p == "JoWood X-Prot v2"))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
.Where(p => p != "JoWood X-Prot v1.0-v1.3")
.Where(p => p != "JoWood X-Prot v1.4+");
}
else if (foundProtections.Any(p => p == "JoWood X-Prot v1.4+"))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
.Where(p => p != "JoWood X-Prot v1.0-v1.3");
}
else if (foundProtections.Any(p => p == "JoWood X-Prot v1.0-v1.3"))
{
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot");
}
}
// LaserLok
// TODO: Figure this one out
// Online Registration
foundProtections = foundProtections.Where(p => !p.StartsWith("Executable-Based Online Registration"));
// ProtectDISC / VOB ProtectCD/DVD
// TODO: Figure this one out
// SafeCast
// TODO: Figure this one out
// Cactus Data Shield / SafeDisc
if (foundProtections.Any(p => p == "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)"))
{
foundProtections = foundProtections
.Where(p => p != "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)");
if (foundProtections.Any(p => !p.StartsWith("SafeDisc")))
{
#if NET20 || NET35 || NET40 || NET452 || NET462
var tempList = new List<string>();
tempList.AddRange(foundProtections);
tempList.Add("Cactus Data Shield 300");
foundProtections = tempList;
#else
foundProtections = foundProtections.Append("Cactus Data Shield 300");
#endif
}
}
// SafeDisc
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => p != "SafeDisc")
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => p != "SafeDisc")
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => !p.StartsWith("Macrovision Security Driver"))
.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => p.StartsWith("Macrovision Security Driver")))
{
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => p != "SafeDisc")
.Where(p => p != "SafeDisc 1/Lite")
.Where(p => p != "SafeDisc 2+");
}
else if (foundProtections.Any(p => p == "SafeDisc 2+"))
{
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.StartsWith("Macrovision Protected Application"))
.Where(p => !p.StartsWith("Macrovision Protection File"))
.Where(p => p != "SafeDisc");
}
}
// SecuROM
// TODO: Figure this one out
// SolidShield
// TODO: Figure this one out
// StarForce
if (foundProtections.Any(p => p.StartsWith("StarForce")))
{
if (foundProtections.Any(p => Regex.IsMatch(p, @"StarForce [0-9]+\..+", RegexOptions.Compiled)))
{
foundProtections = foundProtections.Where(p => p != "StarForce")
.Where(p => p != "StarForce 3-5")
.Where(p => p != "StarForce 5")
.Where(p => p != "StarForce 5 [Protected Module]");
}
else if (foundProtections.Any(p => p == "StarForce 5 [Protected Module]"))
{
foundProtections = foundProtections.Where(p => p != "StarForce")
.Where(p => p != "StarForce 3-5")
.Where(p => p != "StarForce 5");
}
else if (foundProtections.Any(p => p == "StarForce 5"))
{
foundProtections = foundProtections.Where(p => p != "StarForce")
.Where(p => p != "StarForce 3-5");
}
else if (foundProtections.Any(p => p == "StarForce 3-5"))
{
foundProtections = foundProtections.Where(p => p != "StarForce");
}
}
// Sysiphus
if (foundProtections.Any(p => p == "Sysiphus") && foundProtections.Any(p => p.StartsWith("Sysiphus") && p.Length > "Sysiphus".Length))
foundProtections = foundProtections.Where(p => p != "Sysiphus");
// TAGES
// TODO: Figure this one out
// XCP
if (foundProtections.Any(p => p == "XCP") && foundProtections.Any(p => p.StartsWith("XCP") && p.Length > "XCP".Length))
foundProtections = foundProtections.Where(p => p != "XCP");
return string.Join(", ", foundProtections.ToArray());
}
}
}

View File

@@ -0,0 +1,736 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MPF.Core.Data;
using MPF.Core.Modules;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using SabreTools.RedumpLib.Web;
namespace MPF.Core
{
/// <summary>
/// Class to hold all SubmissionInfo-related methods
/// </summary>
internal static class SubmissionInfoTool
{
#region Extraction and Filling
/// <summary>
/// Extract all of the possible information from a given input combination
/// </summary>
/// <param name="outputPath">Output path to write to</param>
/// <param name="drive">Drive object representing the current drive</param>
/// <param name="system">Currently selected system</param>
/// <param name="mediaType">Currently selected media type</param>
/// <param name="options">Options object representing user-defined options</param>
/// <param name="parameters">Parameters object representing what to send to the internal program</param>
/// <param name="resultProgress">Optional result progress callback</param>
/// <param name="protectionProgress">Optional protection progress callback</param>
/// <returns>SubmissionInfo populated based on outputs, null on error</returns>
public static async Task<SubmissionInfo?> ExtractOutputInformation(
string outputPath,
Drive? drive,
RedumpSystem? system,
MediaType? mediaType,
Options options,
BaseParameters? parameters,
IProgress<Result>? resultProgress = null,
IProgress<BinaryObjectScanner.ProtectionProgress>? protectionProgress = null)
{
// Ensure the current disc combination should exist
if (!system.MediaTypes().Contains(mediaType))
return null;
// Split the output path for easier use
var outputDirectory = Path.GetDirectoryName(outputPath);
string outputFilename = Path.GetFileName(outputPath);
// Check that all of the relevant files are there
(bool foundFiles, List<string> missingFiles) = parameters.FoundAllFiles(outputDirectory, outputFilename, false);
if (!foundFiles)
{
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
resultProgress?.Report(Result.Failure($"This may indicate an issue with the hardware or media, including unsupported devices.\nPlease see dumping program documentation for more details."));
return null;
}
// Sanitize the output filename to strip off any potential extension
outputFilename = Path.GetFileNameWithoutExtension(outputFilename);
// Create the SubmissionInfo object with all user-inputted values by default
string combinedBase;
if (string.IsNullOrEmpty(outputDirectory))
combinedBase = outputFilename;
else
combinedBase = Path.Combine(outputDirectory, outputFilename);
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
System = system,
Media = mediaType.ToDiscType(),
Title = options.AddPlaceholders ? Template.RequiredValue : string.Empty,
ForeignTitleNonLatin = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
DiscNumberLetter = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
DiscTitle = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
Category = null,
Region = null,
Languages = null,
Serial = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty,
Barcode = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
Contents = string.Empty,
},
VersionAndEditions = new VersionAndEditionsSection()
{
Version = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty,
OtherEditions = options.AddPlaceholders ? "(VERIFY THIS) Original" : string.Empty,
},
};
// Ensure that required sections exist
info = Builder.EnsureAllSections(info);
// Get specific tool output handling
parameters?.GenerateSubmissionInfo(info, options, combinedBase, drive, options.IncludeArtifacts);
// Get a list of matching IDs for each line in the DAT
if (!string.IsNullOrEmpty(info.TracksAndWriteOffsets!.ClrMameProData) && options.HasRedumpLogin)
#if NET40
_ = FillFromRedump(options, info, resultProgress);
#else
_ = await FillFromRedump(options, info, resultProgress);
#endif
// If we have both ClrMamePro and Size and Checksums data, remove the ClrMamePro
if (!string.IsNullOrEmpty(info.SizeAndChecksums?.CRC32))
info.TracksAndWriteOffsets.ClrMameProData = null;
// Add the volume label to comments, if possible or necessary
string? volLabels = FormatVolumeLabels(drive?.VolumeLabel, parameters?.VolumeLabels);
if (volLabels != null)
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.VolumeLabel] = volLabels;
// Extract info based generically on MediaType
switch (mediaType)
{
case MediaType.CDROM:
case MediaType.GDROM:
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
break;
case MediaType.DVD:
case MediaType.HDDVD:
case MediaType.BluRay:
// If we have a single-layer disc
if (info.SizeAndChecksums!.Layerbreak == default)
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
// If we have a dual-layer disc
else if (info.SizeAndChecksums!.Layerbreak2 == default)
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
// If we have a triple-layer disc
else if (info.SizeAndChecksums!.Layerbreak3 == default)
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
// If we have a quad-layer disc
else
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer2ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer3MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer3MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer3ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
break;
case MediaType.NintendoGameCubeGameDisc:
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.Extras!.BCA ??= (options.AddPlaceholders ? Template.RequiredValue : string.Empty);
break;
case MediaType.NintendoWiiOpticalDisc:
// If we have a single-layer disc
if (info.SizeAndChecksums!.Layerbreak == default)
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
// If we have a dual-layer disc
else
{
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
}
info.Extras!.DiscKey = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.Extras.BCA = info.Extras.BCA ?? (options.AddPlaceholders ? Template.RequiredValue : string.Empty);
break;
case MediaType.UMD:
// Both single- and dual-layer discs have two "layers" for the ring
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
info.SizeAndChecksums!.CRC32 ??= (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty);
info.SizeAndChecksums.MD5 ??= (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty);
info.SizeAndChecksums.SHA1 ??= (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty);
info.TracksAndWriteOffsets.ClrMameProData = null;
break;
}
// Extract info based specifically on RedumpSystem
switch (system)
{
case RedumpSystem.AcornArchimedes:
info.CommonDiscInfo!.Region ??= Region.UnitedKingdom;
break;
case RedumpSystem.AppleMacintosh:
case RedumpSystem.EnhancedCD:
case RedumpSystem.IBMPCcompatible:
case RedumpSystem.PalmOS:
case RedumpSystem.PocketPC:
case RedumpSystem.RainbowDisc:
case RedumpSystem.SonyElectronicBook:
resultProgress?.Report(Result.Success("Running copy protection scan... this might take a while!"));
var (protectionString, fullProtections) = await InfoTool.GetCopyProtection(drive, options, protectionProgress);
info.CopyProtection!.Protection += protectionString;
info.CopyProtection.FullProtections = fullProtections as Dictionary<string, List<string>?> ?? [];
resultProgress?.Report(Result.Success("Copy protection scan complete!"));
break;
case RedumpSystem.AudioCD:
case RedumpSystem.DVDAudio:
case RedumpSystem.SuperAudioCD:
info.CommonDiscInfo!.Category ??= DiscCategory.Audio;
break;
case RedumpSystem.BandaiPlaydiaQuickInteractiveSystem:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? Region.Japan;
break;
case RedumpSystem.BDVideo:
info.CommonDiscInfo!.Category ??= DiscCategory.BonusDiscs;
info.CopyProtection!.Protection = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
break;
case RedumpSystem.CommodoreAmigaCD:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.CommodoreAmigaCD32:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= Region.Europe;
break;
case RedumpSystem.CommodoreAmigaCDTV:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= Region.Europe;
break;
case RedumpSystem.DVDVideo:
info.CommonDiscInfo!.Category ??= DiscCategory.BonusDiscs;
break;
case RedumpSystem.FujitsuFMTownsseries:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? Region.Japan;
break;
case RedumpSystem.FujitsuFMTownsMarty:
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.IncredibleTechnologiesEagle:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamieAmusement:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiFireBeat:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiSystemGV:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiSystem573:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.KonamiTwinkle:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.MattelHyperScan:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.MicrosoftXboxOne:
if (drive?.Name != null)
{
string xboxOneMsxcPath = Path.Combine(drive.Name, "MSXC");
if (drive != null && Directory.Exists(xboxOneMsxcPath))
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = string.Join("\n",
Directory.GetFiles(xboxOneMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).ToArray());
}
}
break;
case RedumpSystem.MicrosoftXboxSeriesXS:
if (drive?.Name != null)
{
string xboxSeriesXMsxcPath = Path.Combine(drive.Name, "MSXC");
if (drive != null && Directory.Exists(xboxSeriesXMsxcPath))
{
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = string.Join("\n",
Directory.GetFiles(xboxSeriesXMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).ToArray());
}
}
break;
case RedumpSystem.NamcoSegaNintendoTriforce:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.NavisoftNaviken21:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo.Region ??= Region.Japan;
break;
case RedumpSystem.NECPC88series:
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.NECPC98series:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.NECPCFXPCFXGA:
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.SegaChihiro:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaDreamcast:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaNaomi:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaNaomi2:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SegaTitanVideo:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SharpX68000:
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.SNKNeoGeoCD:
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.SonyPlayStation:
// Only check the disc if the dumping program couldn't detect
if (drive != null && info.CopyProtection!.AntiModchip == YesNo.NULL)
{
resultProgress?.Report(Result.Success("Checking for anti-modchip strings... this might take a while!"));
info.CopyProtection.AntiModchip = await InfoTool.GetAntiModchipDetected(drive) ? YesNo.Yes : YesNo.No;
resultProgress?.Report(Result.Success("Anti-modchip string scan complete!"));
}
// Special case for DIC only
if (parameters?.InternalProgram == InternalProgram.DiscImageCreator)
{
resultProgress?.Report(Result.Success("Checking for LibCrypt status... this might take a while!"));
InfoTool.GetLibCryptDetected(info, combinedBase);
resultProgress?.Report(Result.Success("LibCrypt status checking complete!"));
}
break;
case RedumpSystem.SonyPlayStation2:
info.CommonDiscInfo!.LanguageSelection = [LanguageSelection.BiosSettings, LanguageSelection.LanguageSelector, LanguageSelection.OptionsMenu];
break;
case RedumpSystem.SonyPlayStation3:
info.Extras!.DiscKey = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
info.Extras.DiscID = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
break;
case RedumpSystem.TomyKissSite:
info.CommonDiscInfo!.Region ??= Region.Japan;
break;
case RedumpSystem.ZAPiTGamesGameWaveFamilyEntertainmentSystem:
info.CopyProtection!.Protection = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
break;
}
// Set the category if it's not overriden
info.CommonDiscInfo!.Category ??= DiscCategory.Games;
// Comments and contents have odd handling
if (string.IsNullOrEmpty(info.CommonDiscInfo.Comments))
info.CommonDiscInfo.Comments = options.AddPlaceholders ? Template.OptionalValue : string.Empty;
if (string.IsNullOrEmpty(info.CommonDiscInfo.Contents))
info.CommonDiscInfo.Contents = options.AddPlaceholders ? Template.OptionalValue : string.Empty;
// Normalize the disc type with all current information
Validator.NormalizeDiscType(info);
return info;
}
/// <summary>
/// Fill in a SubmissionInfo object from Redump, if possible
/// </summary>
/// <param name="options">Options object representing user-defined options</param>
/// <param name="info">Existing SubmissionInfo object to fill</param>
/// <param name="resultProgress">Optional result progress callback</param>
#if NET40
public static bool FillFromRedump(Options options, SubmissionInfo info, IProgress<Result>? resultProgress = null)
#else
public async static Task<bool> FillFromRedump(Options options, SubmissionInfo info, IProgress<Result>? resultProgress = null)
#endif
{
// If no username is provided
if (string.IsNullOrEmpty(options.RedumpUsername) || string.IsNullOrEmpty(options.RedumpPassword))
return false;
// Set the current dumper based on username
info.DumpersAndStatus ??= new DumpersAndStatusSection();
info.DumpersAndStatus.Dumpers = [options.RedumpUsername!];
info.PartiallyMatchedIDs = [];
// Login to Redump
#if NETFRAMEWORK
using var wc = new RedumpWebClient();
bool? loggedIn = wc.Login(options.RedumpUsername!, options.RedumpPassword!);
#else
using var wc = new RedumpHttpClient();
bool? loggedIn = await wc.Login(options.RedumpUsername, options.RedumpPassword);
#endif
if (loggedIn == null)
{
resultProgress?.Report(Result.Failure("There was an unknown error connecting to Redump"));
return false;
}
else if (loggedIn == false)
{
// Don't log the as a failure or error
return false;
}
// Setup the full-track checks
bool allFound = true;
List<int>? fullyMatchedIDs = null;
// Loop through all of the hashdata to find matching IDs
resultProgress?.Report(Result.Success("Finding disc matches on Redump..."));
var splitData = info.TracksAndWriteOffsets?.ClrMameProData?.TrimEnd('\n')?.Split('\n');
int trackCount = splitData?.Length ?? 0;
foreach (string hashData in splitData ?? [])
{
// Catch any errant blank lines
if (string.IsNullOrEmpty(hashData))
{
trackCount--;
resultProgress?.Report(Result.Success("Blank line found, skipping!"));
continue;
}
// If the line ends in a known extra track names, skip them for checking
if (hashData.Contains("(Track 0).bin")
|| hashData.Contains("(Track 0.2).bin")
|| hashData.Contains("(Track 00).bin")
|| hashData.Contains("(Track 00.2).bin")
|| hashData.Contains("(Track A).bin")
|| hashData.Contains("(Track A.2).bin")
|| hashData.Contains("(Track AA).bin")
|| hashData.Contains("(Track AA.2).bin"))
{
trackCount--;
resultProgress?.Report(Result.Success("Extra track found, skipping!"));
continue;
}
// Get the SHA-1 hash
if (!InfoTool.GetISOHashValues(hashData, out _, out _, out _, out string? sha1))
{
resultProgress?.Report(Result.Failure($"Line could not be parsed: {hashData}"));
continue;
}
#if NET40
var validateTask = Validator.ValidateSingleTrack(wc, info, sha1);
validateTask.Wait();
(bool singleFound, var foundIds, string? result) = validateTask.Result;
#else
(bool singleFound, var foundIds, string? result) = await Validator.ValidateSingleTrack(wc, info, sha1);
#endif
if (singleFound)
resultProgress?.Report(Result.Success(result));
else
resultProgress?.Report(Result.Failure(result));
// Ensure that all tracks are found
allFound &= singleFound;
// If we found a track, only keep track of distinct found tracks
if (singleFound && foundIds != null)
{
if (fullyMatchedIDs == null)
fullyMatchedIDs = foundIds;
else
fullyMatchedIDs = fullyMatchedIDs.Intersect(foundIds).ToList();
}
// If no tracks were found, remove all fully matched IDs found so far
else
{
fullyMatchedIDs = [];
}
}
// If we don't have any matches but we have a universal hash
if (!info.PartiallyMatchedIDs.Any() && info.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) == true)
{
#if NET40
var validateTask = Validator.ValidateUniversalHash(wc, info);
validateTask.Wait();
(bool singleFound, var foundIds, string? result) = validateTask.Result;
#else
(bool singleFound, var foundIds, string? result) = await Validator.ValidateUniversalHash(wc, info);
#endif
if (singleFound)
resultProgress?.Report(Result.Success(result));
else
resultProgress?.Report(Result.Failure(result));
// Ensure that the hash is found
allFound = singleFound;
// If we found a track, only keep track of distinct found tracks
if (singleFound && foundIds != null)
{
fullyMatchedIDs = foundIds;
}
// If no tracks were found, remove all fully matched IDs found so far
else
{
fullyMatchedIDs = [];
}
}
// Make sure we only have unique IDs
info.PartiallyMatchedIDs = [.. info.PartiallyMatchedIDs.Distinct().OrderBy(id => id)];
resultProgress?.Report(Result.Success("Match finding complete! " + (fullyMatchedIDs != null && fullyMatchedIDs.Count > 0
? "Fully Matched IDs: " + string.Join(",", fullyMatchedIDs.Select(i => i.ToString()).ToArray())
: "No matches found")));
// Exit early if one failed or there are no matched IDs
if (!allFound || fullyMatchedIDs == null || fullyMatchedIDs.Count == 0)
return false;
// Find the first matched ID where the track count matches, we can grab a bunch of info from it
int totalMatchedIDsCount = fullyMatchedIDs.Count;
for (int i = 0; i < totalMatchedIDsCount; i++)
{
// Skip if the track count doesn't match
#if NET40
var validateTask = Validator.ValidateTrackCount(wc, fullyMatchedIDs[i], trackCount);
validateTask.Wait();
if (!validateTask.Result)
#else
if (!await Validator.ValidateTrackCount(wc, fullyMatchedIDs[i], trackCount))
#endif
continue;
// Fill in the fields from the existing ID
resultProgress?.Report(Result.Success($"Filling fields from existing ID {fullyMatchedIDs[i]}..."));
#if NET40
var fillTask = Task.Factory.StartNew(() => Builder.FillFromId(wc, info, fullyMatchedIDs[i], options.PullAllInformation));
fillTask.Wait();
_ = fillTask.Result;
#else
_ = await Builder.FillFromId(wc, info, fullyMatchedIDs[i], options.PullAllInformation);
#endif
resultProgress?.Report(Result.Success("Information filling complete!"));
// Set the fully matched ID to the current
info.FullyMatchedID = fullyMatchedIDs[i];
break;
}
// Clear out fully matched IDs from the partial list
if (info.FullyMatchedID.HasValue)
{
if (info.PartiallyMatchedIDs.Count == 1)
info.PartiallyMatchedIDs = null;
else
info.PartiallyMatchedIDs.Remove(info.FullyMatchedID.Value);
}
return true;
}
#endregion
#region Helper Functions
/// <summary>
/// Formats a list of volume labels and their corresponding filesystems
/// </summary>
/// <param name="labels">Dictionary of volume labels and their filesystems</param>
/// <returns>Formatted string of volume labels and their filesystems</returns>
private static string? FormatVolumeLabels(string? driveLabel, Dictionary<string, List<string>>? labels)
{
// Must have at least one label to format
if (driveLabel == null && (labels == null || labels.Count == 0))
return null;
// If no labels given, use drive label
if (labels == null || labels.Count == 0)
{
// Ignore common volume labels
if (Drive.GetRedumpSystemFromVolumeLabel(driveLabel) != null)
return null;
return driveLabel;
}
// If only one label, don't mention fs
string firstLabel = labels.First().Key;
if (labels.Count == 1 && (firstLabel == driveLabel || driveLabel == null))
{
// Ignore common volume labels
if (Drive.GetRedumpSystemFromVolumeLabel(firstLabel) != null)
return null;
return firstLabel;
}
// Otherwise, state filesystem for each label
List<string> volLabels = [];
// Begin formatted output with the label from Windows, if it is unique and not a common volume label
if (driveLabel != null && !labels.TryGetValue(driveLabel, out List<string>? value) && Drive.GetRedumpSystemFromVolumeLabel(driveLabel) == null)
volLabels.Add(driveLabel);
// Add remaining labels with their corresponding filesystems
foreach (KeyValuePair<string, List<string>> label in labels)
{
// Ignore common volume labels
if (Drive.GetRedumpSystemFromVolumeLabel(label.Key) == null)
volLabels.Add($"{label.Key} ({string.Join(", ", [.. label.Value])})");
}
// Print each label separated by a comma and a space
if (volLabels.Count == 0)
return null;
return string.Join(", ", [.. volLabels]);
}
#endregion
}
}

View File

@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Core.Converters;
namespace MPF.Core.UI.ComboBoxItems
{
/// <summary>
/// A generic combo box element
/// </summary>
/// <typeparam name="T">Enum type representing the possible values</typeparam>
public class Element<T> : IEquatable<Element<T>>, IElement where T : struct, Enum
{
private readonly T Data;
public Element(T data) => Data = data;
/// <summary>
/// Allow elements to be used as their internal enum type
/// </summary>
/// <param name="item"></param>
public static implicit operator T? (Element<T> item) => item?.Data;
/// <inheritdoc/>
public string Name => EnumConverter.GetLongName(Data);
public override string ToString() => Name;
/// <summary>
/// Internal enum value
/// </summary>
public T Value => Data;
/// <summary>
/// Determine if the item is selected or not
/// </summary>
/// <remarks>Only applies to CheckBox type</remarks>
public bool IsChecked { get; set; }
/// <summary>
/// Generate all elements associated with the data enum type
/// </summary>
/// <returns></returns>
public static IEnumerable<Element<T>> GenerateElements()
{
return Enum.GetValues(typeof(T))
.OfType<T>()
.Select(e => new Element<T>(e));
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return Equals(obj as Element<T>);
}
/// <inheritdoc/>
public bool Equals(Element<T>? other)
{
if (other == null)
return false;
return Name == other.Name;
}
/// <inheritdoc/>
public override int GetHashCode() => base.GetHashCode();
}
}

View File

@@ -0,0 +1,10 @@
namespace MPF.Core.UI.ComboBoxItems
{
public interface IElement
{
/// <summary>
/// Display name for the combo box element
/// </summary>
string Name { get; }
}
}

View File

@@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.UI.ComboBoxItems
{
/// <summary>
/// Represents a single item in the System combo box
/// </summary>
public class RedumpSystemComboBoxItem : IEquatable<RedumpSystemComboBoxItem>, IElement
{
private readonly object? Data;
public RedumpSystemComboBoxItem(RedumpSystem? system) => Data = system;
public RedumpSystemComboBoxItem(SystemCategory? category) => Data = category;
public static implicit operator RedumpSystem?(RedumpSystemComboBoxItem item) => item.Data as RedumpSystem?;
/// <inheritdoc/>
public string Name
{
get
{
if (IsHeader)
return "---------- " + (Data as SystemCategory?).LongName() + " ----------";
else
return (Data as RedumpSystem?).LongName() ?? "No system selected";
}
}
public override string ToString() => Name;
/// <summary>
/// Internal enum value
/// </summary>
public RedumpSystem? Value => Data as RedumpSystem?;
/// <summary>
/// Determines if the item is a header value
/// </summary>
public bool IsHeader => Data is SystemCategory?;
/// <summary>
/// Determines if the item is a standard system value
/// </summary>
public bool IsSystem => Data is RedumpSystem?;
/// <summary>
/// Generate all elements for the known system combo box
/// </summary>
/// <returns></returns>
public static IEnumerable<RedumpSystemComboBoxItem> GenerateElements()
{
var knownSystems = Enum.GetValues(typeof(RedumpSystem))
.OfType<RedumpSystem?>()
.Where(s => !s.IsMarker() && s.GetCategory() != SystemCategory.NONE)
.ToList();
Dictionary<SystemCategory, List<RedumpSystem?>> mapping = knownSystems
.GroupBy(s => s.GetCategory())
.ToDictionary(
k => k.Key,
v => v
.OrderBy(s => s.LongName())
.ToList()
);
var systemsValues = new List<RedumpSystemComboBoxItem>
{
new RedumpSystemComboBoxItem((RedumpSystem?)null),
};
foreach (var group in mapping)
{
systemsValues.Add(new RedumpSystemComboBoxItem(group.Key));
group.Value.ForEach(system => systemsValues.Add(new RedumpSystemComboBoxItem(system)));
}
return systemsValues;
}
/// <inheritdoc/>
public override bool Equals(object? obj)
{
return Equals(obj as RedumpSystemComboBoxItem);
}
/// <inheritdoc/>
public bool Equals(RedumpSystemComboBoxItem? other)
{
if (other == null)
return false;
return Value == other.Value;
}
/// <inheritdoc/>
public override int GetHashCode() => base.GetHashCode();
}
}

View File

@@ -0,0 +1,432 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using BinaryObjectScanner;
using MPF.Core.Data;
using MPF.Core.UI.ComboBoxItems;
using MPF.Core.Utilities;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.UI.ViewModels
{
/// <summary>
/// Constructor
/// </summary>
public class CheckDumpViewModel : INotifyPropertyChanged
{
#region Fields
/// <summary>
/// Access to the current options
/// </summary>
public Data.Options Options
{
get => _options;
}
private readonly Data.Options _options;
/// <summary>
/// Indicates if SelectionChanged events can be executed
/// </summary>
public bool CanExecuteSelectionChanged { get; private set; } = false;
/// <inheritdoc/>
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
#region Properties
/// <summary>
/// Currently selected system value
/// </summary>
public RedumpSystem? CurrentSystem
{
get => _currentSystem;
set
{
_currentSystem = value;
TriggerPropertyChanged(nameof(CurrentSystem));
}
}
private RedumpSystem? _currentSystem;
/// <summary>
/// Indicates the status of the system type combo box
/// </summary>
public bool SystemTypeComboBoxEnabled
{
get => _systemTypeComboBoxEnabled;
set
{
_systemTypeComboBoxEnabled = value;
TriggerPropertyChanged(nameof(SystemTypeComboBoxEnabled));
}
}
private bool _systemTypeComboBoxEnabled;
/// <summary>
/// Currently selected media type value
/// </summary>
public MediaType? CurrentMediaType
{
get => _currentMediaType;
set
{
_currentMediaType = value;
TriggerPropertyChanged(nameof(CurrentMediaType));
}
}
private MediaType? _currentMediaType;
/// <summary>
/// Indicates the status of the media type combo box
/// </summary>
public bool MediaTypeComboBoxEnabled
{
get => _mediaTypeComboBoxEnabled;
set
{
_mediaTypeComboBoxEnabled = value;
TriggerPropertyChanged(nameof(MediaTypeComboBoxEnabled));
}
}
private bool _mediaTypeComboBoxEnabled;
/// <summary>
/// Currently provided input path
/// </summary>
public string? InputPath
{
get => _inputPath;
set
{
_inputPath = value;
TriggerPropertyChanged(nameof(InputPath));
}
}
private string? _inputPath;
/// <summary>
/// Indicates the status of the input path text box
/// </summary>
public bool InputPathTextBoxEnabled
{
get => _inputPathTextBoxEnabled;
set
{
_inputPathTextBoxEnabled = value;
TriggerPropertyChanged(nameof(InputPathTextBoxEnabled));
}
}
private bool _inputPathTextBoxEnabled;
/// <summary>
/// Indicates the status of the input path browse button
/// </summary>
public bool InputPathBrowseButtonEnabled
{
get => _inputPathBrowseButtonEnabled;
set
{
_inputPathBrowseButtonEnabled = value;
TriggerPropertyChanged(nameof(InputPathBrowseButtonEnabled));
}
}
private bool _inputPathBrowseButtonEnabled;
/// <summary>
/// Currently selected dumping program
/// </summary>
public InternalProgram CurrentProgram
{
get => _currentProgram;
set
{
_currentProgram = value;
TriggerPropertyChanged(nameof(CurrentProgram));
}
}
private InternalProgram _currentProgram;
/// <summary>
/// Indicates the status of the dumping program combo box
/// </summary>
public bool DumpingProgramComboBoxEnabled
{
get => _dumpingProgramComboBoxEnabled;
set
{
_dumpingProgramComboBoxEnabled = value;
TriggerPropertyChanged(nameof(DumpingProgramComboBoxEnabled));
}
}
private bool _dumpingProgramComboBoxEnabled;
/// <summary>
/// Indicates the status of the check dump button
/// </summary>
public bool CheckDumpButtonEnabled
{
get => _checkDumpButtonEnabled;
set
{
_checkDumpButtonEnabled = value;
TriggerPropertyChanged(nameof(CheckDumpButtonEnabled));
}
}
private bool _checkDumpButtonEnabled;
#endregion
#region List Properties
/// <summary>
/// Current list of supported media types
/// </summary>
public List<Element<MediaType>>? MediaTypes
{
get => _mediaTypes;
set
{
_mediaTypes = value;
TriggerPropertyChanged(nameof(MediaTypes));
}
}
private List<Element<MediaType>>? _mediaTypes;
/// <summary>
/// Current list of supported system profiles
/// </summary>
public List<RedumpSystemComboBoxItem> Systems
{
get => _systems;
set
{
_systems = value;
TriggerPropertyChanged(nameof(Systems));
}
}
private List<RedumpSystemComboBoxItem> _systems;
/// <summary>
/// List of available internal programs
/// </summary>
public List<Element<InternalProgram>> InternalPrograms
{
get => _internalPrograms;
set
{
_internalPrograms = value;
TriggerPropertyChanged(nameof(InternalPrograms));
}
}
private List<Element<InternalProgram>> _internalPrograms;
#endregion
/// <summary>
/// Constructor for pure view model
/// </summary>
public CheckDumpViewModel()
{
_options = OptionsLoader.LoadFromConfig();
_internalPrograms = [];
_inputPath = string.Empty;
_systems = [];
SystemTypeComboBoxEnabled = true;
InputPathTextBoxEnabled = true;
InputPathBrowseButtonEnabled = true;
MediaTypeComboBoxEnabled = true;
DumpingProgramComboBoxEnabled = true;
CheckDumpButtonEnabled = false;
MediaTypes = [];
Systems = RedumpSystemComboBoxItem.GenerateElements().ToList();
InternalPrograms = [];
PopulateMediaType();
PopulateInternalPrograms();
EnableEventHandlers();
}
#region Property Updates
/// <summary>
/// Trigger a property changed event
/// </summary>
private void TriggerPropertyChanged(string propertyName)
{
// Disable event handlers temporarily
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
// If the property change event is initialized
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
#endregion
#region UI Commands
/// <summary>
/// Change the currently selected system
/// </summary>
public void ChangeSystem()
{
PopulateMediaType();
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
/// Change the currently selected media type
/// </summary>
public void ChangeMediaType()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
/// Change the currently selected dumping program
/// </summary>
public void ChangeDumpingProgram()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
/// <summary>
/// Change the currently selected input path
/// </summary>
public void ChangeInputPath()
{
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
}
#endregion
#region Population
/// <summary>
/// Populate media type according to system type
/// </summary>
private void PopulateMediaType()
{
// Disable other UI updates
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
if (this.CurrentSystem != null)
{
var mediaTypeValues = this.CurrentSystem.MediaTypes();
int index = mediaTypeValues.FindIndex(m => m == this.CurrentMediaType);
MediaTypes = mediaTypeValues.Select(m => new Element<MediaType>(m ?? MediaType.NONE)).ToList();
this.MediaTypeComboBoxEnabled = MediaTypes.Count > 1;
this.CurrentMediaType = (index > -1 ? MediaTypes[index] : MediaTypes[0]);
}
else
{
this.MediaTypeComboBoxEnabled = false;
this.MediaTypes = null;
this.CurrentMediaType = null;
}
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
/// <summary>
/// Populate media type according to system type
/// </summary>
private void PopulateInternalPrograms()
{
// Disable other UI updates
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
DisableEventHandlers();
// Get the current internal program
InternalProgram internalProgram = this.Options.InternalProgram;
// Create a static list of supported Check programs, not everything
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.CleanRip, InternalProgram.UmdImageCreator };
InternalPrograms = internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
// Select the current default dumping program
int currentIndex = InternalPrograms.FindIndex(m => m == internalProgram);
this.CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
// Reenable event handlers, if necessary
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
}
#endregion
#region UI Functionality
private bool ShouldEnableCheckDumpButton()
{
return this.CurrentSystem != null && this.CurrentMediaType != null && !string.IsNullOrEmpty(this.InputPath);
}
/// <summary>
/// Enable all textbox and combobox event handlers
/// </summary>
private void EnableEventHandlers()
{
CanExecuteSelectionChanged = true;
}
/// <summary>
/// Disable all textbox and combobox event handlers
/// </summary>
private void DisableEventHandlers()
{
CanExecuteSelectionChanged = false;
}
#endregion
#region MPF.Check
/// <summary>
/// Performs MPF.Check functionality
/// </summary>
/// <returns>An error message if failed, otherwise string.Empty/null</returns>
public string? CheckDump(Func<SubmissionInfo?, (bool?, SubmissionInfo?)> processUserInfo)
{
if (string.IsNullOrEmpty(InputPath))
return "Invalid Input path";
if (!File.Exists(this.InputPath!.Trim('"')))
return "Input Path is not a valid file";
// Populate an environment
var env = new DumpEnvironment(Options, Path.GetFullPath(this.InputPath.Trim('"')), null, this.CurrentSystem, this.CurrentMediaType, this.CurrentProgram, parameters: null);
// Make new Progress objects
var resultProgress = new Progress<Result>();
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
var protectionProgress = new Progress<ProtectionProgress>();
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
// Finally, attempt to do the output dance
#if NET40
var resultTask = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress);
resultTask.Wait();
var result = resultTask.Result;
#else
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress, processUserInfo).ConfigureAwait(false).GetAwaiter().GetResult();
#endif
return result.Message;
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,247 @@
using System.Collections.Generic;
using System.Linq;
using MPF.Core.Data;
using MPF.Core.UI.ComboBoxItems;
using MPF.Core.Utilities;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.UI.ViewModels
{
public class DiscInformationViewModel
{
#region Fields
/// <summary>
/// Application-level Options object
/// </summary>
public Options Options { get; private set; }
/// <summary>
/// SubmissionInfo object to fill and save
/// </summary>
public SubmissionInfo SubmissionInfo { get; private set; }
#endregion
#region Lists
/// <summary>
/// List of available disc categories
/// </summary>
public List<Element<DiscCategory>> Categories { get; private set; } = Element<DiscCategory>.GenerateElements().ToList();
/// <summary>
/// List of available regions
/// </summary>
public List<Element<Region>> Regions { get; private set; } = Element<Region>.GenerateElements().ToList();
/// <summary>
/// List of Redump-supported Regions
/// </summary>
private static readonly List<Region> RedumpRegions = new()
{
Region.Argentina,
Region.Asia,
Region.AsiaEurope,
Region.AsiaUSA,
Region.Australia,
Region.AustraliaGermany,
Region.AustraliaNewZealand,
Region.Austria,
Region.AustriaSwitzerland,
Region.Belarus,
Region.Belgium,
Region.BelgiumNetherlands,
Region.Brazil,
Region.Bulgaria,
Region.Canada,
Region.China,
Region.Croatia,
Region.Czechia,
Region.Denmark,
Region.Estonia,
Region.Europe,
Region.EuropeAsia,
Region.EuropeAustralia,
Region.EuropeCanada,
Region.EuropeGermany,
Region.Export,
Region.Finland,
Region.France,
Region.FranceSpain,
Region.Germany,
Region.GreaterChina,
Region.Greece,
Region.Hungary,
Region.Iceland,
Region.India,
Region.Ireland,
Region.Israel,
Region.Italy,
Region.Japan,
Region.JapanAsia,
Region.JapanEurope,
Region.JapanKorea,
Region.JapanUSA,
Region.SouthKorea,
Region.LatinAmerica,
Region.Lithuania,
Region.Netherlands,
Region.NewZealand,
Region.Norway,
Region.Poland,
Region.Portugal,
Region.Romania,
Region.RussianFederation,
Region.Scandinavia,
Region.Serbia,
Region.Singapore,
Region.Slovakia,
Region.SouthAfrica,
Region.Spain,
Region.SpainPortugal,
Region.Sweden,
Region.Switzerland,
Region.Taiwan,
Region.Thailand,
Region.Turkey,
Region.UnitedArabEmirates,
Region.UnitedKingdom,
Region.UKAustralia,
Region.Ukraine,
Region.UnitedStatesOfAmerica,
Region.USAAsia,
Region.USAAustralia,
Region.USABrazil,
Region.USACanada,
Region.USAEurope,
Region.USAGermany,
Region.USAJapan,
Region.USAKorea,
Region.World,
};
/// <summary>
/// List of available languages
/// </summary>
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
/// <summary>
/// List of Redump-supported Languages
/// </summary>
private static readonly List<Language> RedumpLanguages = new()
{
Language.Afrikaans,
Language.Albanian,
Language.Arabic,
Language.Armenian,
Language.Basque,
Language.Belarusian,
Language.Bulgarian,
Language.Catalan,
Language.Chinese,
Language.Croatian,
Language.Czech,
Language.Danish,
Language.Dutch,
Language.English,
Language.Estonian,
Language.Finnish,
Language.French,
Language.Gaelic,
Language.German,
Language.Greek,
Language.Hebrew,
Language.Hindi,
Language.Hungarian,
Language.Icelandic,
Language.Indonesian,
Language.Italian,
Language.Japanese,
Language.Korean,
Language.Latin,
Language.Latvian,
Language.Lithuanian,
Language.Macedonian,
Language.Norwegian,
Language.Polish,
Language.Portuguese,
Language.Panjabi,
Language.Romanian,
Language.Russian,
Language.Serbian,
Language.Slovak,
Language.Slovenian,
Language.Spanish,
Language.Swedish,
Language.Tamil,
Language.Thai,
Language.Turkish,
Language.Ukrainian,
Language.Vietnamese,
};
/// <summary>
/// List of available languages
/// </summary>
public List<Element<LanguageSelection>> LanguageSelections { get; private set; } = Element<LanguageSelection>.GenerateElements().ToList();
#endregion
/// <summary>
/// Constructor
/// </summary>
public DiscInformationViewModel(Options options, SubmissionInfo? submissionInfo)
{
Options = options;
SubmissionInfo = submissionInfo?.Clone() as SubmissionInfo ?? new SubmissionInfo();
}
#region Helpers
/// <summary>
/// Load the current contents of the base SubmissionInfo to the UI
/// </summary>
/// TODO: Convert selected list item to binding
public void Load()
{
if (SubmissionInfo.CommonDiscInfo?.Languages != null)
Languages.ForEach(l => l.IsChecked = SubmissionInfo.CommonDiscInfo.Languages.Contains(l));
if (SubmissionInfo.CommonDiscInfo?.LanguageSelection != null)
LanguageSelections.ForEach(ls => ls.IsChecked = SubmissionInfo.CommonDiscInfo.LanguageSelection.Contains(ls));
}
/// <summary>
/// Save the current contents of the UI to the base SubmissionInfo
/// </summary>
/// TODO: Convert selected list item to binding
public void Save()
{
if (SubmissionInfo.CommonDiscInfo == null)
SubmissionInfo.CommonDiscInfo = new CommonDiscInfoSection();
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
SubmissionInfo.CommonDiscInfo.Languages = [null];
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
SubmissionInfo.CommonDiscInfo.Title = InfoTool.NormalizeDiscTitle(SubmissionInfo.CommonDiscInfo.Title, SubmissionInfo.CommonDiscInfo.Languages);
}
/// <summary>
/// Repopulate the list of Languages based on Redump support
/// </summary>
public void SetRedumpLanguages()
{
this.Languages = RedumpLanguages.Select(l => new Element<Language>(l)).ToList();
}
/// <summary>
/// Repopulate the list of Regions based on Redump support
/// </summary>
public void SetRedumpRegions()
{
this.Regions = RedumpRegions.Select(r => new Element<Region>(r)).ToList();
}
#endregion
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,125 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using MPF.Core.Data;
using MPF.Core.UI.ComboBoxItems;
using SabreTools.RedumpLib.Web;
namespace MPF.Core.UI.ViewModels
{
/// <summary>
/// Constructor
/// </summary>
public class OptionsViewModel : INotifyPropertyChanged
{
#region Fields
/// <summary>
/// Title for the window
/// </summary>
public string? Title
{
get => _title;
set
{
_title = value;
TriggerPropertyChanged(nameof(Title));
}
}
private string? _title;
/// <summary>
/// Current set of options
/// </summary>
public Options Options { get; }
/// <summary>
/// Flag for if settings were saved or not
/// </summary>
public bool SavedSettings { get; set; }
/// <inheritdoc/>
public event PropertyChangedEventHandler? PropertyChanged;
#endregion
#region Lists
/// <summary>
/// List of available internal programs
/// </summary>
public static List<Element<InternalProgram>> InternalPrograms => PopulateInternalPrograms();
/// <summary>
/// Current list of supported system profiles
/// </summary>
public static List<RedumpSystemComboBoxItem> Systems => RedumpSystemComboBoxItem.GenerateElements().ToList();
#endregion
/// <summary>
/// Constructor for pure view model
/// </summary>
public OptionsViewModel()
{
Options = new Options();
}
/// <summary>
/// Constructor for in-code
/// </summary>
public OptionsViewModel(Options baseOptions)
{
Options = new Options(baseOptions);
}
#region Population
/// <summary>
/// Get a complete list of supported internal programs
/// </summary>
private static List<Element<InternalProgram>> PopulateInternalPrograms()
{
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.DiscImageCreator, InternalProgram.Aaru };
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
}
#endregion
#region UI Commands
/// <summary>
/// Test Redump login credentials
/// </summary>
#if NET40
public static Task<(bool?, string?)> TestRedumpLogin(string username, string password)
#else
public static async Task<(bool?, string?)> TestRedumpLogin(string username, string password)
#endif
{
#if NET40
return Task.Factory.StartNew(() => RedumpWebClient.ValidateCredentials(username, password));
#elif NETFRAMEWORK
return await Task.Run(() => RedumpWebClient.ValidateCredentials(username, password));
#else
return await RedumpHttpClient.ValidateCredentials(username, password);
#endif
}
#endregion
#region Property Updates
/// <summary>
/// Trigger a property changed event
/// </summary>
private void TriggerPropertyChanged(string propertyName)
{
// If the property change event is initialized
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}

View File

@@ -0,0 +1,174 @@
using System;
using System.IO;
namespace MPF.Core.Utilities
{
/// <summary>
/// Big endian reading overloads for BinaryReader
/// </summary>
public static class BinaryReaderExtensions
{
/// <summary>
/// Reads the specified number of bytes from the stream, starting from a specified point in the byte array.
/// </summary>
/// <param name="buffer">The buffer to read data into.</param>
/// <param name="index">The starting point in the buffer at which to begin reading into the buffer.</param>
/// <param name="count">The number of bytes to read.</param>
/// <returns>The number of bytes read into buffer. This might be less than the number of bytes requested if that many bytes are not available, or it might be zero if the end of the stream is reached.</returns>
public static int ReadBigEndian(this BinaryReader reader, byte[] buffer, int index, int count)
{
int retval = reader.Read(buffer, index, count);
Array.Reverse(buffer);
return retval;
}
/// <summary>
/// Reads the specified number of characters from the stream, starting from a specified point in the character array.
/// </summary>
/// <param name="buffer">The buffer to read data into.</param>
/// <param name="index">The starting point in the buffer at which to begin reading into the buffer.</param>
/// <param name="count">The number of characters to read.</param>
/// <returns>The total number of characters read into the buffer. This might be less than the number of characters requested if that many characters are not currently available, or it might be zero if the end of the stream is reached.</returns>
public static int ReadBigEndian(this BinaryReader reader, char[] buffer, int index, int count)
{
int retval = reader.Read(buffer, index, count);
Array.Reverse(buffer);
return retval;
}
/// <summary>
/// Reads the specified number of bytes from the current stream into a byte array and advances the current position by that number of bytes.
/// </summary>
/// <param name="count">The number of bytes to read. This value must be 0 or a non-negative number or an exception will occur.</param>
/// <returns>A byte array containing data read from the underlying stream. This might be less than the number of bytes requested if the end of the stream is reached.</returns>
public static byte[] ReadBytesBigEndian(this BinaryReader reader, int count)
{
byte[] retval = reader.ReadBytes(count);
Array.Reverse(retval);
return retval;
}
/// <summary>
/// Reads the specified number of characters from the current stream, returns the data in a character array, and advances the current position in accordance with the Encoding used and the specific character being read from the stream.
/// </summary>
/// <param name="count">The number of characters to read. This value must be 0 or a non-negative number or an exception will occur.</param>
/// <returns>A character array containing data read from the underlying stream. This might be less than the number of bytes requested if the end of the stream is reached.</returns>
public static char[] ReadCharsBigEndian(this BinaryReader reader, int count)
{
char[] retval = reader.ReadChars(count);
Array.Reverse(retval);
return retval;
}
/// <summary>
/// Reads a decimal value from the current stream and advances the current position of the stream by sixteen bytes.
/// </summary>
/// <returns>A decimal value read from the current stream.</returns>
public static decimal ReadDecimalBigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(16);
Array.Reverse(retval);
int i1 = BitConverter.ToInt32(retval, 0);
int i2 = BitConverter.ToInt32(retval, 4);
int i3 = BitConverter.ToInt32(retval, 8);
int i4 = BitConverter.ToInt32(retval, 12);
return new decimal(new int[] { i1, i2, i3, i4 });
}
/// <summary>
/// eads an 8-byte floating point value from the current stream and advances the current position of the stream by eight bytes.
/// </summary>
/// <returns>An 8-byte floating point value read from the current stream.</returns>
public static double ReadDoubleBigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(8);
Array.Reverse(retval);
return BitConverter.ToDouble(retval, 0);
}
/// <summary>
/// Reads a 2-byte signed integer from the current stream and advances the current position of the stream by two bytes.
/// </summary>
/// <returns>A 2-byte signed integer read from the current stream.</returns>
public static short ReadInt16BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(2);
Array.Reverse(retval);
return BitConverter.ToInt16(retval, 0);
}
/// <summary>
/// Reads a 4-byte signed integer from the current stream and advances the current position of the stream by four bytes.
/// </summary>
/// <returns>A 4-byte signed integer read from the current stream.</returns>
public static int ReadInt32BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(4);
Array.Reverse(retval);
return BitConverter.ToInt32(retval, 0);
}
/// <summary>
/// Reads an 8-byte signed integer from the current stream and advances the current position of the stream by eight bytes.
/// </summary>
/// <returns>An 8-byte signed integer read from the current stream.</returns>
public static long ReadInt64BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(8);
Array.Reverse(retval);
return BitConverter.ToInt64(retval, 0);
}
/// <summary>
/// Reads a 4-byte floating point value from the current stream and advances the current position of the stream by four bytes.
/// </summary>
/// <returns>A 4-byte floating point value read from the current stream.</returns>
public static float ReadSingleBigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(4);
Array.Reverse(retval);
return BitConverter.ToSingle(retval, 0);
}
/// <summary>
/// Reads a 2-byte unsigned integer from the current stream using little-endian encoding and advances the position of the stream by two bytes.
///
/// This API is not CLS-compliant.
/// </summary>
/// <returns>A 2-byte unsigned integer read from this stream.</returns>
public static ushort ReadUInt16BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(2);
Array.Reverse(retval);
return BitConverter.ToUInt16(retval, 0);
}
/// <summary>
/// Reads a 4-byte unsigned integer from the current stream and advances the position of the stream by four bytes.
///
/// This API is not CLS-compliant.
/// </summary>
/// <returns>A 4-byte unsigned integer read from this stream.</returns>
public static uint ReadUInt32BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(4);
Array.Reverse(retval);
return BitConverter.ToUInt32(retval, 0);
}
/// <summary>
/// Reads an 8-byte unsigned integer from the current stream and advances the position of the stream by eight bytes.
///
/// This API is not CLS-compliant.
/// </summary>
/// <returns>An 8-byte unsigned integer read from this stream.</returns>
public static ulong ReadUInt64BigEndian(this BinaryReader reader)
{
byte[] retval = reader.ReadBytes(8);
Array.Reverse(retval);
return BitConverter.ToUInt64(retval, 0);
}
}
}

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

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

View File

@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using MPF.Core.Converters;
using MPF.Core.Data;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Utilities
{
public static class EnumExtensions
{
/// <summary>
/// Determine if the media supports drive speeds
/// </summary>
/// <param name="type">MediaType value to check</param>
/// <returns>True if the media has variable dumping speeds, false otherwise</returns>
public static bool DoesSupportDriveSpeed(this MediaType? type)
{
return type switch
{
MediaType.CDROM
or MediaType.DVD
or MediaType.GDROM
or MediaType.HDDVD
or MediaType.BluRay
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => true,
_ => false,
};
}
/// <summary>
/// List all programs with their short usable names
/// </summary>
public static List<string> ListPrograms()
{
var programs = new List<string>();
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
{
if (((InternalProgram)val!) == InternalProgram.NONE)
continue;
programs.Add($"{((InternalProgram?)val).LongName()}");
}
return programs;
}
}
}

View File

@@ -0,0 +1,177 @@
using System;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace MPF.Core.Utilities
{
public static class Logging
{
/// <summary>
/// Process a chunk of text and send it to a handler
/// </summary>
/// <param name="reader">TextReader representing the input</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
#if NET20 || NET35
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#elif NET40
public static void OutputToLog(TextReader reader, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#else
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string>? handler)
#endif
{
// Initialize the required variables
char[] buffer = new char[256];
int read = 0;
var sb = new StringBuilder();
try
{
while (true)
{
// Try to read the next chunk of characters
#if NET20 || NET35
read = await Task.Run(() => reader.Read(buffer, 0, buffer.Length));
#elif NET40
var readTask = Task.Factory.StartNew(() => reader.Read(buffer, 0, buffer.Length));
readTask.Wait();
read = readTask.Result;
#else
read = await reader.ReadAsync(buffer, 0, buffer.Length);
#endif
if (read == 0)
{
Thread.Sleep(10);
continue;
}
// Convert the characters into a string
string line = new(buffer, 0, read);
// If we have no newline characters, store in the string builder
#if NETFRAMEWORK
if (!line.Contains("\r") && !line.Contains("\n"))
#else
if (!line.Contains('\r') && !line.Contains('\n'))
#endif
sb.Append(line);
// If we have a newline, append and log
#if NETFRAMEWORK
else if (line.Contains("\n") || line.Contains("\r\n"))
#else
else if (line.Contains('\n') || line.Contains("\r\n"))
#endif
ProcessNewLines(sb, line, baseClass, handler);
// If we have a carriage return only, append and log first and last instances
#if NETFRAMEWORK
else if (line.Contains("\r"))
#else
else if (line.Contains('\r'))
#endif
ProcessCarriageReturns(sb, line, baseClass, handler);
}
}
catch { }
finally
{
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
#else
handler?.Invoke(baseClass, sb.ToString());
#endif
}
}
/// <summary>
/// Process a chunk that contains newlines
/// </summary>
/// <param name="sb">StringBuilder to write from and append to</param>
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
#if NET20 || NET35 || NET40
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#else
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
#endif
{
line = line.Replace("\r\n", "\n");
var split = line.Split('\n');
for (int i = 0; i < split.Length; i++)
{
// If the chunk contains a carriage return, handle it like a separate line
#if NETFRAMEWORK
if (split[i].Contains("\r"))
#else
if (split[i].Contains('\r'))
#endif
{
ProcessCarriageReturns(sb, split[i], baseClass, handler);
continue;
}
// For the first item, append to anything existing and then write out
if (i == 0)
{
sb.Append(split[i]);
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
sb = new();
#else
handler?.Invoke(baseClass, sb.ToString());
sb.Clear();
#endif
}
// For the last item, just append so it's dealt with the next time
else if (i == split.Length - 1)
{
sb.Append(split[i]);
}
// For everything else, directly write out
else
{
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = split[i] });
#else
handler?.Invoke(baseClass, split[i]);
#endif
}
}
}
/// <summary>
/// Process a chunk that contains carriage returns
/// </summary>
/// <param name="sb">StringBuilder to write from and append to</param>
/// <param name="line">Current line to process</param>
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
/// <param name="handler">Event handler to be invoked to write to log</param>
#if NET20 || NET35 || NET40
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
#else
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
#endif
{
var split = line.Split('\r');
// Append and log the first
sb.Append(split[0]);
#if NET20 || NET35 || NET40
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
sb = new();
#else
handler?.Invoke(baseClass, sb.ToString());
sb.Clear();
#endif
// Append the last
sb.Append($"\r{split[split.Length - 1]}");
}
}
}

View File

@@ -0,0 +1,293 @@
using System;
using System.Collections.Generic;
using System.IO;
using MPF.Core.Converters;
using MPF.Core.Data;
using Newtonsoft.Json;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Utilities
{
public static class OptionsLoader
{
private const string ConfigurationPath = "config.json";
#region Arguments
/// <summary>
/// Process any standalone arguments for the program
/// </summary>
/// <returns>True if one of the arguments was processed, false otherwise</returns>
public static bool? ProcessStandaloneArguments(string[] args)
{
// Help options
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
return false;
// List options
if (args[0] == "-lm" || args[0] == "--listmedia")
{
Console.WriteLine("Supported Media Types:");
foreach (string mediaType in Extensions.ListMediaTypes())
{
Console.WriteLine(mediaType);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-lp" || args[0] == "--listprograms")
{
Console.WriteLine("Supported Programs:");
foreach (string program in EnumExtensions.ListPrograms())
{
Console.WriteLine(program);
}
Console.ReadLine();
return true;
}
else if (args[0] == "-ls" || args[0] == "--listsystems")
{
Console.WriteLine("Supported Systems:");
foreach (string system in Extensions.ListSystems())
{
Console.WriteLine(system);
}
Console.ReadLine();
return true;
}
return false;
}
/// <summary>
/// Process common arguments for all functionality
/// </summary>
/// <returns>True if all arguments pass, false otherwise</returns>
public static (bool, MediaType, RedumpSystem?, string?) ProcessCommonArguments(string[] args)
{
// All other use requires at least 3 arguments
if (args.Length < 3)
return (false, MediaType.NONE, null, "Invalid number of arguments");
// Check the MediaType
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
if (mediaType == MediaType.NONE)
return (false, MediaType.NONE, null, $"{args[0]} is not a recognized media type");
// Check the RedumpSystem
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
if (knownSystem == null)
return (false, MediaType.NONE, null, $"{args[1]} is not a recognized system");
return (true, mediaType, knownSystem, null);
}
/// <summary>
/// Load the current set of options from application arguments
/// </summary>
public static (Options, SubmissionInfo?, string?, int) LoadFromArguments(string[] args, int startIndex = 0)
{
// Create the output values with defaults
var options = new Options()
{
RedumpUsername = null,
RedumpPassword = null,
InternalProgram = InternalProgram.NONE,
AddFilenameSuffix = false,
OutputSubmissionJSON = false,
CompressLogFiles = false,
DeleteUnnecessaryFiles = false,
};
// Create the submission info to return, if necessary
SubmissionInfo? info = null;
string? parsedPath = null;
// These values require multiple parts to be active
bool scan = false, protectFile = false, hideDriveLetters = false;
// If we have no arguments, just return
if (args == null || args.Length == 0)
return (options, null, null, 0);
// If we have an invalid start index, just return
if (startIndex < 0 || startIndex >= args.Length)
return (options, null, null, startIndex);
// Loop through the arguments and parse out values
for (; startIndex < args.Length; startIndex++)
{
// Use specific program
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
{
string internalProgram = args[startIndex].Split('=')[1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
}
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
{
string internalProgram = args[startIndex + 1];
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
startIndex++;
}
// Redump login
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
{
string[] credentials = args[startIndex].Split('=')[1].Split(';');
options.RedumpUsername = credentials[0];
options.RedumpPassword = credentials[1];
}
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
{
options.RedumpUsername = args[startIndex + 1];
options.RedumpPassword = args[startIndex + 2];
startIndex += 2;
}
// Pull all information (requires Redump login)
else if (args[startIndex].Equals("-a") || args[startIndex].Equals("--pull-all"))
{
options.PullAllInformation = true;
}
// Use a device path for physical checks
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
{
parsedPath = args[startIndex].Split('=')[1];
}
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
{
parsedPath = args[startIndex + 1];
startIndex++;
}
// Scan for protection (requires device path)
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
{
scan = true;
}
// Output protection to separate file (requires scan for protection)
else if (args[startIndex].Equals("-f") || args[startIndex].Equals("--protect-file"))
{
protectFile = true;
}
// Hide drive letters from scan output (requires --protect-file)
else if (args[startIndex].Equals("-g") || args[startIndex].Equals("--hide-drive-letters"))
{
hideDriveLetters = true;
}
// Include seed info file
else if (args[startIndex].StartsWith("-l=") || args[startIndex].StartsWith("--load-seed="))
{
string seedInfo = args[startIndex].Split('=')[1];
info = Builder.CreateFromFile(seedInfo);
}
else if (args[startIndex] == "-l" || args[startIndex] == "--load-seed")
{
string seedInfo = args[startIndex + 1];
info = Builder.CreateFromFile(seedInfo);
startIndex++;
}
// Add filename suffix
else if (args[startIndex].Equals("-x") || args[startIndex].Equals("--suffix"))
{
options.AddFilenameSuffix = true;
}
// Output submission JSON
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
{
options.OutputSubmissionJSON = true;
}
// Compress log and extraneous files
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
{
options.CompressLogFiles = true;
}
// Delete unnecessary files files
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
{
options.DeleteUnnecessaryFiles = true;
}
// Default, we fall out
else
{
break;
}
}
// Now deal with the complex options
options.ScanForProtection = scan && !string.IsNullOrEmpty(parsedPath);
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrEmpty(parsedPath);
options.HideDriveLetters = hideDriveLetters && scan && protectFile && !string.IsNullOrEmpty(parsedPath);
return (options, info, parsedPath, startIndex);
}
/// <summary>
/// Return a list of supported arguments and descriptions
/// </summary>
public static List<string> PrintSupportedArguments()
{
var supportedArguments = new List<string>
{
"-u, --use <program> Dumping program output type [REQUIRED]",
"-c, --credentials <user> <pw> Redump username and password",
"-a, --pull-all Pull all information from Redump (requires --credentials)",
"-p, --path <drivepath> Physical drive path for additional checks",
"-s, --scan Enable copy protection scan (requires --path)",
"-f, --protect-file Output protection to separate file (requires --scan)",
"-g, --hide-drive-letters Hide drive letters from scan output (requires --protect-file)",
"-l, --load-seed <path> Load a seed submission JSON for user information",
"-x, --suffix Enable adding filename suffix",
"-j, --json Enable submission JSON output",
"-z, --zip Enable log file compression",
"-d, --delete Enable unnecessary file deletion",
};
return supportedArguments;
}
#endregion
#region Configuration
/// <summary>
/// Load the current set of options from the application configuration
/// </summary>
public static Options LoadFromConfig()
{
if (!File.Exists(ConfigurationPath))
{
_ = File.Create(ConfigurationPath);
return new Options();
}
var serializer = JsonSerializer.Create();
var reader = new StreamReader(ConfigurationPath);
var settings = serializer.Deserialize(reader, typeof(Dictionary<string, string?>)) as Dictionary<string, string?>;
return new Options(settings);
}
/// <summary>
/// Save the current set of options to the application configuration
/// </summary>
public static void SaveToConfig(Options options)
{
var serializer = JsonSerializer.Create();
var sw = new StreamWriter(ConfigurationPath) { AutoFlush = true };
var writer = new JsonTextWriter(sw) { Formatting = Formatting.Indented };
serializer.Serialize(writer, options.Settings, typeof(Dictionary<string, string>));
}
#endregion
}
}

265
MPF.Core/Utilities/Tools.cs Normal file
View File

@@ -0,0 +1,265 @@
using System;
using System.Net;
using System.Reflection;
using MPF.Core.Data;
using Newtonsoft.Json.Linq;
using SabreTools.RedumpLib.Data;
namespace MPF.Core.Utilities
{
public static class Tools
{
#region Byte Arrays
/// <summary>
/// Search for a byte array in another array
/// </summary>
public static bool Contains(this byte[] stack, byte[] needle, out int position, int start = 0, int end = -1)
{
// Initialize the found position to -1
position = -1;
// If either array is null or empty, we can't do anything
if (stack == null || stack.Length == 0 || needle == null || needle.Length == 0)
return false;
// If the needle array is larger than the stack array, it can't be contained within
if (needle.Length > stack.Length)
return false;
// If start or end are not set properly, set them to defaults
if (start < 0)
start = 0;
if (end < 0)
end = stack.Length - needle.Length;
for (int i = start; i < end; i++)
{
if (stack.EqualAt(needle, i))
{
position = i;
return true;
}
}
return false;
}
/// <summary>
/// See if a byte array starts with another
/// </summary>
public static bool StartsWith(this byte[] stack, byte[] needle)
{
return stack.Contains(needle, out int _, start: 0, end: 1);
}
/// <summary>
/// Get if a stack at a certain index is equal to a needle
/// </summary>
private static bool EqualAt(this byte[] stack, byte[] needle, int index)
{
// If we're too close to the end of the stack, return false
if (needle.Length >= stack.Length - index)
return false;
for (int i = 0; i < needle.Length; i++)
{
if (stack[i + index] != needle[i])
return false;
}
return true;
}
#endregion
#region Support
/// <summary>
/// Verify that, given a system and a media type, they are correct
/// </summary>
public static Result GetSupportStatus(RedumpSystem? system, MediaType? type)
{
// No system chosen, update status
if (system == null)
return Result.Failure("Please select a valid system");
// If we're on an unsupported type, update the status accordingly
return type switch
{
// Fully supported types
MediaType.BluRay
or MediaType.CDROM
or MediaType.DVD
or MediaType.FloppyDisk
or MediaType.HardDisk
or MediaType.CompactFlash
or MediaType.SDCard
or MediaType.FlashDrive
or MediaType.HDDVD => Result.Success($"{type.LongName()} ready to dump"),
// Partially supported types
MediaType.GDROM
or MediaType.NintendoGameCubeGameDisc
or MediaType.NintendoWiiOpticalDisc => Result.Success($"{type.LongName()} partially supported for dumping"),
// Special case for other supported tools
MediaType.UMD => Result.Failure($"{type.LongName()} supported for submission info parsing"),
// Specifically unknown type
MediaType.NONE => Result.Failure($"Please select a valid media type"),
// Undumpable but recognized types
_ => Result.Failure($"{type.LongName()} media are not supported for dumping"),
};
}
/// <summary>
/// Returns false if a given InternalProgram does not support a given MediaType
/// </summary>
public static bool ProgramSupportsMedia(InternalProgram program, MediaType? type)
{
// If the media type is not set, return false
if (type == null || type == MediaType.NONE)
return false;
return (program) switch
{
// Aaru
InternalProgram.Aaru when type == MediaType.BluRay => true,
InternalProgram.Aaru when type == MediaType.CDROM => true,
InternalProgram.Aaru when type == MediaType.CompactFlash => true,
InternalProgram.Aaru when type == MediaType.DVD => true,
InternalProgram.Aaru when type == MediaType.GDROM => true,
InternalProgram.Aaru when type == MediaType.FlashDrive => true,
InternalProgram.Aaru when type == MediaType.FloppyDisk => true,
InternalProgram.Aaru when type == MediaType.HardDisk => true,
InternalProgram.Aaru when type == MediaType.HDDVD => true,
InternalProgram.Aaru when type == MediaType.NintendoGameCubeGameDisc => true,
InternalProgram.Aaru when type == MediaType.NintendoWiiOpticalDisc => true,
InternalProgram.Aaru when type == MediaType.SDCard => true,
// DiscImageCreator
InternalProgram.DiscImageCreator when type == MediaType.BluRay => true,
InternalProgram.DiscImageCreator when type == MediaType.CDROM => true,
InternalProgram.DiscImageCreator when type == MediaType.CompactFlash => true,
InternalProgram.DiscImageCreator when type == MediaType.DVD => true,
InternalProgram.DiscImageCreator when type == MediaType.GDROM => true,
InternalProgram.DiscImageCreator when type == MediaType.FlashDrive => true,
InternalProgram.DiscImageCreator when type == MediaType.FloppyDisk => true,
InternalProgram.DiscImageCreator when type == MediaType.HardDisk => true,
InternalProgram.DiscImageCreator when type == MediaType.HDDVD => true,
InternalProgram.DiscImageCreator when type == MediaType.NintendoGameCubeGameDisc => true,
InternalProgram.DiscImageCreator when type == MediaType.NintendoWiiOpticalDisc => true,
InternalProgram.DiscImageCreator when type == MediaType.SDCard => true,
// Redumper
InternalProgram.Redumper when type == MediaType.BluRay => true,
InternalProgram.Redumper when type == MediaType.CDROM => true,
InternalProgram.Redumper when type == MediaType.DVD => true,
InternalProgram.Redumper when type == MediaType.GDROM => true,
InternalProgram.Redumper when type == MediaType.HDDVD => true,
// Default
_ => false,
};
}
#endregion
#region Versioning
/// <summary>
/// Check for a new MPF version
/// </summary>
/// <returns>
/// Bool representing if the values are different.
/// String representing the message to display the the user.
/// String representing the new release URL.
/// </returns>
public static (bool different, string message, string? url) CheckForNewVersion()
{
try
{
// Get current assembly version
var assemblyVersion = Assembly.GetEntryAssembly()?.GetName()?.Version;
if (assemblyVersion == null)
return (false, "Assembly version could not be determined", null);
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}.{assemblyVersion.Build}";
// Get the latest tag from GitHub
var (tag, url) = GetRemoteVersionAndUrl();
bool different = version != tag && tag != null;
string message = $"Local version: {version}"
+ $"{Environment.NewLine}Remote version: {tag}"
+ (different
? $"{Environment.NewLine}The update URL has been added copied to your clipboard"
: $"{Environment.NewLine}You have the newest version!");
return (different, message, url);
}
catch (Exception ex)
{
return (false, ex.ToString(), null);
}
}
/// <summary>
/// Get the current informational version formatted as a string
/// </summary>
public static string? GetCurrentVersion()
{
try
{
var assembly = Assembly.GetEntryAssembly();
if (assembly == null)
return null;
var assemblyVersion = Attribute.GetCustomAttribute(assembly, typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
return assemblyVersion?.InformationalVersion;
}
catch (Exception ex)
{
return ex.ToString();
}
}
/// <summary>
/// Get the latest version of MPF from GitHub and the release URL
/// </summary>
private static (string? tag, string? url) GetRemoteVersionAndUrl()
{
#if NET20 || NET35 || NET40
// Not supported in .NET Frameworks 2.0, 3.5, or 4.0
return (null, null);
#else
using var hc = new System.Net.Http.HttpClient();
#if NET452
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
#endif
// TODO: Figure out a better way than having this hardcoded...
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
var message = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
var latestReleaseJsonString = hc.SendAsync(message)?.ConfigureAwait(false).GetAwaiter().GetResult()
.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
if (latestReleaseJsonString == null)
return (null, null);
var latestReleaseJson = JObject.Parse(latestReleaseJsonString);
if (latestReleaseJson == null)
return (null, null);
var latestTag = latestReleaseJson["tag_name"]?.ToString();
var releaseUrl = latestReleaseJson["html_url"]?.ToString();
return (latestTag, releaseUrl);
#endif
}
#endregion
}
}

View File

@@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using MPF.Core.Converters;
using MPF.Core.Data;
using Xunit;
namespace MPF.Test.Core.Converters
{
public class EnumConverterTests
{
#region Cross-enumeration conversions
/// <summary>
/// DiscType values that map to InternalDriveType
/// </summary>
private static readonly DriveType[] _mappableDriveTypes =
[
DriveType.CDRom,
DriveType.Fixed,
DriveType.Removable,
];
/// <summary>
/// Check that every supported DriveType maps to an InternalDriveType
/// </summary>
/// <param name="driveType">DriveType value to check</param>
/// <param name="expectNull">True to expect a null mapping, false otherwise</param>
[Theory]
[MemberData(nameof(GenerateDriveTypeMappingTestData))]
public void ToInternalDriveTypeTest(DriveType driveType, bool expectNull)
{
var actual = driveType.ToInternalDriveType();
if (expectNull)
Assert.Null(actual);
else
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of DriveType values
/// </summary>
/// <returns>MemberData-compatible list of DriveType values</returns>
public static List<object?[]> GenerateDriveTypeMappingTestData()
{
var testData = new List<object?[]>() { new object?[] { null, true } };
foreach (DriveType driveType in Enum.GetValues(typeof(DriveType)))
{
if (_mappableDriveTypes.Contains(driveType))
testData.Add([driveType, false]);
else
testData.Add([driveType, true]);
}
return testData;
}
#endregion
#region Convert to Long Name
// TODO: Maybe add a test for the generic "GetLongName" method
/// <summary>
/// Check that every InternalProgram has a long name provided
/// </summary>
/// <param name="internalProgram">InternalProgram value to check</param>
[Theory]
[MemberData(nameof(GenerateInternalProgramTestData))]
public void InternalProgramLongNameTest(InternalProgram? internalProgram)
{
string actual = internalProgram.LongName();
Assert.NotNull(actual);
}
/// <summary>
/// Generate a test set of InternalProgram values
/// </summary>
/// <returns>MemberData-compatible list of InternalProgram values</returns>
public static List<object?[]> GenerateInternalProgramTestData()
{
var testData = new List<object?[]>() { new object?[] { null } };
foreach (InternalProgram? internalProgram in Enum.GetValues(typeof(InternalProgram)))
{
testData.Add([internalProgram]);
}
return testData;
}
#endregion
// TODO: Add from-string tests
}
}

View File

@@ -0,0 +1,42 @@
using MPF.Core.Data;
using Xunit;
namespace MPF.Test.Core.Data
{
public class ResultTests
{
[Fact]
public void EmptySuccessTest()
{
var actual = Result.Success();
Assert.True(actual);
Assert.Empty(actual.Message);
}
[Fact]
public void CustomMessageSuccessTest()
{
string message = "Success!";
var actual = Result.Success(message);
Assert.True(actual);
Assert.Equal(message, actual.Message);
}
[Fact]
public void EmptyFailureTest()
{
var actual = Result.Failure();
Assert.False(actual);
Assert.Empty(actual.Message);
}
[Fact]
public void CustomMessageFailureTest()
{
string message = "Failure!";
var actual = Result.Failure(message);
Assert.False(actual);
Assert.Equal(message, actual.Message);
}
}
}

View File

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

View File

@@ -0,0 +1,32 @@
using MPF.Core;
using MPF.Core.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Library
{
public class DumpEnvironmentTests
{
[Theory]
[InlineData(null, 'D', false, MediaType.NONE, false)]
[InlineData("", 'D', false, MediaType.NONE, false)]
[InlineData("cd F test.bin 8 /c2 20", 'F', false, MediaType.CDROM, true)]
[InlineData("fd A test.img", 'A', true, MediaType.FloppyDisk, true)]
[InlineData("dvd X test.iso 8 /raw", 'X', false, MediaType.FloppyDisk, false)]
[InlineData("stop D", 'D', false, MediaType.DVD, true)]
public void ParametersValidTest(string? parameters, char letter, bool isFloppy, MediaType? mediaType, bool expected)
{
var options = new Options() { InternalProgram = InternalProgram.DiscImageCreator };
// TODO: This relies on creating real objects for the drive. Can we mock this out instead?
var drive = isFloppy
? Drive.Create(InternalDriveType.Floppy, letter.ToString())
: Drive.Create(InternalDriveType.Optical, letter.ToString());
var env = new DumpEnvironment(options, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, null, parameters);
bool actual = env.ParametersValid();
Assert.Equal(expected, actual);
}
}
}

View File

@@ -0,0 +1,160 @@
using System.Collections.Generic;
using System.IO;
using MPF.Core;
using SabreTools.RedumpLib;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Library
{
public class InfoToolTests
{
[Theory]
[InlineData(null, "")]
[InlineData(" ", " ")]
[InlineData("super\\blah.bin", "super\\blah.bin")]
[InlineData("super\\hero\\blah.bin", "super\\hero\\blah.bin")]
[InlineData("super.hero\\blah.bin", "super.hero\\blah.bin")]
[InlineData("superhero\\blah.rev.bin", "superhero\\blah.rev.bin")]
[InlineData("super&hero\\blah.bin", "super&hero\\blah.bin")]
[InlineData("superhero\\blah&foo.bin", "superhero\\blah&foo.bin")]
public void NormalizeOutputPathsTest(string? outputPath, string? expectedPath)
{
if (!string.IsNullOrEmpty(expectedPath))
expectedPath = Path.GetFullPath(expectedPath);
string actualPath = InfoTool.NormalizeOutputPaths(outputPath, true);
Assert.Equal(expectedPath, actualPath);
}
[Fact]
public void ProcessSpecialFieldsCompleteTest()
{
// Create a new SubmissionInfo object
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = "This is a comments line\n[T:ISBN] ISBN Value",
CommentsSpecialFields = new Dictionary<SiteCode, string>()
{
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
},
Contents = "This is a contents line\n[T:GF] Game Footage",
ContentsSpecialFields = new Dictionary<SiteCode, string>()
{
[SiteCode.Patches] = "1.04 patch",
},
}
};
// Process the special fields
Formatter.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
Assert.Null(info.CommonDiscInfo.CommentsSpecialFields);
Assert.NotNull(info.CommonDiscInfo.Contents);
Assert.Null(info.CommonDiscInfo.ContentsSpecialFields);
// Split the values
string[] splitComments = info.CommonDiscInfo.Comments.Split('\n');
string[] splitContents = info.CommonDiscInfo.Contents.Split('\n');
// Validate the lines
Assert.Equal(3, splitComments.Length);
Assert.Equal(5, splitContents.Length);
}
[Fact]
public void ProcessSpecialFieldsNullObjectTest()
{
// Create a new SubmissionInfo object
var info = new SubmissionInfo()
{
CommonDiscInfo = null,
};
// Process the special fields
Formatter.ProcessSpecialFields(info);
// Validate
Assert.Null(info.CommonDiscInfo);
}
[Fact]
public void ProcessSpecialFieldsNullCommentsContentsTest()
{
// Create a new SubmissionInfo object
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = null,
CommentsSpecialFields = new Dictionary<SiteCode, string>()
{
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
},
Contents = null,
ContentsSpecialFields = new Dictionary<SiteCode, string>()
{
[SiteCode.Patches] = "1.04 patch",
},
}
};
// Process the special fields
Formatter.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
Assert.Null(info.CommonDiscInfo.CommentsSpecialFields);
Assert.NotNull(info.CommonDiscInfo.Contents);
Assert.Null(info.CommonDiscInfo.ContentsSpecialFields);
// Split the values
string[] splitComments = info.CommonDiscInfo.Comments.Split('\n');
string[] splitContents = info.CommonDiscInfo.Contents.Split('\n');
// Validate the lines
Assert.Single(splitComments);
Assert.Equal(2, splitContents.Length);
}
[Fact]
public void ProcessSpecialFieldsNullDictionariesTest()
{
// Create a new SubmissionInfo object
var info = new SubmissionInfo()
{
CommonDiscInfo = new CommonDiscInfoSection()
{
Comments = "This is a comments line\n[T:ISBN] ISBN Value",
CommentsSpecialFields = null,
Contents = "This is a contents line\n[T:GF] Game Footage",
ContentsSpecialFields = null,
}
};
// Process the special fields
Formatter.ProcessSpecialFields(info);
// Validate the basics
Assert.NotNull(info.CommonDiscInfo.Comments);
Assert.Null(info.CommonDiscInfo.CommentsSpecialFields);
Assert.NotNull(info.CommonDiscInfo.Contents);
Assert.Null(info.CommonDiscInfo.ContentsSpecialFields);
// Split the values
string[] splitComments = info.CommonDiscInfo.Comments.Split('\n');
string[] splitContents = info.CommonDiscInfo.Contents.Split('\n');
// Validate the lines
Assert.Equal(2, splitComments.Length);
Assert.Equal(2, splitContents.Length);
}
}
}

View File

@@ -0,0 +1,234 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MPF.Core;
using Xunit;
namespace MPF.Test.Library
{
public class ProtectionTests
{
[Fact]
public void SanitizeFoundProtectionsActiveMARKTest()
{
List<string> protections =
[
"ActiveMARK",
"ActiveMARK 5",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("ActiveMARK 5", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsCactusDataShieldTest()
{
List<string> protections =
[
"Cactus Data Shield 200",
"Cactus Data Shield 200 (Build 3.0.100a)",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Cactus Data Shield 200 (Build 3.0.100a)", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsCDCheckTest()
{
List<string> protections =
[
"Anything Else Protection",
"Executable-Based CD Check",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsCDCopsTest()
{
List<string> protections =
[
"CD-Cops",
"CD-Cops v1.2.0",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("CD-Cops v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsCDKeyTest()
{
List<string> protections =
[
"Anything Else Protection",
"CD-Key / Serial",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsEACdKeyTest()
{
List<string> protections =
[
"EA CdKey Registration Module",
"EA CdKey Registration Module v1.2.0",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("EA CdKey Registration Module v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsEADRMTest()
{
List<string> protections =
[
"EA DRM Protection",
"EA DRM Protection v1.2.0",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("EA DRM Protection v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsGFWLTest()
{
List<string> protections =
[
"Games for Windows LIVE",
"Games for Windows LIVE v1.2.0",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Games for Windows LIVE v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsGFWLZDPPTest()
{
List<string> protections =
[
"Games for Windows LIVE",
"Games for Windows LIVE Zero Day Piracy Protection",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Games for Windows LIVE, Games for Windows LIVE Zero Day Piracy Protection", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsImpulseReactorTest()
{
List<string> protections =
[
"Impulse Reactor",
"Impulse Reactor Core Module v1.2.0",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Impulse Reactor Core Module v1.2.0", sanitized);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public void SanitizeFoundProtectionsJoWoodXProtTest(int skip)
{
List<string> protections =
[
"JoWood X-Prot 1.2.0.00",
"JoWood X-Prot v2",
"JoWood X-Prot v1.4+",
"JoWood X-Prot v1.0-v1.3",
"JoWood X-Prot",
];
// Safeguard for the future
if (skip >= protections.Count)
throw new ArgumentException("Invalid skip value", nameof(skip));
// The list is in order of preference
protections = protections.Skip(skip).ToList();
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal(protections[0], sanitized);
}
[Fact]
public void SanitizeFoundProtectionsOnlineRegistrationTest()
{
List<string> protections =
[
"Anything Else Protection",
"Executable-Based Online Registration",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Anything Else Protection", sanitized);
}
[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public void SanitizeFoundProtectionStarForceTest(int skip)
{
List<string> protections =
[
"StarForce 1.20.000.000",
"StarForce 5 [Protected Module]",
"StarForce 5",
"StarForce 3-5",
"StarForce",
];
// Safeguard for the future
if (skip >= protections.Count)
throw new ArgumentException("Invalid skip value", nameof(skip));
// The list is in order of preference
protections = protections.Skip(skip).ToList();
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal(protections[0], sanitized);
}
[Fact]
public void SanitizeFoundProtectionsSysiphusTest()
{
List<string> protections =
[
"Sysiphus",
"Sysiphus v1.2.0",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("Sysiphus v1.2.0", sanitized);
}
[Fact]
public void SanitizeFoundProtectionsXCPTest()
{
List<string> protections =
[
"XCP",
"XCP v1.2.0",
];
string sanitized = Protection.SanitizeFoundProtections(protections);
Assert.Equal("XCP v1.2.0", sanitized);
}
}
}

38
MPF.Test/MPF.Test.csproj Normal file
View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0-windows;net8.0-windows</TargetFrameworks>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\MPF\MPF.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeCoverage" Version="17.9.0-preview-23531-01" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0-preview-23531-01" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
<PackageReference Include="xunit" Version="2.6.2" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.analyzers" Version="1.6.0" />
<PackageReference Include="xunit.assert" Version="2.6.2" />
<PackageReference Include="xunit.core" Version="2.6.2" />
<PackageReference Include="xunit.extensibility.core" Version="2.6.2" />
<PackageReference Include="xunit.extensibility.execution" Version="2.6.2" />
<PackageReference Include="xunit.runner.console" Version="2.6.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,270 @@
using System;
using System.Collections.Generic;
using MPF.Core.Data;
using MPF.Core.Modules.DiscImageCreator;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Modules
{
public class DiscImageCreatorTests
{
#region Old Tests
[Theory]
[InlineData(RedumpSystem.MicrosoftXbox, MediaType.CDROM, CommandStrings.CompactDisc)]
[InlineData(RedumpSystem.MicrosoftXbox, MediaType.DVD, CommandStrings.XBOX)]
[InlineData(RedumpSystem.MicrosoftXbox, MediaType.LaserDisc, null)]
[InlineData(RedumpSystem.SonyPlayStation3, MediaType.BluRay, CommandStrings.BluRay)]
[InlineData(RedumpSystem.AppleMacintosh, MediaType.FloppyDisk, CommandStrings.Floppy)]
[InlineData(RedumpSystem.RawThrillsVarious, MediaType.GDROM, null)]
public void ParametersFromSystemAndTypeTest(RedumpSystem? knownSystem, MediaType? mediaType, string? expected)
{
var options = new Options();
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
Assert.Equal(expected, actual.BaseCommand);
}
[Theory]
[InlineData(RedumpSystem.AppleMacintosh, MediaType.LaserDisc, null)] // Deliberately unsupported
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, new string[] { FlagStrings.Raw })]
public void ParametersFromOptionsSpecialDefaultTest(RedumpSystem? knownSystem, MediaType? mediaType, string[]? expected)
{
var options = new Options();
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
}
[Theory]
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, 1000, new string[] { FlagStrings.C2Opcode })]
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, -1, new string[] { FlagStrings.C2Opcode })]
public void ParametersFromOptionsC2RereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadC2, string[] expected)
{
var options = new Options { DICRereadCount = rereadC2 };
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (rereadC2 == -1 || !knownSystem.MediaTypes().Contains(mediaType))
Assert.Null(actual.C2OpcodeValue[0]);
else
Assert.Equal(rereadC2, actual.C2OpcodeValue[0]);
}
[Theory]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, 1000, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, -1, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.BluRay, 1000, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.BluRay, -1, new string[] { FlagStrings.DVDReread })]
public void ParametersFromOptionsDVDRereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadDVDBD, string[] expected)
{
var options = new Options { DICDVDRereadCount = rereadDVDBD };
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (rereadDVDBD == -1 || !knownSystem.MediaTypes().Contains(mediaType))
Assert.Null(actual.DVDRereadValue);
else
Assert.Equal(rereadDVDBD, actual.DVDRereadValue);
}
[Theory]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, true, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, false, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.MultiSectorRead, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, false, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, new string[] { FlagStrings.DVDReread })]
public void ParametersFromOptionsMultiSectorReadTest(RedumpSystem? knownSystem, MediaType? mediaType, bool multiSectorRead, string[] expected)
{
var options = new Options { DICMultiSectorRead = multiSectorRead };
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (expectedSet.Count != 1 && multiSectorRead)
Assert.Equal(0, actual.MultiSectorReadValue);
}
[Theory]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, true, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, false, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, false, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, new string[] { FlagStrings.DVDReread, FlagStrings.ScanFileProtect })]
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, new string[] { FlagStrings.DVDReread })]
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, new string[] { FlagStrings.DVDReread })]
public void ParametersFromOptionsParanoidModeTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoidMode, string[] expected)
{
var options = new Options { DICParanoidMode = paranoidMode };
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
HashSet<string> actualSet = GenerateUsedKeys(actual);
Assert.Equal(expectedSet, actualSet);
if (paranoidMode)
{
if (actualSet.Contains(FlagStrings.ScanSectorProtect))
Assert.True(actual[FlagStrings.ScanSectorProtect]);
if (actualSet.Contains(FlagStrings.SubchannelReadLevel))
{
Assert.True(actual[FlagStrings.SubchannelReadLevel]);
Assert.Equal(2, actual.SubchannelReadLevelValue);
}
}
else
{
if (actualSet.Contains(FlagStrings.ScanSectorProtect))
Assert.False(actual[FlagStrings.ScanSectorProtect]);
if (actualSet.Contains(FlagStrings.SubchannelReadLevel))
Assert.False(actual[FlagStrings.SubchannelReadLevel]);
Assert.Null(actual.SubchannelReadLevelValue);
}
}
[Theory]
[InlineData(null, false)]
[InlineData("", false)]
[InlineData("cd F test.bin 8 /c2 20", true)]
[InlineData("fd A test.img", true)]
[InlineData("dvd X super\\test.iso 8 /raw", true)]
[InlineData("bd D longer\\path_test.iso 16", true)]
[InlineData("stop D", true)]
[InlineData("ls", false)]
public void ValidateParametersTest(string? parameters, bool expected)
{
var actual = new Parameters(parameters);
Assert.Equal(expected, actual.IsValid());
}
[Theory]
[InlineData(MediaType.CDROM, ".bin")]
[InlineData(MediaType.DVD, ".iso")]
[InlineData(MediaType.LaserDisc, ".raw")]
[InlineData(MediaType.NintendoWiiUOpticalDisc, ".wud")]
[InlineData(MediaType.FloppyDisk, ".img")]
[InlineData(MediaType.Cassette, ".wav")]
[InlineData(MediaType.NONE, null)]
public void MediaTypeToExtensionTest(MediaType? mediaType, string? expected)
{
var actual = Converters.Extension(mediaType);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(CommandStrings.Audio, MediaType.CDROM)]
[InlineData(CommandStrings.BluRay, MediaType.BluRay)]
[InlineData(CommandStrings.Close, null)]
[InlineData(CommandStrings.CompactDisc, MediaType.CDROM)]
[InlineData(CommandStrings.Data, MediaType.CDROM)]
[InlineData(CommandStrings.DigitalVideoDisc, MediaType.DVD)]
[InlineData(CommandStrings.Eject, null)]
[InlineData(CommandStrings.Floppy, MediaType.FloppyDisk)]
[InlineData(CommandStrings.GDROM, MediaType.GDROM)]
[InlineData(CommandStrings.MDS, null)]
[InlineData(CommandStrings.Reset, null)]
[InlineData(CommandStrings.SACD, MediaType.CDROM)]
[InlineData(CommandStrings.Start, null)]
[InlineData(CommandStrings.Stop, null)]
[InlineData(CommandStrings.Sub, null)]
[InlineData(CommandStrings.Swap, MediaType.GDROM)]
[InlineData(CommandStrings.XBOX, MediaType.DVD)]
public void BaseCommandToMediaTypeTest(string command, MediaType? expected)
{
MediaType? actual = Converters.ToMediaType(command);
Assert.Equal(expected, actual);
}
[Theory]
[InlineData(CommandStrings.Audio, RedumpSystem.AudioCD)]
[InlineData(CommandStrings.BluRay, RedumpSystem.SonyPlayStation3)]
[InlineData(CommandStrings.Close, null)]
[InlineData(CommandStrings.CompactDisc, RedumpSystem.IBMPCcompatible)]
[InlineData(CommandStrings.Data, RedumpSystem.IBMPCcompatible)]
[InlineData(CommandStrings.DigitalVideoDisc, RedumpSystem.IBMPCcompatible)]
[InlineData(CommandStrings.Eject, null)]
[InlineData(CommandStrings.Floppy, RedumpSystem.IBMPCcompatible)]
[InlineData(CommandStrings.GDROM, RedumpSystem.SegaDreamcast)]
[InlineData(CommandStrings.MDS, null)]
[InlineData(CommandStrings.Reset, null)]
[InlineData(CommandStrings.SACD, RedumpSystem.SuperAudioCD)]
[InlineData(CommandStrings.Start, null)]
[InlineData(CommandStrings.Stop, null)]
[InlineData(CommandStrings.Sub, null)]
[InlineData(CommandStrings.Swap, RedumpSystem.SegaDreamcast)]
[InlineData(CommandStrings.XBOX, RedumpSystem.MicrosoftXbox)]
public void BaseCommandToRedumpSystemTest(string command, RedumpSystem? expected)
{
RedumpSystem? actual = Converters.ToRedumpSystem(command);
Assert.Equal(expected, actual);
}
#endregion
[Fact]
public void DiscImageCreatorAudioParametersTest()
{
string originalParameters = "audio F \"ISO\\Audio CD\\Audio CD.bin\" 72 -5 0";
// Validate that a common audio commandline is parsed
var parametersObject = new Parameters(originalParameters);
Assert.NotNull(parametersObject);
// Validate that the same set of parameters are generated on the output
var newParameters = parametersObject.GenerateParameters();
Assert.NotNull(newParameters);
Assert.Equal(originalParameters, newParameters);
}
[Fact]
public void DiscImageCreatorDataParametersTest()
{
string originalParameters = "data F \"ISO\\Data CD\\Data CD.bin\" 72 -5 0";
// Validate that a common audio commandline is parsed
var parametersObject = new Parameters(originalParameters);
Assert.NotNull(parametersObject);
// Validate that the same set of parameters are generated on the output
var newParameters = parametersObject.GenerateParameters();
Assert.NotNull(newParameters);
Assert.Equal(originalParameters, newParameters);
}
/// <summary>
/// Generate a HashSet of keys that are considered to be set
/// </summary>
/// <param name="parameters">Parameters object to get keys from</param>
/// <returns>HashSet representing the strings</returns>
private static HashSet<string> GenerateUsedKeys(Parameters parameters)
{
var usedKeys = new HashSet<string>();
if (parameters?.Keys == null)
return usedKeys;
foreach (string key in parameters.Keys)
{
if (parameters[key] == true)
usedKeys.Add(key);
}
return usedKeys;
}
}
}

View File

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

View File

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

View File

@@ -0,0 +1,21 @@
using MPF.Core.Data;
using SabreTools.RedumpLib.Data;
using Xunit;
namespace MPF.Test.Data
{
public class UIElementsTest
{
[Theory]
[InlineData(MediaType.CDROM, 72)]
[InlineData(MediaType.DVD, 24)]
[InlineData(MediaType.BluRay, 16)]
[InlineData(MediaType.LaserDisc, 1)]
[InlineData(null, 1)]
public void GetAllowedDriveSpeedForMediaTypeTest(MediaType? mediaType, int maxExpected)
{
var actual = Interface.GetSpeedsForMediaType(mediaType);
Assert.Equal(maxExpected, actual[actual.Count - 1]);
}
}
}

27
MPF.UI.Core/Constants.cs Normal file
View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Media;
using static MPF.Core.Data.Interface;
namespace MPF.UI.Core
{
/// <summary>
/// Variables for UI elements
/// </summary>
public static class Constants
{
// 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);
#if NET20 || NET35 || NET40
private static DoubleCollection GetDoubleCollectionFromIntList(IList<int> list)
#else
private static DoubleCollection GetDoubleCollectionFromIntList(IReadOnlyList<int> list)
#endif
=> new(list.Select(i => Convert.ToDouble(i)).ToList());
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Globalization;
using System.Windows.Data;
using MPF.Core.Data;
using MPF.Core.UI.ComboBoxItems;
using SabreTools.RedumpLib.Data;
namespace MPF.UI.Core
{
internal class ElementConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value switch
{
DiscCategory discCategory => new Element<DiscCategory>(discCategory),
InternalProgram internalProgram => new Element<InternalProgram>(internalProgram),
MediaType mediaType => new Element<MediaType>(mediaType),
RedumpSystem redumpSystem => new RedumpSystemComboBoxItem(redumpSystem),
Region region => new Element<Region>(region),
// Null values are treated as a system value
_ => new RedumpSystemComboBoxItem((RedumpSystem?)null),
};
}
public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// If it's an IElement but ends up null
if (value is not IElement element)
return null;
return element switch
{
Element<DiscCategory> dcElement => dcElement.Value,
Element<InternalProgram> ipElement => ipElement.Value,
Element<MediaType> mtElement => mtElement.Value,
RedumpSystemComboBoxItem rsElement => rsElement.Value,
Element<Region> reValue => reValue.Value,
_ => null,
};
}
}
}

View File

@@ -0,0 +1,596 @@
// -----------------------------------------------------------------------
// <copyright file="MessageBox.cs">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace WPFCustomMessageBox
{
using System;
using System.Windows;
/// <summary>
/// Displays a message box.
/// </summary>
public static class CustomMessageBox
{
/// <summary>
/// Global parameter to enable (true) or disable (false) the removal of the title bar icon.
/// If you are using a custom window style, the icon removal may cause issues like displaying two title bar (the default windows one and the custom one).
/// </summary>
private static readonly bool RemoveTitleBarIcon = true;
/// <summary>
/// Displays a message box that has a message and returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, string.Empty, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message and a title bar caption; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, string caption, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box in front of the specified window. The message box displays a message and returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, string.Empty, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box in front of the specified window. The message box displays a message and title bar caption; and it returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and button; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(null, messageBoxText, caption, null, null, null, null, timeout, timeoutResult);
default:
return ShowOKMessage(null, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and button; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(owner, messageBoxText, caption, null, null, null, null, timeout, timeoutResult);
default:
return ShowOKMessage(owner, messageBoxText, caption, null, null, null, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(null, messageBoxText, caption, null, null, null, icon, timeout, timeoutResult);
default:
return ShowOKMessage(null, messageBoxText, caption, null, null, icon, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, button, and icon; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="button">A System.Windows.MessageBoxButton value that specifies which button or buttons to display.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult Show(Window owner, string? messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
switch (button)
{
case MessageBoxButton.YesNo:
case MessageBoxButton.YesNoCancel:
return ShowYesNoMessage(owner, messageBoxText, caption, null, null, null, icon, timeout, timeoutResult);
default:
return ShowOKMessage(owner, messageBoxText, caption, null, null, icon, timeout, timeoutResult);
}
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and OK button with a custom System.String value for the button's text; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(string messageBoxText, string caption, string okButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, and OK button with a custom System.String value for the button's text; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(Window owner, string messageBoxText, string caption, string okButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, OK button with a custom System.String value for the button's text, and icon; and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(string messageBoxText, string caption, string okButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, title bar caption, OK button with a custom System.String value for the button's text, and icon; and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOK(Window owner, string messageBoxText, string caption, string okButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and OK/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(string messageBoxText, string caption, string okButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and OK/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, OK/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(null, messageBoxText, caption, okButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, OK/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowOKCancel(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowOKMessage(owner, messageBoxText, caption, okButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(string messageBoxText, string caption, string yesButtonText, string noButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, null, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(string messageBoxText, string caption, string yesButtonText, string noButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNo(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, null, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, and Yes/No/Cancel buttons with custom System.String values for the buttons' text;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, null, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(null, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">
/// The message box will close automatically after <paramref name="timeout"/> milliseconds.
/// If null, the message box will act like default message box and not close automatically.
/// </param>
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
public static MessageBoxResult ShowYesNoCancel(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
return ShowYesNoMessage(owner, messageBoxText, caption, yesButtonText, noButtonText, cancelButtonText, icon, timeout, timeoutResult);
}
/// <summary>
/// Displays a message box that has a message, caption, OK/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="okButtonText">A System.String that specifies the text to display within the OK button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
private static MessageBoxResult ShowOKMessage(Window? owner, string? messageBoxText, string caption, string? okButtonText, string? cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.OK : MessageBoxButton.OKCancel;
var msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
if (!string.IsNullOrEmpty(okButtonText))
msg.OkButtonText = okButtonText;
if (!string.IsNullOrEmpty(cancelButtonText))
msg.CancelButtonText = cancelButtonText;
ShowDialog(msg, timeout, timeoutResult);
return msg.Result;
}
/// <summary>
/// Displays a message box that has a message, caption, Yes/No/Cancel buttons with custom System.String values for the buttons' text, and icon;
/// and that returns a result.
/// </summary>
/// <param name="owner">A System.Windows.Window that represents the owner window of the message box.</param>
/// <param name="messageBoxText">A System.String that specifies the text to display.</param>
/// <param name="caption">A System.String that specifies the title bar caption to display.</param>
/// <param name="yesButtonText">A System.String that specifies the text to display within the Yes button.</param>
/// <param name="noButtonText">A System.String that specifies the text to display within the No button.</param>
/// <param name="cancelButtonText">A System.String that specifies the text to display within the Cancel button.</param>
/// <param name="icon">A System.Windows.MessageBoxImage value that specifies the icon to display.</param>
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
private static MessageBoxResult ShowYesNoMessage(Window? owner, string? messageBoxText, string caption, string? yesButtonText, string? noButtonText, string? cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
{
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.YesNo : MessageBoxButton.YesNoCancel;
var msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
if (!string.IsNullOrEmpty(yesButtonText))
msg.YesButtonText = yesButtonText;
if (!string.IsNullOrEmpty(noButtonText))
msg.NoButtonText = noButtonText;
if (!string.IsNullOrEmpty(cancelButtonText))
msg.CancelButtonText = cancelButtonText;
ShowDialog(msg, timeout, timeoutResult);
return msg.Result;
}
private static void ShowDialog(CustomMessageBoxWindow dialog, int? timeout, MessageBoxResult timeoutResult)
{
if (timeout.HasValue && timeout.Value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(timeout), string.Format("Timeout must be greater than 0."));
}
if (timeout.HasValue)
{
//System.Threading.Timer timer = null;
//timer = new System.Threading.Timer(s => { msg.Close(); timer.Dispose(); }, null, timeout.Value, System.Threading.Timeout.Infinite);
var timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
timer.Elapsed += delegate {
Application.Current.Dispatcher.Invoke(new Action(() =>
{
dialog.Result = timeoutResult;
dialog.Close();
//timer.Stop();
//timer.Dispose();
//timer = null;
}));
};
timer.Start();
dialog.ShowDialog();
timer.Stop();
timer.Dispose();
}
else
dialog.ShowDialog();
}
}
}

View File

@@ -0,0 +1,78 @@
<Window x:Class="WPFCustomMessageBox.CustomMessageBoxWindow"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
ResizeMode="NoResize" SizeToContent="WidthAndHeight"
Title="" MinHeight="155" MaxWidth="470" MinWidth="154"
BorderBrush="DarkGray" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown" Content="{Binding Path=Title,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
</Grid>
<!-- Make the window width fit the title by embedding the title invisibly -->
<TextBlock Text="{Binding Path=Title,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"
Visibility="Hidden" Height="0" Margin="50 0 0 0" />
<Grid Grid.Row="1" Background="{DynamicResource CustomMessageBox.Static.Background}" MinHeight="69">
<DockPanel>
<Image Name="Image_MessageBox" Width="32" Height="32" HorizontalAlignment="Left" DockPanel.Dock="Left" Margin="30,0,0,0" Visibility="Collapsed"/>
<TextBlock Name="TextBlock_Message" TextWrapping="Wrap" MaxWidth="500" Width="Auto"
VerticalAlignment="Center" Margin="12,20,41,15" />
</DockPanel>
</Grid>
<Grid Grid.Row="2" Background="{DynamicResource CustomMessageBox.Static.Background}" MinHeight="49">
<DockPanel Margin="5,0">
<!-- Cancel Button -->
<Button Name="Button_Cancel" MinWidth="88" MaxWidth="160" Height="26" Margin="5,0" HorizontalAlignment="Right" Visibility="Collapsed" IsCancel="True"
DockPanel.Dock="Right" Click="Button_Cancel_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_Cancel" Padding="0" Margin="10,0">_Cancel</Label>
</Button>
<!-- End Cancel Button -->
<!-- No Button -->
<Button Name="Button_No" MinWidth="88" MaxWidth="160" Height="26" Margin="5,0" HorizontalAlignment="Right" Visibility="Collapsed"
DockPanel.Dock="Right" Click="Button_No_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_No" Padding="0" Margin="10,0">_No</Label>
</Button>
<!-- End No Button -->
<!-- Yes Button -->
<Button Name="Button_Yes" MinWidth="88" MaxWidth="160" Height="26" Margin="35,0,5,0" HorizontalAlignment="Right" Visibility="Collapsed"
DockPanel.Dock="Right" Click="Button_Yes_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_Yes" Padding="0" Margin="10,0">_Yes</Label>
</Button>
<!-- End Yes Button -->
<!-- OK Button -->
<Button Name="Button_OK" MinWidth="88" MaxWidth="160" Margin="35,0,5,0" HorizontalAlignment="Right" Height="26"
Click="Button_OK_Click" Style="{DynamicResource CustomButtonStyle}">
<Label Name="Label_Ok" Padding="0" Margin="10,0">_OK</Label>
</Button>
<!-- End OK Button -->
</DockPanel>
</Grid>
</Grid>
</Window>

View File

@@ -0,0 +1,347 @@
using System;
using System.Drawing;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using MPF.UI.Core;
#pragma warning disable IDE1006 // Naming Styles
namespace WPFCustomMessageBox
{
/// <summary>
/// Interaction logic for ModalDialog.xaml
/// </summary>
internal partial class CustomMessageBoxWindow : Window
{
private readonly bool _removeTitleBarIcon = true;
public string? Caption
{
get
{
return Title;
}
set
{
Title = value;
}
}
public string? Message
{
get
{
#if NET35
return _TextBlock_Message!.Text;
#else
return TextBlock_Message.Text;
#endif
}
set
{
#if NET35
_TextBlock_Message!.Text = value;
#else
TextBlock_Message.Text = value;
#endif
}
}
public string? OkButtonText
{
get
{
#if NET35
return _Label_Ok!.Content.ToString();
#else
return Label_Ok.Content.ToString();
#endif
}
set
{
#if NET35
_Label_Ok!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Ok.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
public string? CancelButtonText
{
get
{
#if NET35
return _Label_Cancel!.Content.ToString();
#else
return Label_Cancel.Content.ToString();
#endif
}
set
{
#if NET35
_Label_Cancel!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Cancel.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
public string? YesButtonText
{
get
{
#if NET35
return _Label_Yes!.Content.ToString();
#else
return Label_Yes.Content.ToString();
#endif
}
set
{
#if NET35
_Label_Yes!.Content = value.TryAddKeyboardAccellerator();
#else
Label_Yes.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
public string? NoButtonText
{
get
{
#if NET35
return _Label_No!.Content.ToString();
#else
return Label_No.Content.ToString();
#endif
}
set
{
#if NET35
_Label_No!.Content = value.TryAddKeyboardAccellerator();
#else
Label_No.Content = value.TryAddKeyboardAccellerator();
#endif
}
}
public MessageBoxResult Result { get; set; }
#if NET35
private Button? _Button_Cancel => ItemHelper.FindChild<Button>(this, "Button_Cancel");
private Button? _Button_No => ItemHelper.FindChild<Button>(this, "Button_No");
private Button? _Button_OK => ItemHelper.FindChild<Button>(this, "Button_OK");
private Button? _Button_Yes => ItemHelper.FindChild<Button>(this, "Button_Yes");
private System.Windows.Controls.Image? _Image_MessageBox => ItemHelper.FindChild<System.Windows.Controls.Image>(this, "Image_MessageBox");
private Label? _Label_Cancel => ItemHelper.FindChild<Label>(this, "Label_Cancel");
private Label? _Label_No => ItemHelper.FindChild<Label>(this, "Label_No");
private Label? _Label_Ok => ItemHelper.FindChild<Label>(this, "Label_Ok");
private Label? _Label_Yes => ItemHelper.FindChild<Label>(this, "Label_Yes");
private TextBlock? _TextBlock_Message => ItemHelper.FindChild<TextBlock>(this, "TextBlock_Message");
#endif
internal CustomMessageBoxWindow(Window? owner, string? message, string? caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
{
#if NET40_OR_GREATER || NETCOREAPP
System.Windows.Media.TextOptions.SetTextFormattingMode(this, System.Windows.Media.TextFormattingMode.Display);
System.Windows.Media.TextOptions.SetTextRenderingMode(this, System.Windows.Media.TextRenderingMode.ClearType);
UseLayoutRounding = true;
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
_removeTitleBarIcon = removeTitleBarIcon;
Focusable = true;
ShowActivated = true;
ShowInTaskbar = true;
if (owner != null && owner.IsLoaded)
{
Owner = owner;
WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
Message = message;
Caption = caption;
DisplayButtons(button ?? MessageBoxButton.OK);
if (image.HasValue)
DisplayImage(image.Value);
else
#if NET35
_Image_MessageBox!.Visibility = Visibility.Collapsed;
#else
Image_MessageBox.Visibility = Visibility.Collapsed;
#endif
}
protected override void OnSourceInitialized(EventArgs e)
{
if (_removeTitleBarIcon)
Util.RemoveIcon(this);
base.OnSourceInitialized(e);
}
private void DisplayButtons(MessageBoxButton button)
{
switch (button)
{
case MessageBoxButton.OKCancel:
// Hide all but OK, Cancel
#if NET35
_Button_OK!.Visibility = Visibility.Visible;
_Button_OK.Focus();
_Button_Cancel!.Visibility = Visibility.Visible;
_Button_Yes!.Visibility = Visibility.Collapsed;
_Button_No!.Visibility = Visibility.Collapsed;
#else
Button_OK.Visibility = Visibility.Visible;
Button_OK.Focus();
Button_Cancel.Visibility = Visibility.Visible;
Button_Yes.Visibility = Visibility.Collapsed;
Button_No.Visibility = Visibility.Collapsed;
#endif
break;
case MessageBoxButton.YesNo:
// Hide all but Yes, No
#if NET35
_Button_Yes!.Visibility = Visibility.Visible;
_Button_Yes.Focus();
_Button_No!.Visibility = Visibility.Visible;
_Button_OK!.Visibility = Visibility.Collapsed;
_Button_Cancel!.Visibility = Visibility.Collapsed;
#else
Button_Yes.Visibility = Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = Visibility.Visible;
Button_OK.Visibility = Visibility.Collapsed;
Button_Cancel.Visibility = Visibility.Collapsed;
#endif
break;
case MessageBoxButton.YesNoCancel:
// Hide only OK
#if NET35
_Button_Yes!.Visibility = Visibility.Visible;
_Button_Yes.Focus();
_Button_No!.Visibility = Visibility.Visible;
_Button_Cancel!.Visibility = Visibility.Visible;
_Button_OK!.Visibility = Visibility.Collapsed;
#else
Button_Yes.Visibility = Visibility.Visible;
Button_Yes.Focus();
Button_No.Visibility = Visibility.Visible;
Button_Cancel.Visibility = Visibility.Visible;
Button_OK.Visibility = Visibility.Collapsed;
#endif
break;
default:
// Hide all but OK
#if NET35
_Button_OK!.Visibility = Visibility.Visible;
_Button_OK.Focus();
_Button_Yes!.Visibility = Visibility.Collapsed;
_Button_No!.Visibility = Visibility.Collapsed;
_Button_Cancel!.Visibility = Visibility.Collapsed;
#else
Button_OK.Visibility = Visibility.Visible;
Button_OK.Focus();
Button_Yes.Visibility = Visibility.Collapsed;
Button_No.Visibility = Visibility.Collapsed;
Button_Cancel.Visibility = Visibility.Collapsed;
#endif
break;
}
}
private void DisplayImage(MessageBoxImage image)
{
Icon icon;
switch (image)
{
case MessageBoxImage.Exclamation: // Enumeration value 48 - also covers "Warning"
icon = SystemIcons.Exclamation;
break;
case MessageBoxImage.Error: // Enumeration value 16, also covers "Hand" and "Stop"
icon = SystemIcons.Hand;
break;
case MessageBoxImage.Information: // Enumeration value 64 - also covers "Asterisk"
icon = SystemIcons.Information;
break;
case MessageBoxImage.Question:
icon = SystemIcons.Question;
break;
default:
icon = SystemIcons.Information;
break;
}
#if NET35
_Image_MessageBox!.Source = icon.ToImageSource();
_Image_MessageBox.Visibility = Visibility.Visible;
#else
Image_MessageBox.Source = icon.ToImageSource();
Image_MessageBox.Visibility = Visibility.Visible;
#endif
}
private void Button_OK_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.OK;
Close();
}
private void Button_Cancel_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.Cancel;
Close();
}
private void Button_Yes_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.Yes;
Close();
}
private void Button_No_Click(object sender, RoutedEventArgs e)
{
Result = MessageBoxResult.No;
Close();
}
/// <summary>
/// Handler for Title MouseDown event
/// </summary>
private void TitleMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
this.DragMove();
}
}
}

View File

@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2021 Thomas Absenger
Copyright (c) 2013 Evan Wondrasek / Apricity Software LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,92 @@
// -----------------------------------------------------------------------
// <copyright file="Util.cs">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------
namespace WPFCustomMessageBox
{
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
/// <summary>
/// TODO: Update summary.
/// </summary>
internal static class Util
{
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
const int GWL_EXSTYLE = -20;
const int WS_EX_DLGMODALFRAME = 0x0001;
const int SWP_NOSIZE = 0x0001;
const int SWP_NOMOVE = 0x0002;
const int SWP_NOZORDER = 0x0004;
const int SWP_FRAMECHANGED = 0x0020;
//const uint WM_SETICON = 0x0080;
internal static ImageSource ToImageSource(this Icon icon)
{
ImageSource imageSource = Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return imageSource;
}
/// <summary>
/// Keyboard Accellerators are used in Windows to allow easy shortcuts to controls like Buttons and
/// MenuItems. These allow users to press the Alt key, and a shortcut key will be highlighted on the
/// control. If the user presses that key, that control will be activated.
/// This method checks a string if it contains a keyboard accellerator. If it doesn't, it adds one to the
/// beginning of the string. If there are two strings with the same accellerator, Windows handles it.
/// The keyboard accellerator character for WPF is underscore (_). It will not be visible.
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
internal static string? TryAddKeyboardAccellerator(this string? input)
{
if (input == null)
return input;
const string accellerator = "_"; // This is the default WPF accellerator symbol - used to be & in WinForms
// If it already contains an accellerator, do nothing
if (input.Contains(accellerator)) return input;
return accellerator + input;
}
/// <summary>
/// Removes the icon from the given window.
/// See https://stackoverflow.com/a/18581096
/// </summary>
/// <param name="window">The window to remove the icon from.</param>
internal static void RemoveIcon(Window window)
{
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(window).Handle;
// Change the extended window style to not show a window icon
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
_ = SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
// Update the window's non-client area to reflect the changes
SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

124
MPF.UI.Core/ItemHelper.cs Normal file
View File

@@ -0,0 +1,124 @@
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
namespace MPF.UI.Core
{
internal static class ItemHelper
{
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T? FindChild<T>(DependencyObject? parent, string childName) where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T? foundChild = null;
if (parent is ItemsControl itemsControl && itemsControl.Items != null)
{
int childrenCount = itemsControl.Items.Count;
for (int i = 0; i < childrenCount; i++)
{
var child = itemsControl.Items[i] as DependencyObject;
// If the child is not of the request child type child
if (child is not T)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null)
break;
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
}
else if (parent is ContentControl contentControl && contentControl.Content != null)
{
var child = contentControl.Content as DependencyObject;
// If the child is not of the request child type child
if (child is not T)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
}
}
else
{
// child element found.
foundChild = (T)child;
}
}
else if (parent is Visual)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
if (child is not T)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null)
break;
}
else if (!string.IsNullOrEmpty(childName))
{
// If the child's name is set for search
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
}
return foundChild;
}
}
}

View File

@@ -0,0 +1,59 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<!-- Assembly Properties -->
<TargetFrameworks>net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0-windows;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<ImportFrameworkWinFXTargets Condition="$(TargetFramework.StartsWith(`net3`))">true</ImportFrameworkWinFXTargets>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<VersionPrefix>3.1.1</VersionPrefix>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<!-- Package Properties -->
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
<Description>Common code for all MPF UI implementations</Description>
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
<RepositoryType>git</RepositoryType>
</PropertyGroup>
<ItemGroup>
<Resource Include="Images\ring-code-guide-1-layer.png" />
<Resource Include="Images\ring-code-guide-2-layer.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
</ItemGroup>
<!-- Support for old .NET versions -->
<ItemGroup Condition="$(TargetFramework.StartsWith(`net3`))">
<Reference Include="PresentationBuildTasks" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationBuildTasks.dll" />
<Reference Include="PresentationCore" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll" />
<Reference Include="PresentationFramework" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll" />
<Reference Include="WindowsBase" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.2" />
</ItemGroup>
<ItemGroup>
<Page Update="UserControls\UserInput.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Windows\DiscInformationWindow.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
<Page Update="Windows\RingCodeGuideWindow.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
</Page>
</ItemGroup>
</Project>

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 abstract class Theme
{
#region Application-Wide
/// <summary>
/// SolidColorBrush used to paint the active window's border.
/// </summary>
public SolidColorBrush? ActiveBorderBrush { get; protected set; }
/// <summary>
/// SolidColorBrush that paints the face of a three-dimensional display element.
/// </summary>
public SolidColorBrush? ControlBrush { get; protected set; }
/// <summary>
/// SolidColorBrush that paints text in a three-dimensional display element.
/// </summary>
public SolidColorBrush? ControlTextBrush { get; protected set; }
/// <summary>
/// SolidColorBrush that paints disabled text.
/// </summary>
public SolidColorBrush? GrayTextBrush { get; protected set; }
/// <summary>
/// SolidColorBrush that paints the background of a window's client area.
/// </summary>
public SolidColorBrush? WindowBrush { get; protected set; }
/// <summary>
/// SolidColorBrush that paints the text in the client area of a window.
/// </summary>
public SolidColorBrush? WindowTextBrush { get; protected set; }
#endregion
#region Button
/// <summary>
/// Brush for the Button.Disabled.Background resource
/// </summary>
public Brush? Button_Disabled_Background { get; protected set; }
/// <summary>
/// Brush for the Button.MouseOver.Background resource
/// </summary>
public Brush? Button_MouseOver_Background { get; protected set; }
/// <summary>
/// Brush for the Button.Pressed.Background resource
/// </summary>
public Brush? Button_Pressed_Background { get; protected set; }
/// <summary>
/// Brush for the Button.Static.Background resource
/// </summary>
public Brush? Button_Static_Background { get; protected set; }
#endregion
#region ComboBox
/// <summary>
/// Brush for the ComboBox.Disabled.Background resource
/// </summary>
public Brush? ComboBox_Disabled_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Disabled.Editable.Background resource
/// </summary>
public Brush? ComboBox_Disabled_Editable_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Disabled.Editable.Button.Background resource
/// </summary>
public Brush? ComboBox_Disabled_Editable_Button_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.MouseOver.Background resource
/// </summary>
public Brush? ComboBox_MouseOver_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.MouseOver.Editable.Background resource
/// </summary>
public Brush? ComboBox_MouseOver_Editable_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.MouseOver.Editable.Button.Background resource
/// </summary>
public Brush? ComboBox_MouseOver_Editable_Button_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Pressed.Background resource
/// </summary>
public Brush? ComboBox_Pressed_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Pressed.Editable.Background resource
/// </summary>
public Brush? ComboBox_Pressed_Editable_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Pressed.Editable.Button.Background resource
/// </summary>
public Brush? ComboBox_Pressed_Editable_Button_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Static.Background resource
/// </summary>
public Brush? ComboBox_Static_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Static.Editable.Background resource
/// </summary>
public Brush? ComboBox_Static_Editable_Background { get; protected set; }
/// <summary>
/// Brush for the ComboBox.Static.Editable.Button.Background resource
/// </summary>
public Brush? ComboBox_Static_Editable_Button_Background { get; protected set; }
#endregion
#region CustomMessageBox
/// <summary>
/// Brush for the CustomMessageBox.Static.Background resource
/// </summary>
public Brush? CustomMessageBox_Static_Background { get; protected set; }
#endregion
#region MenuItem
/// <summary>
/// Brush for the MenuItem.SubMenu.Background resource
/// </summary>
public Brush? MenuItem_SubMenu_Background { get; protected set; }
/// <summary>
/// Brush for the MenuItem.SubMenu.Border resource
/// </summary>
public Brush? MenuItem_SubMenu_Border { get; protected set; }
#endregion
#region ProgressBar
/// <summary>
/// Brush for the ProgressBar.Background resource
/// </summary>
public Brush? ProgressBar_Background { get; protected set; }
#endregion
#region ScrollViewer
/// <summary>
/// Brush for the ScrollViewer.ScrollBar.Background resource
/// </summary>
public Brush? ScrollViewer_ScrollBar_Background { get; protected set; }
#endregion
#region TabItem
/// <summary>
/// Brush for the TabItem.Selected.Background resource
/// </summary>
public Brush? TabItem_Selected_Background { get; protected set; }
/// <summary>
/// Brush for the TabItem.Static.Background resource
/// </summary>
public Brush? TabItem_Static_Background { get; protected set; }
/// <summary>
/// Brush for the TabItem.Static.Border resource
/// </summary>
public Brush? TabItem_Static_Border { get; protected set; }
#endregion
#region TextBox
/// <summary>
/// Brush for the TextBox.Static.Background resource
/// </summary>
public Brush? TextBox_Static_Background { get; protected 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

@@ -0,0 +1,31 @@
<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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<StackPanel Orientation="Vertical">
<Grid Height="22" Margin="10 10 10 0">
<ProgressBar x:Name="ProgressBar" Style="{DynamicResource CustomProgressBarStyle}" />
<TextBlock x:Name="ProgressLabel" Grid.Row="0" Height="22" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="0 2 0 0" />
</Grid>
<Border Height="180" Background="White" BorderBrush="Gainsboro" BorderThickness="1" Margin="10">
<ScrollViewer Name="OutputViewer" Template="{DynamicResource CustomScrollViewerControlStyle}">
<RichTextBox Name="Output" FontFamily="Consolas" Background="#FF202020" IsReadOnly="true" />
</ScrollViewer>
</Border>
<GroupBox Grid.Row="2" Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
<Label/> <!-- Empty label for padding -->
<Button Name="ClearButton" Height="25" Width="80" Content="Clear" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="SaveButton" Height="25" Width="80" Content="Save" Style="{DynamicResource CustomButtonStyle}" />
<Label/> <!-- Empty label for padding -->
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,241 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Threading;
using MPF.Core.Data;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.UserControls
{
public partial class LogOutput : UserControl
{
/// <summary>
/// Document representing the text
/// </summary>
internal FlowDocument Document { get; private set; }
/// <summary>
/// Queue of items that need to be logged
/// </summary>
internal ProcessingQueue<LogLine> LogQueue { get; private set; }
/// <summary>
/// Paragraph backing the log
/// </summary>
private readonly Paragraph _paragraph;
/// <summary>
/// Cached value of the last line written
/// </summary>
private Run? lastLine = null;
#if NET35
private Button? _ClearButton => ItemHelper.FindChild<Button>(this, "ClearButton");
private RichTextBox? _Output => ItemHelper.FindChild<RichTextBox>(this, "Output");
private ScrollViewer? _OutputViewer => ItemHelper.FindChild<ScrollViewer>(this, "OutputViewer");
private Button? _SaveButton => ItemHelper.FindChild<Button>(this, "SaveButton");
#endif
public LogOutput()
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
// Update the internal state
Document = new FlowDocument()
{
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
};
_paragraph = new Paragraph();
Document.Blocks.Add(_paragraph);
// Setup the processing queue
LogQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
// Add handlers
#if NET35
_OutputViewer!.SizeChanged += OutputViewerSizeChanged;
_Output!.TextChanged += OnTextChanged;
_ClearButton!.Click += OnClearButton;
_SaveButton!.Click += OnSaveButton;
#else
OutputViewer.SizeChanged += OutputViewerSizeChanged;
Output.TextChanged += OnTextChanged;
ClearButton.Click += OnClearButton;
SaveButton.Click += OnSaveButton;
#endif
// Update the internal state
#if NET35
_Output.Document = Document;
#else
Output.Document = Document;
#endif
}
#region Logging
/// <summary>
/// Enqueue text to the log with formatting
/// </summary>
/// <param name="logLevel">LogLevel for the log</param>
/// <param name="text">Text to write to the log</param>
public void EnqueueLog(LogLevel logLevel, string text)
{
// Null text gets ignored
if (text == null)
return;
// Enqueue the text
LogQueue.Enqueue(new LogLine(text, logLevel));
}
/// <summary>
/// Log line wrapper
/// </summary>
internal readonly 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()
{
return this.LogLevel switch
{
LogLevel.SECRET => Brushes.Blue,
LogLevel.ERROR => Brushes.Red,
LogLevel.VERBOSE => Brushes.Yellow,
_ => 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>
/// Process the log lines in the queue
/// </summary>
/// <param name="nextLogLine">LogLine item to process</param>
internal void ProcessLogLine(LogLine nextLogLine)
{
// Null text gets ignored
string nextText = 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)
{
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)
{
Dispatcher.Invoke(() =>
{
lastLine ??= new Run();
lastLine.Text = logLine.Text;
lastLine.Foreground = logLine.GetForegroundColor();
});
}
#endregion
#region Helpers
/// <summary>
/// Clear all inlines of the paragraph
/// </summary>
private void ClearInlines() => _paragraph.Inlines.Clear();
/// <summary>
/// Save all inlines to console.log
/// </summary>
private void SaveInlines()
{
using var sw = new StreamWriter(File.OpenWrite("console.log"));
foreach (var inline in _paragraph.Inlines)
{
if (inline is Run run)
sw.Write(run.Text);
}
}
/// <summary>
/// Scroll the current view to the bottom
/// </summary>
#if NET35
public void ScrollToBottom() => _OutputViewer!.ScrollToBottom();
#else
public void ScrollToBottom() => OutputViewer.ScrollToBottom();
#endif
#endregion
#region EventHandlers
private void OnClearButton(object sender, EventArgs e)
=> ClearInlines();
private void OnSaveButton(object sender, EventArgs e)
=> SaveInlines();
private void OnTextChanged(object sender, TextChangedEventArgs e)
=> ScrollToBottom();
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e)
=> ScrollToBottom();
#endregion
}
}

View File

@@ -0,0 +1,30 @@
<UserControl x:Class="MPF.UI.Core.UserControls.UserInput"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" x:Name="userInput">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0"
VerticalAlignment="Center"
HorizontalAlignment="Right"
Content="{Binding ElementName=userInput, Path=Label}" />
<TextBox Grid.Row="0" Grid.Column="1"
Height="{Binding ElementName=userInput, Path=TextHeight}"
HorizontalAlignment="Stretch"
Text="{Binding ElementName=userInput, Path=Text}"
AcceptsTab="{Binding ElementName=userInput, Path=Tab}"
AcceptsReturn="{Binding ElementName=userInput, Path=Enter}"
TextWrapping="{Binding ElementName=userInput, Path=TextWrapping}"
IsReadOnly="{Binding ElementName=userInput, Path=IsReadOnly}"
VerticalContentAlignment="{Binding ElementName=userInput, Path=VerticalContentAlignmentValue}"
HorizontalScrollBarVisibility="{Binding ElementName=userInput, Path=HorizontalScrollBarVisibility}"
VerticalScrollBarVisibility="{Binding ElementName=userInput, Path=VerticalScrollBarVisibility}"/>
</Grid>
</UserControl>

View File

@@ -0,0 +1,126 @@
using System.Windows;
using System.Windows.Controls;
namespace MPF.UI.Core.UserControls
{
/// <summary>
/// Interaction logic for UserInput.xaml
/// </summary>
public partial class UserInput : UserControl
{
#region Dependency Properties
public static readonly DependencyProperty LabelProperty =
DependencyProperty.Register("Label", typeof(string), typeof(UserInput));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(UserInput));
public static readonly DependencyProperty TextHeightProperty =
DependencyProperty.Register("TextHeight", typeof(string), typeof(UserInput));
public static readonly DependencyProperty TabProperty =
DependencyProperty.Register("Tab", typeof(bool), typeof(UserInput));
public static readonly DependencyProperty EnterProperty =
DependencyProperty.Register("Enter", typeof(bool), typeof(UserInput));
public static readonly DependencyProperty TextWrappingProperty =
DependencyProperty.Register("TextWrapping", typeof(TextWrapping), typeof(UserInput));
public static readonly DependencyProperty IsReadOnlyProperty =
DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(UserInput));
public static readonly DependencyProperty VerticalContentAlignmentValueProperty =
DependencyProperty.Register("VerticalContentAlignmentValue", typeof(VerticalAlignment), typeof(UserInput));
public static readonly DependencyProperty HorizontalScrollBarVisibilityProperty =
DependencyProperty.Register("HorizontalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(UserInput));
public static readonly DependencyProperty VerticalScrollBarVisibilityProperty =
DependencyProperty.Register("VerticalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(UserInput));
#endregion
#region Properties
public string Label
{
get => (string)GetValue(LabelProperty);
set => SetValue(LabelProperty, value);
}
public string Text
{
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value);
}
public string TextHeight
{
get => (string)GetValue(TextHeightProperty);
set => SetValue(TextHeightProperty, value);
}
public bool Tab
{
get => (bool)GetValue(TabProperty);
set => SetValue(TabProperty, value);
}
public bool Enter
{
get => (bool)GetValue(EnterProperty);
set => SetValue(EnterProperty, value);
}
public TextWrapping TextWrapping
{
get => (TextWrapping)GetValue(TextWrappingProperty);
set => SetValue(TextWrappingProperty, value);
}
public bool IsReadOnly
{
get => (bool)GetValue(IsReadOnlyProperty);
set => SetValue(IsReadOnlyProperty, value);
}
public VerticalAlignment VerticalContentAlignmentValue
{
get => (VerticalAlignment)GetValue(VerticalContentAlignmentValueProperty);
set => SetValue(VerticalContentAlignmentValueProperty, value);
}
public ScrollBarVisibility HorizontalScrollBarVisibility
{
get => (ScrollBarVisibility)GetValue(HorizontalScrollBarVisibilityProperty);
set => SetValue(HorizontalScrollBarVisibilityProperty, value);
}
public ScrollBarVisibility VerticalScrollBarVisibility
{
get => (ScrollBarVisibility)GetValue(VerticalScrollBarVisibilityProperty);
set => SetValue(VerticalScrollBarVisibilityProperty, value);
}
#endregion
public UserInput()
{
// Set default values
TextHeight = "22";
Tab = false;
Enter = false;
TextWrapping = TextWrapping.NoWrap;
IsReadOnly = false;
VerticalContentAlignmentValue = VerticalAlignment.Center;
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
}
}
}

View File

@@ -0,0 +1,127 @@
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.CheckDumpWindow"
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:core="clr-namespace:MPF.UI.Core"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
mc:Ignorable="d"
Title="Check Existing Dump" Width="600" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<Window.DataContext>
<viewModels:CheckDumpViewModel/>
</Window.DataContext>
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,2,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<Run FontWeight="Bold" Text="Check Existing Dump" />
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Settings">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="InputPathLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Input Path"/>
<TextBox x:Name="InputPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding InputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding InputPathTextBoxEnabled}" />
<Button x:Name="InputPathBrowseButton" Grid.Row="0" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding InputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="SystemMediaTypeLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="1" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
ItemsSource="{Binding Systems}" SelectedItem="{Binding Path=CurrentSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding SystemTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHeader}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="1" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right"
ItemsSource="{Binding MediaTypes}" SelectedItem="{Binding Path=CurrentMediaType, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding MediaTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
<Label x:Name="DumpingProgramLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Dumping Program"/>
<ComboBox x:Name="DumpingProgramComboBox" Grid.Row="2" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
ItemsSource="{Binding InternalPrograms}" SelectedItem="{Binding Path=CurrentProgram, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding DumpingProgramComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
</GroupBox>
<!-- Check Dump / Cancel -->
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
<Label/>
<!-- Empty label for padding -->
<Button Name="CheckDumpButton" Height="25" Width="80" IsDefault="True" Content="Check Dump"
IsEnabled="{Binding CheckDumpButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
Style="{DynamicResource CustomButtonStyle}" />
<Label/>
<!-- Empty label for padding -->
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</coreWindows:WindowBase>

View File

@@ -0,0 +1,283 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using MPF.Core.UI.ViewModels;
using SabreTools.RedumpLib.Data;
using WPFCustomMessageBox;
using WinForms = System.Windows.Forms;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.Windows
{
/// <summary>
/// Interaction logic for CheckDumpWindow.xaml
/// </summary>
public partial class CheckDumpWindow : WindowBase
{
/// <summary>
/// Read-only access to the current check dump view model
/// </summary>
public CheckDumpViewModel CheckDumpViewModel => DataContext as CheckDumpViewModel ?? new CheckDumpViewModel();
#if NET35
#region Settings
private ComboBox? _DumpingProgramComboBox => ItemHelper.FindChild<ComboBox>(this, "DumpingProgramComboBox");
private Button? _InputPathBrowseButton => ItemHelper.FindChild<Button>(this, "InputPathBrowseButton");
private TextBox? _InputPathTextBox => ItemHelper.FindChild<TextBox>(this, "InputPathTextBox");
private ComboBox? _MediaTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "MediaTypeComboBox");
private ComboBox? _SystemTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "SystemTypeComboBox");
#endregion
#region Controls
private System.Windows.Controls.Button? _CheckDumpButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CheckDumpButton");
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
#endregion
#endif
/// <summary>
/// Constructor
/// </summary>
public CheckDumpWindow(MainWindow parent)
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
}
/// <summary>
/// Handler for CheckDumpWindow OnContentRendered event
/// </summary>
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
// Add the click handlers to the UI
AddEventHandlers();
}
#region UI Functionality
/// <summary>
/// Add all event handlers
/// </summary>
public void AddEventHandlers()
{
// Main buttons
#if NET35
_CheckDumpButton!.Click += OnCheckDumpClick;
_CancelButton!.Click += OnCancelClick;
#else
CheckDumpButton.Click += OnCheckDumpClick;
CancelButton.Click += OnCancelClick;
#endif
// User Area Click
#if NET35
_InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
#else
InputPathBrowseButton.Click += InputPathBrowseButtonClick;
#endif
// User Area SelectionChanged
#if NET35
_SystemTypeComboBox!.SelectionChanged += SystemTypeComboBoxSelectionChanged;
_MediaTypeComboBox!.SelectionChanged += MediaTypeComboBoxSelectionChanged;
_DumpingProgramComboBox!.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
#else
SystemTypeComboBox.SelectionChanged += SystemTypeComboBoxSelectionChanged;
MediaTypeComboBox.SelectionChanged += MediaTypeComboBoxSelectionChanged;
DumpingProgramComboBox.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
#endif
// User Area TextChanged
#if NET35
_InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
#else
InputPathTextBox.TextChanged += InputPathTextBoxTextChanged;
#endif
}
/// <summary>
/// Browse for an input file path
/// </summary>
public void BrowseFile()
{
// Get the current path, if possible
string? currentPath = CheckDumpViewModel.InputPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CheckDumpViewModel.Options.DefaultOutputPath))
currentPath = CheckDumpViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "Disc Images|*.iso;*.cue;*.aaruf|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CheckDumpViewModel.InputPath = fileDialog.FileName;
}
}
/// <summary>
/// Display a user message using a CustomMessageBox
/// </summary>
/// <param name="title">Title to display to the user</param>
/// <param name="message">Message to display to the user</param>
/// <param name="optionCount">Number of options to display</param>
/// <param name="flag">true for inquiry, false otherwise</param>
/// <returns>true for positive, false for negative, null for neutral</returns>
public bool? DisplayUserMessage(string title, string message, int optionCount, bool flag)
{
// Set the correct button style
var button = optionCount switch
{
1 => MessageBoxButton.OK,
2 => MessageBoxButton.YesNo,
3 => MessageBoxButton.YesNoCancel,
// This should not happen, but default to "OK"
_ => MessageBoxButton.OK,
};
// Set the correct icon
MessageBoxImage image = flag ? MessageBoxImage.Question : MessageBoxImage.Exclamation;
// Display and get the result
MessageBoxResult result = CustomMessageBox.Show(this, message, title, button, image);
return result switch
{
MessageBoxResult.OK or MessageBoxResult.Yes => true,
MessageBoxResult.No => false,
_ => null,
};
}
/// <summary>
/// Show the disc information window
/// </summary>
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
/// <returns>Dialog open result</returns>
public (bool?, SubmissionInfo?) ShowDiscInformationWindow(SubmissionInfo? submissionInfo)
{
var discInformationWindow = new DiscInformationWindow(CheckDumpViewModel.Options, submissionInfo)
{
Focusable = true,
Owner = this,
ShowActivated = true,
ShowInTaskbar = true,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
discInformationWindow.Closed += delegate { this.Activate(); };
bool? result = discInformationWindow.ShowDialog();
// Copy back the submission info changes, if necessary
if (result == true)
submissionInfo = (discInformationWindow.DiscInformationViewModel.SubmissionInfo.Clone() as SubmissionInfo)!;
return (result, submissionInfo!);
}
#endregion
#region Event Handlers
/// <summary>
/// Handler for CheckDumpButton Click event
/// </summary>
private void OnCheckDumpClick(object sender, EventArgs e)
{
string? errorMessage = CheckDumpViewModel.CheckDump(ShowDiscInformationWindow);
if (string.IsNullOrEmpty(errorMessage))
{
bool? checkAgain = DisplayUserMessage("Check Complete", "The dump has been processed successfully! Would you like to check another dump?", 2, false);
if (checkAgain == false)
Close();
}
else
{
DisplayUserMessage("Check Failed", errorMessage!, 1, false);
}
}
/// <summary>
/// Handler for CancelButtom Click event
/// </summary>
private void OnCancelClick(object sender, EventArgs e)
{
Close();
}
/// <summary>
/// Handler for DumpingProgramComboBox SelectionChanged event
/// </summary>
public void DumpingProgramComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (CheckDumpViewModel.CanExecuteSelectionChanged)
CheckDumpViewModel.ChangeDumpingProgram();
}
/// <summary>
/// Handler for InputPathBrowseButton Click event
/// </summary>
public void InputPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseFile();
if (CheckDumpViewModel.CanExecuteSelectionChanged)
CheckDumpViewModel.ChangeInputPath();
}
/// <summary>
/// Handler for InputPathTextBox TextChanged event
/// </summary>
public void InputPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CheckDumpViewModel.CanExecuteSelectionChanged)
CheckDumpViewModel.ChangeInputPath();
}
/// <summary>
/// Handler for MediaTypeComboBox SelectionChanged event
/// </summary>
public void MediaTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (CheckDumpViewModel.CanExecuteSelectionChanged)
CheckDumpViewModel.ChangeMediaType();
}
/// <summary>
/// Handler for SystemTypeComboBox SelectionChanged event
/// </summary>
public void SystemTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (CheckDumpViewModel.CanExecuteSelectionChanged)
CheckDumpViewModel.ChangeSystem();
}
#endregion
}
}

View File

@@ -0,0 +1,209 @@
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.CreateIRDWindow"
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:core="clr-namespace:MPF.UI.Core"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
mc:Ignorable="d"
Title="Create PS3 IRD" Width="600" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<Window.DataContext>
<viewModels:CreateIRDViewModel/>
</Window.DataContext>
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,2,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<Run FontWeight="Bold" Text="Create PS3 IRD" />
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Input">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="InputPathLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="PS3 ISO Path"/>
<TextBox x:Name="InputPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding InputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding InputPathTextBoxEnabled}" />
<Button x:Name="InputPathBrowseButton" Grid.Row="0" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding InputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="LogPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="*.getkey.log File Path"/>
<TextBox x:Name="LogPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding LogPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding LogPathTextBoxEnabled}" />
<Button x:Name="LogPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding LogPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
</Grid>
</GroupBox>
<Expander Name="KeyExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define PS3 Disc Encryption Key">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="KeyLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Hexadecimal Key"/>
<TextBox x:Name="KeyTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding HexKey, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding HexKeyTextBoxEnabled}" />
<Label x:Name="KeyPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
ToolTip="Typically a *.key file" Content="Key File Path"/>
<TextBox x:Name="KeyPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding KeyPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding KeyPathTextBoxEnabled}" />
<Button x:Name="KeyPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding KeyPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="KeyStatusLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Key Status"/>
<Label x:Name="KeyStatusText" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" Content="{Binding KeyStatus}"/>
</Grid>
</Expander>
<Expander Name="DiscIDExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define PS3 Disc ID">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="DiscIDLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Disc ID"/>
<TextBox x:Name="DiscIDTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding DiscIDString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding DiscIDTextBoxEnabled}" />
<Label x:Name="DiscIDStatusLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="DiscID Status"/>
<Label x:Name="DiscIDStatusText" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Content="{Binding DiscIDStatus}"/>
</Grid>
</Expander>
<Expander Name="PICExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define Permanent Information &#x26; Control (PIC)">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="LayerbreakLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Layerbreak (sectors)"/>
<TextBox x:Name="LayerbreakTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding LayerbreakString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding LayerbreakTextBoxEnabled}" />
<Label x:Name="PICLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="PIC"/>
<TextBox x:Name="PICTextBox" Grid.Row="1" Grid.Column="1" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Top"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"
MinHeight="55" MaxHeight="146" AcceptsReturn="True"
Text="{Binding PICString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding PICTextBoxEnabled}" />
<Label x:Name="PICPathLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center"
ToolTip="Typically a *.physical or *_PIC.bin file" Content="PIC File Path"/>
<TextBox x:Name="PICPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
Text="{Binding PICPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding PICPathTextBoxEnabled}" />
<Button x:Name="PICPathBrowseButton" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding PICPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<Label x:Name="PICStatusLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="PIC Status"/>
<Label x:Name="PICStatusText" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" Content="{Binding PICStatus}"/>
</Grid>
</Expander>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center"
Text="{Binding CreateIRDStatus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</UniformGrid>
</GroupBox>
<!-- Create IRD / Cancel -->
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
<Label/>
<!-- Empty label for padding -->
<Button Name="CreateIRDButton" Height="25" Width="80" IsDefault="True" Content="Create IRD"
IsEnabled="{Binding CreateIRDButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
IsEnabled="{Binding CancelButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Label/>
<!-- Empty label for padding -->
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</coreWindows:WindowBase>

View File

@@ -0,0 +1,475 @@
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using MPF.Core.UI.ViewModels;
using WPFCustomMessageBox;
using WinForms = System.Windows.Forms;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.Windows
{
/// <summary>
/// Interaction logic for CreateIRDWindow.xaml
/// </summary>
public partial class CreateIRDWindow : WindowBase
{
/// <summary>
/// Read-only access to the current check dump view model
/// </summary>
public CreateIRDViewModel CreateIRDViewModel => DataContext as CreateIRDViewModel ?? new CreateIRDViewModel();
#if NET35
#region Settings
private Button? _InputPathBrowseButton => ItemHelper.FindChild<Button>(this, "InputPathBrowseButton");
private TextBox? _InputPathTextBox => ItemHelper.FindChild<TextBox>(this, "InputPathTextBox");
private Button? _LogPathBrowseButton => ItemHelper.FindChild<Button>(this, "LogPathBrowseButton");
private TextBox? _LogPathTextBox => ItemHelper.FindChild<TextBox>(this, "LogPathTextBox");
private Button? _KeyPathBrowseButton => ItemHelper.FindChild<Button>(this, "KeyPathBrowseButton");
private TextBox? _KeyPathTextBox => ItemHelper.FindChild<TextBox>(this, "KeyPathTextBox");
private TextBox? _KeyTextBox => ItemHelper.FindChild<TextBox>(this, "KeyTextBox");
private TextBox? _DiscIDTextBox => ItemHelper.FindChild<TextBox>(this, "DiscIDTextBox");
private Button? _PICPathBrowseButton => ItemHelper.FindChild<Button>(this, "PICPathBrowseButton");
private TextBox? _PICPathTextBox => ItemHelper.FindChild<TextBox>(this, "PICPathTextBox");
private TextBox? _PICTextBox => ItemHelper.FindChild<TextBox>(this, "PICTextBox");
private TextBox? _LayerbreakTextBox => ItemHelper.FindChild<TextBox>(this, "LayerbreakTextBox");
#endregion
#region Controls
private System.Windows.Controls.Button? _CreateIRDButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CreateIRDButton");
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
#endregion
#endif
/// <summary>
/// Constructor
/// </summary>
public CreateIRDWindow(MainWindow parent)
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
}
/// <summary>
/// Handler for CheckDumpWindow OnContentRendered event
/// </summary>
protected override void OnContentRendered(EventArgs e)
{
base.OnContentRendered(e);
// Add the click handlers to the UI
AddEventHandlers();
}
#region UI Functionality
/// <summary>
/// Add all event handlers
/// </summary>
public void AddEventHandlers()
{
// Main buttons
#if NET35
_CreateIRDButton!.Click += OnCreateIRDClick;
_CancelButton!.Click += OnCancelClick;
#else
CreateIRDButton.Click += OnCreateIRDClick;
CancelButton.Click += OnCancelClick;
#endif
// User Area Click
#if NET35
_InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
_LogPathBrowseButton!.Click += LogPathBrowseButtonClick;
_KeyPathBrowseButton!.Click += KeyPathBrowseButtonClick;
_PICPathBrowseButton!.Click += PICPathBrowseButtonClick;
#else
InputPathBrowseButton.Click += InputPathBrowseButtonClick;
LogPathBrowseButton.Click += LogPathBrowseButtonClick;
KeyPathBrowseButton.Click += KeyPathBrowseButtonClick;
PICPathBrowseButton.Click += PICPathBrowseButtonClick;
#endif
// User Area TextChanged
#if NET35
_InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
_LogPathTextBox!.TextChanged += LogPathTextBoxTextChanged;
_KeyPathTextBox!.TextChanged += KeyPathTextBoxTextChanged;
_KeyTextBox!.TextChanged += KeyTextBoxTextChanged;
_DiscIDTextBox!.TextChanged += DiscIDTextBoxTextChanged;
_PICPathTextBox!.TextChanged += PICPathTextBoxTextChanged;
_PICTextBox!.TextChanged += PICTextBoxTextChanged;
_LayerbreakTextBox!.TextChanged += LayerbreakTextBoxTextChanged;
#else
InputPathTextBox.TextChanged += InputPathTextBoxTextChanged;
LogPathTextBox.TextChanged += LogPathTextBoxTextChanged;
KeyPathTextBox.TextChanged += KeyPathTextBoxTextChanged;
KeyTextBox.TextChanged += KeyTextBoxTextChanged;
DiscIDTextBox.TextChanged += DiscIDTextBoxTextChanged;
PICPathTextBox.TextChanged += PICPathTextBoxTextChanged;
PICTextBox.TextChanged += PICTextBoxTextChanged;
LayerbreakTextBox.TextChanged += LayerbreakTextBoxTextChanged;
#endif
}
/// <summary>
/// Browse for an input ISO file path
/// </summary>
public void BrowseISOFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.InputPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "ISO|*.iso|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.InputPath = fileDialog.FileName;
}
}
/// <summary>
/// Browse for an .getkey.log file path
/// </summary>
public void BrowseLogFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.LogPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "GetKey Log|*.getkey.log|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.LogPath = fileDialog.FileName;
}
}
/// <summary>
/// Browse for an key file path
/// </summary>
public void BrowseKeyFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.LogPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "Key|*.key|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.KeyPath = fileDialog.FileName;
}
}
/// <summary>
/// Browse for an IRD output path
/// </summary>
/// <returns>Output path if provided, else null</returns>
public string? BrowseOutputFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.InputPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = Path.Combine(CreateIRDViewModel.Options.DefaultOutputPath, "game.ird");
else if (string.IsNullOrEmpty(currentPath))
currentPath = "game.ird";
if (string.IsNullOrEmpty(currentPath))
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, "game.ird");
// Get the full path
currentPath = Path.GetFullPath(currentPath);
// Get the directory
var directory = Path.GetDirectoryName(currentPath);
// Get the filename
string filename = Path.ChangeExtension(Path.GetFileName(currentPath), ".ird");
WinForms.FileDialog fileDialog = new WinForms.SaveFileDialog
{
FileName = filename,
InitialDirectory = directory,
Filter = "IRD File|*.ird|All Files|*.*"
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
return fileDialog.FileName;
else
return null;
}
/// <summary>
/// Browse for an PIC file path
/// </summary>
public void BrowsePICFile()
{
// Get the current path, if possible
string? currentPath = CreateIRDViewModel.LogPath;
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
if (string.IsNullOrEmpty(currentPath))
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
// Get the full directory
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
{
InitialDirectory = directory,
Filter = "PIC|*.physical;*_PIC.bin;*.PIC|All Files|*.*",
};
WinForms.DialogResult result = fileDialog.ShowDialog();
if (result == WinForms.DialogResult.OK)
{
CreateIRDViewModel.PICPath = fileDialog.FileName;
}
}
/// <summary>
/// Display a user message using a CustomMessageBox
/// </summary>
/// <param name="title">Title to display to the user</param>
/// <param name="message">Message to display to the user</param>
/// <param name="optionCount">Number of options to display</param>
/// <param name="flag">true for inquiry, false otherwise</param>
/// <returns>true for positive, false for negative, null for neutral</returns>
public bool? DisplayUserMessage(string title, string message, int optionCount, bool flag)
{
// Set the correct button style
var button = optionCount switch
{
1 => MessageBoxButton.OK,
2 => MessageBoxButton.YesNo,
3 => MessageBoxButton.YesNoCancel,
// This should not happen, but default to "OK"
_ => MessageBoxButton.OK,
};
// Set the correct icon
MessageBoxImage image = flag ? MessageBoxImage.Question : MessageBoxImage.Exclamation;
// Display and get the result
MessageBoxResult result = CustomMessageBox.Show(this, message, title, button, image);
return result switch
{
MessageBoxResult.OK or MessageBoxResult.Yes => true,
MessageBoxResult.No => false,
_ => null,
};
}
#endregion
#region Event Handlers
/// <summary>
/// Handler for CreateIRDButton Click event
/// </summary>
private void OnCreateIRDClick(object sender, EventArgs e)
{
KeyExpander.IsExpanded = false;
DiscIDExpander.IsExpanded = false;
PICExpander.IsExpanded = false;
string tempStatus = CreateIRDViewModel.CreateIRDStatus;
bool[] enabledFields = CreateIRDViewModel.DisableUIFields();
CreateIRDViewModel.CreateIRDStatus = "Creating IRD... Please Wait";
string? outputPath = BrowseOutputFile();
string? errorMessage = "Please provide an output path";
if (outputPath != null)
{
errorMessage = CreateIRDViewModel.CreateIRD(outputPath);
}
if (string.IsNullOrEmpty(errorMessage))
{
bool? checkAgain = DisplayUserMessage("IRD Create", "An IRD has been created successfully! Would you like to create another IRD?", 2, false);
if (checkAgain == false)
Close();
else
CreateIRDViewModel.ResetFields();
}
else
{
DisplayUserMessage("Failed to create IRD", errorMessage!, 1, false);
CreateIRDViewModel.ReenableUIFields(enabledFields);
CreateIRDViewModel.CreateIRDStatus = tempStatus;
}
}
/// <summary>
/// Handler for CancelButtom Click event
/// </summary>
private void OnCancelClick(object sender, EventArgs e)
{
Close();
}
/// <summary>
/// Handler for DiscIDTextBox TextChanged event
/// </summary>
public void DiscIDTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeDiscID();
}
/// <summary>
/// Handler for InputPathBrowseButton Click event
/// </summary>
public void InputPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseISOFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeInputPath();
}
/// <summary>
/// Handler for InputPathTextBox TextChanged event
/// </summary>
public void InputPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeInputPath();
}
/// <summary>
/// Handler for LogPathBrowseButton Click event
/// </summary>
public void LogPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseLogFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeLogPath();
}
/// <summary>
/// Handler for LogPathTextBox TextChanged event
/// </summary>
public void LogPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeLogPath();
}
/// <summary>
/// Handler for KeyPathBrowseButton Click event
/// </summary>
public void KeyPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowseKeyFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeKeyPath();
}
/// <summary>
/// Handler for KeyPathTextBox TextChanged event
/// </summary>
public void KeyPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeKeyPath();
}
/// <summary>
/// Handler for KeyTextBox TextChanged event
/// </summary>
public void KeyTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeKey();
}
/// <summary>
/// Handler for PICPathBrowseButton Click event
/// </summary>
public void PICPathBrowseButtonClick(object sender, RoutedEventArgs e)
{
BrowsePICFile();
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangePICPath();
}
/// <summary>
/// Handler for PICPathTextBox TextChanged event
/// </summary>
public void PICPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangePICPath();
}
/// <summary>
/// Handler for PICTextBox TextChanged event
/// </summary>
public void PICTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangePIC();
}
/// <summary>
/// Handler for LayerbreakTextBox TextChanged event
/// </summary>
public void LayerbreakTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
if (CreateIRDViewModel.CanExecuteSelectionChanged)
CreateIRDViewModel.ChangeLayerbreak();
}
#endregion
}
}

View File

@@ -0,0 +1,481 @@
<windows:WindowBase x:Class="MPF.UI.Core.Windows.DiscInformationWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
xmlns:core="clr-namespace:MPF.UI.Core"
xmlns:redump="clr-namespace:SabreTools.RedumpLib.Data;assembly=SabreTools.RedumpLib"
xmlns:windows="clr-namespace:MPF.UI.Core.Windows"
mc:Ignorable="d"
Title="Disc Information" Width="515" WindowStyle="None"
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical" Width="500" MaxHeight="650">
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition Width="115"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<Run FontWeight="Bold" Text="Disc Information" />
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
BorderBrush="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<TabControl Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomTabControlStyle}">
<TabItem x:Name="CommonInfo" Header="Common Info" Style="{DynamicResource CustomTabItemStyle}">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<GroupBox Header="Common Disc Information" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput Label="Title"
Text="{Binding SubmissionInfo.CommonDiscInfo.Title, Mode=TwoWay}"/>
<controls:UserInput x:Name="AlternativeTitleTextBox" Label="Alternative Title"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AlternativeTitle], Mode=TwoWay}"/>
<controls:UserInput Label="Foreign Title (Non-Latin)"
Text="{Binding SubmissionInfo.CommonDiscInfo.ForeignTitleNonLatin, Mode=TwoWay}"/>
<controls:UserInput x:Name="AlternativeForeignTitleTextBox" Label="Alternative Foreign Title"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AlternativeForeignTitle], Mode=TwoWay}"/>
<controls:UserInput Label="Disc Number / Letter"
Text="{Binding SubmissionInfo.CommonDiscInfo.DiscNumberLetter, Mode=TwoWay}"/>
<controls:UserInput Label="Disc Title"
Text="{Binding SubmissionInfo.CommonDiscInfo.DiscTitle, Mode=TwoWay}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label x:Name="CategoryLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Category" />
<ComboBox x:Name="CategoryComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
ItemsSource="{Binding Categories}" SelectedItem="{Binding SubmissionInfo.CommonDiscInfo.Category, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label x:Name="RegionLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Region" />
<ComboBox x:Name="RegionComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
ItemsSource="{Binding Regions}" SelectedItem="{Binding SubmissionInfo.CommonDiscInfo.Region, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
Style="{DynamicResource CustomComboBoxStyle}" />
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label x:Name="LanguagesLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Languages" />
<ComboBox x:Name="LanguagesComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch"
ItemsSource="{Binding Languages}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
<Grid x:Name="LanguageSelectionGrid" Visibility="Collapsed">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label x:Name="LanguageSelectionLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Language Selection Via" />
<ComboBox x:Name="LanguageSelectionComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch"
ItemsSource="{Binding LanguageSelections}"
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Name}" IsChecked="{Binding IsChecked}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
<controls:UserInput Label="Serial" Text="{Binding SubmissionInfo.CommonDiscInfo.Serial, Mode=TwoWay}"/>
<controls:UserInput Label="Barcode" Text="{Binding SubmissionInfo.CommonDiscInfo.Barcode, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="Version and Editions" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput Label="Version" Text="{Binding SubmissionInfo.VersionAndEditions.Version, Mode=TwoWay}"/>
<controls:UserInput Label="Edition" Text="{Binding SubmissionInfo.VersionAndEditions.OtherEditions, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="Additional Info" Style="{DynamicResource CustomTabItemStyle}">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<GroupBox Header="Extras" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="CommentsTextBox" Label="Comments"
Text="{Binding SubmissionInfo.CommonDiscInfo.Comments, Mode=TwoWay}" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top" />
<controls:UserInput x:Name="DiscKeyTextBox" Label="Disc Key" Visibility="Collapsed"
Text="{Binding Path=SubmissionInfo.Extras.DiscKey, Mode=TwoWay}"/>
<controls:UserInput x:Name="DiscIDTextBox" Label="Disc ID" Visibility="Collapsed"
Text="{Binding Path=SubmissionInfo.Extras.DiscID, Mode=TwoWay}"/>
<controls:UserInput x:Name="GenreTextBox" Label="Genre"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Genre], Mode=TwoWay}"/>
<controls:UserInput x:Name="ProtectionTextBox" Label="Protection" ToolTip="CAUTION: Only edit if you know what you are doing!"
Text="{Binding SubmissionInfo.CopyProtection.Protection, Mode=TwoWay}" TextHeight="75" TextWrapping="Wrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label/>
<CheckBox Grid.Column="1" x:Name="PostgapTypeCheckBox" Content="Postgap type (Form 2)" Margin="0,4"
IsChecked="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PostgapType], Mode=TwoWay}"/>
</Grid>
<controls:UserInput x:Name="SeriesTextBox" Label="Series"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Series], Mode=TwoWay}"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.75*" />
<ColumnDefinition Width="1.25*" />
</Grid.ColumnDefinitions>
<Label/>
<CheckBox Grid.Column="1" x:Name="VCDCheckBox" Content="V-CD" Margin="0,4"
IsChecked="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VCD], Mode=TwoWay}"/>
</Grid>
</StackPanel>
</GroupBox>
<GroupBox Header="Physical Identifiers" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="BBFCRegistrationNumberTextBox" Label="BBFC Reg. No."
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BBFCRegistrationNumber], Mode=TwoWay}"/>
<controls:UserInput x:Name="DiscHologramIDTextBox" Label="Disc Hologram ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DiscHologramID], Mode=TwoWay}"/>
<controls:UserInput x:Name="DNASDiscIDTextBox" Label="DNAS Disc ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DNASDiscID], Mode=TwoWay}"/>
<controls:UserInput x:Name="ISBNTextBox" Label="ISBN"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ISBN], Mode=TwoWay}"/>
<controls:UserInput x:Name="ISSNTextBox" Label="ISSN"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ISSN], Mode=TwoWay}"/>
<controls:UserInput x:Name="PPNTextBox" Label="PPN"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PPN], Mode=TwoWay}"/>
<controls:UserInput x:Name="VFCCodeTextBox" Label="VFC code"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VFCCode], Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
<GroupBox Header="Publisher Identifiers" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="AcclaimIDTextBox" Label="Acclaim ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AcclaimID], Mode=TwoWay}"/>
<controls:UserInput x:Name="ActivisionIDTextBox" Label="Activision ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ActivisionID], Mode=TwoWay}"/>
<controls:UserInput x:Name="BandaiIDTextBox" Label="Bandai ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BandaiID], Mode=TwoWay}"/>
<controls:UserInput x:Name="CDProjektIDTextBox" Label="CD Projekt ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)CDProjektID], Mode=TwoWay}"/>
<controls:UserInput x:Name="ElectronicArtsIDTextBox" Label="Electronic Arts ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ElectronicArtsID], Mode=TwoWay}"/>
<controls:UserInput x:Name="FoxInteractiveIDTextBox" Label="Fox Interactive ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)FoxInteractiveID], Mode=TwoWay}"/>
<controls:UserInput x:Name="GTInteractiveIDTextBox" Label="GT Interactive ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)GTInteractiveID], Mode=TwoWay}"/>
<controls:UserInput x:Name="JASRACIDTextBox" Label="JASRAC ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)JASRACID], Mode=TwoWay}"/>
<controls:UserInput x:Name="KingRecordsIDTextBox" Label="King Records ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KingRecordsID], Mode=TwoWay}"/>
<controls:UserInput x:Name="KoeiIDTextBox" Label="Koei ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KoeiID], Mode=TwoWay}"/>
<controls:UserInput x:Name="KonamiIDTextBox" Label="Konami ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KonamiID], Mode=TwoWay}"/>
<controls:UserInput x:Name="LucasArtsIDTextBox" Label="LucasArts ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)LucasArtsID], Mode=TwoWay}"/>
<controls:UserInput x:Name="MicrosoftIDTextBox" Label="Microsoft ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)MicrosoftID], Mode=TwoWay}"/>
<controls:UserInput x:Name="NaganoIDTextBox" Label="Nagano ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NaganoID], Mode=TwoWay}"/>
<controls:UserInput x:Name="NamcoIDTextBox" Label="Namco ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NamcoID], Mode=TwoWay}"/>
<controls:UserInput x:Name="NipponIchiSoftwareIDTextBox" Label="Nippon Ichi Software ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NipponIchiSoftwareID], Mode=TwoWay}"/>
<controls:UserInput x:Name="OriginIDTextBox" Label="Origin ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)OriginID], Mode=TwoWay}"/>
<controls:UserInput x:Name="PonyCanyonIDTextBox" Label="Pony Canyon ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PonyCanyonID], Mode=TwoWay}"/>
<controls:UserInput x:Name="SegaIDTextBox" Label="Sega ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SegaID], Mode=TwoWay}"/>
<controls:UserInput x:Name="SelenIDTextBox" Label="Selen ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SelenID], Mode=TwoWay}"/>
<controls:UserInput x:Name="SierraIDTextBox" Label="Sierra ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SierraID], Mode=TwoWay}"/>
<controls:UserInput x:Name="TaitoIDTextBox" Label="Taito ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)TaitoID], Mode=TwoWay}"/>
<controls:UserInput x:Name="UbisoftIDTextBox" Label="Ubisoft ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)UbisoftID], Mode=TwoWay}"/>
<controls:UserInput x:Name="ValveIDTextBox" Label="Valve ID"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ValveID], Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="Contents" Style="{DynamicResource CustomTabItemStyle}">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="GeneralContent" Label="General"
Text="{Binding SubmissionInfo.CommonDiscInfo.Contents, Mode=TwoWay}" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
<!-- Games -->
<controls:UserInput x:Name="GamesTextBox" Label="Games" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Games], Mode=TwoWay}"/>
<controls:UserInput x:Name="NetYarozeGamesTextBox" Label="Net Yaroze Games" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)NetYarozeGames], Mode=TwoWay}"/>
<!-- Demos -->
<controls:UserInput x:Name="PlayableDemosTextBox" Label="Playable Demos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)PlayableDemos], Mode=TwoWay}"/>
<controls:UserInput x:Name="RollingDemosTextBox" Label="Rolling Demos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)RollingDemos], Mode=TwoWay}"/>
<controls:UserInput x:Name="TechDemosTextBox" Label="Tech Demos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)TechDemos], Mode=TwoWay}"/>
<!-- Video -->
<controls:UserInput x:Name="GameFootageTextBox" Label="Game Footage" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)GameFootage], Mode=TwoWay}"/>
<controls:UserInput x:Name="VideosTextBox" Label="Videos" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Videos], Mode=TwoWay}"/>
<!-- Miscellaneous -->
<controls:UserInput x:Name="PatchesTextBox" Label="Patches" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Patches], Mode=TwoWay}"/>
<controls:UserInput x:Name="SavegamesTextBox" Label="Savegames" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Savegames], Mode=TwoWay}"/>
<controls:UserInput x:Name="ExtrasTextBox" Label="Extras" TextHeight="50"
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Extras], Mode=TwoWay}"/>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="Ringcodes" Style="{DynamicResource CustomTabItemStyle}">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<GroupBox x:Name="L0Info" Header="Data/L0 Info" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L0MasteringRing" Label="Data/L0 Mastering Ring"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0MasteringRing, Mode=TwoWay}"/>
<controls:UserInput x:Name="L0MasteringSID" Label="Data/L0 Mastering SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0MasteringSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L0Toolstamp" Label="Data/L0 Toolstamp/Mastering Code"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0ToolstampMasteringCode, Mode=TwoWay}"/>
<controls:UserInput x:Name="L0MouldSID" Label="Data/L0 Mould SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0MouldSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L0AdditionalMould" Label="Data/L0 Additional Mould"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0AdditionalMould, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
<GroupBox x:Name="L1Info" Header="Label/L1 Info" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L1MasteringRing" Label="Label/L1 Mastering Ring"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1MasteringRing, Mode=TwoWay}"/>
<controls:UserInput x:Name="L1MasteringSID" Label="Label/L1 Mastering SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1MasteringSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L1Toolstamp" Label="Label/L1 Toolstamp/Mastering Code"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1ToolstampMasteringCode, Mode=TwoWay}"/>
<controls:UserInput x:Name="L1MouldSID" Label="Label/L1 Mould SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1MouldSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L1AdditionalMould" Label="Label/L1 Additional Mould"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1AdditionalMould, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
<GroupBox x:Name="L2Info" Header="L2 Info" Visibility="Collapsed" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L2MasteringRing" Label="L2 Mastering Ring"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer2MasteringRing, Mode=TwoWay}"/>
<controls:UserInput x:Name="L2MasteringSID" Label="L2 Mastering SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer2MasteringSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L2Toolstamp" Label="L2 Toolstamp/Mastering Code"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer2ToolstampMasteringCode, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
<GroupBox x:Name="L3Info" Header="L3 Info" Visibility="Collapsed" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="L3MasteringRing" Label="L3 Mastering Ring"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer3MasteringRing, Mode=TwoWay}"/>
<controls:UserInput x:Name="L3MasteringSID" Label="L3 Mastering SID"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer3MasteringSID, Mode=TwoWay}"/>
<controls:UserInput x:Name="L3Toolstamp" Label="L3 Toolstamp/Mastering Code"
Text="{Binding SubmissionInfo.CommonDiscInfo.Layer3ToolstampMasteringCode, Mode=TwoWay}"/>
</StackPanel>
</GroupBox>
</StackPanel>
</ScrollViewer>
</TabItem>
<TabItem Header="Read-Only Info" Style="{DynamicResource CustomTabItemStyle}">
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
<StackPanel Orientation="Vertical">
<controls:UserInput x:Name="FullyMatchedID" Label="Fully Matched ID" IsReadOnly="True"
Text="{Binding SubmissionInfo.FullyMatchedID, Mode=TwoWay}"/>
<controls:UserInput x:Name="PartiallyMatchedIDs" Label="Partially Matched ID(s)" IsReadOnly="True"/>
<controls:UserInput x:Name="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.AntiModchip, Mode=TwoWay}"/>
<controls:UserInput x:Name="DiscOffset" Label="Disc Offset" IsReadOnly="True"
Text="{Binding SubmissionInfo.TracksAndWriteOffsets.OtherWriteOffsets, Mode=TwoWay}"/>
<controls:UserInput x:Name="DMIHash" Label="DMI Hash" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DMIHash], Mode=TwoWay}"/>
<controls:UserInput x:Name="EDC" Label="EDC" IsReadOnly="True"
Text="{Binding SubmissionInfo.EDC.EDC, Mode=TwoWay}"/>
<controls:UserInput x:Name="ErrorsCount" Label="Error(s) Count" IsReadOnly="True"
Text="{Binding SubmissionInfo.CommonDiscInfo.ErrorsCount, Mode=TwoWay}"/>
<controls:UserInput x:Name="EXEDateBuildDate" Label="EXE/Build Date" IsReadOnly="True"
Text="{Binding SubmissionInfo.CommonDiscInfo.EXEDateBuildDate, Mode=TwoWay}"/>
<controls:UserInput x:Name="Filename" Label="Filename" IsReadOnly="True"
TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Filename], Mode=TwoWay}"/>
<controls:UserInput x:Name="Header" Label="Header" IsReadOnly="True"
Text="{Binding SubmissionInfo.Extras.Header, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="InternalName" Label="Internal Names" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)InternalName], Mode=TwoWay}"/>
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)InternalSerialName], Mode=TwoWay}"/>
<controls:UserInput x:Name="Multisession" Label="Multisession" IsReadOnly="True" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Multisession], Mode=TwoWay}"/>
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.LibCrypt, Mode=TwoWay}"/>
<controls:UserInput x:Name="LibCryptData" Label="LibCrypt Data" IsReadOnly="True"
Text="{Binding SubmissionInfo.CopyProtection.LibCryptData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="PFIHash" Label="PFI Hash" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PFIHash], Mode=TwoWay}"/>
<controls:UserInput x:Name="PIC" Label="PIC" IsReadOnly="True"
Text="{Binding SubmissionInfo.Extras.PIC, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
<controls:UserInput x:Name="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"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)RingNonZeroDataStart], Mode=TwoWay}"/>
<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"/>
<controls:UserInput x:Name="SSHash" Label="Security Sector Hash" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SSHash], Mode=TwoWay}"/>
<controls:UserInput x:Name="SecuritySectorRanges" Label="Security Sector Ranges" IsReadOnly="True"
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"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SSVersion], Mode=TwoWay}"/>
<controls:UserInput x:Name="UniversalHash" Label="Universal Hash (SHA-1)" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)UniversalHash], Mode=TwoWay}"/>
<controls:UserInput x:Name="VolumeLabel" Label="Volume Label" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VolumeLabel], Mode=TwoWay}"/>
<controls:UserInput x:Name="XeMID" Label="XeMID" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)XeMID], Mode=TwoWay}"/>
<controls:UserInput x:Name="XMID" Label="XMID" IsReadOnly="True"
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)XMID], Mode=TwoWay}"/>
<!-- TODO: Add track/checksum data once there's a reasonable way to do so -->
</StackPanel>
</ScrollViewer>
</TabItem>
</TabControl>
<!-- Accept / Cancel -->
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<UniformGrid Columns="3" Margin="5,5,5,5" Height="28">
<Button Name="AcceptButton" Height="25" Width="80" Content="Accept"
Style="{DynamicResource CustomButtonStyle}" />
<Button Name="CancelButton" Height="25" Width="80" Content="Cancel"
Style="{DynamicResource CustomButtonStyle}" />
<Button Name="RingCodeGuideButton" Grid.Column="2" Height="25" Width="120" Content="Ring Code Guide"
Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
</StackPanel>
</Grid>
</windows:WindowBase>

View File

@@ -0,0 +1,759 @@
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using MPF.Core.Data;
using MPF.Core.UI.ViewModels;
using MPF.Core.Utilities;
using MPF.UI.Core.UserControls;
using SabreTools.RedumpLib.Data;
#pragma warning disable IDE1006 // Naming Styles
namespace MPF.UI.Core.Windows
{
/// <summary>
/// Interaction logic for DiscInformationWindow.xaml
/// </summary>
public partial class DiscInformationWindow : WindowBase
{
#if NET35
#region Common Info
private Grid? _LanguageSelectionGrid => ItemHelper.FindChild<Grid>(this, "LanguageSelectionGrid");
#endregion
#region Additional Info
private UserInput? _CommentsTextBox => ItemHelper.FindChild<UserInput>(this, "CommentsTextBox");
private UserInput? _DiscIDTextBox => ItemHelper.FindChild<UserInput>(this, "DiscIDTextBox");
private UserInput? _DiscKeyTextBox => ItemHelper.FindChild<UserInput>(this, "DiscKeyTextBox");
#endregion
#region Contents
private UserInput? _ExtrasTextBox => ItemHelper.FindChild<UserInput>(this, "ExtrasTextBox");
private UserInput? _GameFootageTextBox => ItemHelper.FindChild<UserInput>(this, "GameFootageTextBox");
private UserInput? _GamesTextBox => ItemHelper.FindChild<UserInput>(this, "GamesTextBox");
private UserInput? _GeneralContent => ItemHelper.FindChild<UserInput>(this, "GeneralContent");
private UserInput? _NetYarozeGamesTextBox => ItemHelper.FindChild<UserInput>(this, "NetYarozeGamesTextBox");
private UserInput? _PatchesTextBox => ItemHelper.FindChild<UserInput>(this, "PatchesTextBox");
private UserInput? _PlayableDemosTextBox => ItemHelper.FindChild<UserInput>(this, "PlayableDemosTextBox");
private UserInput? _RollingDemosTextBox => ItemHelper.FindChild<UserInput>(this, "RollingDemosTextBox");
private UserInput? _SavegamesTextBox => ItemHelper.FindChild<UserInput>(this, "SavegamesTextBox");
private UserInput? _TechDemosTextBox => ItemHelper.FindChild<UserInput>(this, "TechDemosTextBox");
private UserInput? _VideosTextBox => ItemHelper.FindChild<UserInput>(this, "VideosTextBox");
#endregion
#region Ringcodes
private GroupBox? _L0Info => ItemHelper.FindChild<GroupBox>(this, "L0Info");
private UserInput? _L0MasteringRing => ItemHelper.FindChild<UserInput>(this, "L0MasteringRing");
private UserInput? _L0MasteringSID => ItemHelper.FindChild<UserInput>(this, "L0MasteringSID");
private UserInput? _L0Toolstamp => ItemHelper.FindChild<UserInput>(this, "L0Toolstamp");
private UserInput? _L0MouldSID => ItemHelper.FindChild<UserInput>(this, "L0MouldSID");
private UserInput? _L0AdditionalMould => ItemHelper.FindChild<UserInput>(this, "L0AdditionalMould");
private GroupBox? _L1Info => ItemHelper.FindChild<GroupBox>(this, "L1Info");
private UserInput? _L1MasteringRing => ItemHelper.FindChild<UserInput>(this, "L1MasteringRing");
private UserInput? _L1MasteringSID => ItemHelper.FindChild<UserInput>(this, "L1MasteringSID");
private UserInput? _L1Toolstamp => ItemHelper.FindChild<UserInput>(this, "L1Toolstamp");
private UserInput? _L1MouldSID => ItemHelper.FindChild<UserInput>(this, "L1MouldSID");
private UserInput? _L1AdditionalMould => ItemHelper.FindChild<UserInput>(this, "L1AdditionalMould");
private GroupBox? _L2Info => ItemHelper.FindChild<GroupBox>(this, "L2Info");
private UserInput? _L2MasteringRing => ItemHelper.FindChild<UserInput>(this, "L2MasteringRing");
private UserInput? _L2MasteringSID => ItemHelper.FindChild<UserInput>(this, "L2MasteringSID");
private UserInput? _L2Toolstamp => ItemHelper.FindChild<UserInput>(this, "L2Toolstamp");
private GroupBox? _L3Info => ItemHelper.FindChild<GroupBox>(this, "L3Info");
private UserInput? _L3MasteringRing => ItemHelper.FindChild<UserInput>(this, "L3MasteringRing");
private UserInput? _L3MasteringSID => ItemHelper.FindChild<UserInput>(this, "L3MasteringSID");
private UserInput? _L3Toolstamp => ItemHelper.FindChild<UserInput>(this, "L3Toolstamp");
#endregion
#region Read-Only Info
private UserInput? _FullyMatchedID => ItemHelper.FindChild<UserInput>(this, "FullyMatchedID");
private UserInput? _PartiallyMatchedIDs => ItemHelper.FindChild<UserInput>(this, "PartiallyMatchedIDs");
private UserInput? _AntiModchip => ItemHelper.FindChild<UserInput>(this, "AntiModchip");
private UserInput? _DiscOffset => ItemHelper.FindChild<UserInput>(this, "DiscOffset");
private UserInput? _DMIHash => ItemHelper.FindChild<UserInput>(this, "DMIHash");
private UserInput? _EDC => ItemHelper.FindChild<UserInput>(this, "EDC");
private UserInput? _ErrorsCount => ItemHelper.FindChild<UserInput>(this, "ErrorsCount");
private UserInput? _EXEDateBuildDate => ItemHelper.FindChild<UserInput>(this, "EXEDateBuildDate");
private UserInput? _Filename => ItemHelper.FindChild<UserInput>(this, "Filename");
private UserInput? _Header => ItemHelper.FindChild<UserInput>(this, "Header");
private UserInput? _InternalName => ItemHelper.FindChild<UserInput>(this, "InternalName");
private UserInput? _InternalSerialName => ItemHelper.FindChild<UserInput>(this, "InternalSerialName");
private UserInput? _Multisession => ItemHelper.FindChild<UserInput>(this, "Multisession");
private UserInput? _LibCrypt => ItemHelper.FindChild<UserInput>(this, "LibCrypt");
private UserInput? _LibCryptData => ItemHelper.FindChild<UserInput>(this, "LibCryptData");
private UserInput? _PFIHash => ItemHelper.FindChild<UserInput>(this, "PFIHash");
private UserInput? _PIC => ItemHelper.FindChild<UserInput>(this, "PIC");
private UserInput? _PVD => ItemHelper.FindChild<UserInput>(this, "PVD");
private UserInput? _RingNonZeroDataStart => ItemHelper.FindChild<UserInput>(this, "RingNonZeroDataStart");
private UserInput? _SecuROMData => ItemHelper.FindChild<UserInput>(this, "SecuROMData");
private UserInput? _SSHash => ItemHelper.FindChild<UserInput>(this, "SSHash");
private UserInput? _SecuritySectorRanges => ItemHelper.FindChild<UserInput>(this, "SecuritySectorRanges");
private UserInput? _SSVersion => ItemHelper.FindChild<UserInput>(this, "SSVersion");
private UserInput? _UniversalHash => ItemHelper.FindChild<UserInput>(this, "UniversalHash");
private UserInput? _VolumeLabel => ItemHelper.FindChild<UserInput>(this, "VolumeLabel");
private UserInput? _XeMID => ItemHelper.FindChild<UserInput>(this, "XeMID");
private UserInput? _XMID => ItemHelper.FindChild<UserInput>(this, "XMID");
#endregion
#region Accept / Cancel
private Button? _AcceptButton => ItemHelper.FindChild<Button>(this, "AcceptButton");
private Button? _CancelButton => ItemHelper.FindChild<Button>(this, "CancelButton");
private Button? _RingCodeGuideButton => ItemHelper.FindChild<Button>(this, "RingCodeGuideButton");
#endregion
#endif
/// <summary>
/// Read-only access to the current disc information view model
/// </summary>
public DiscInformationViewModel DiscInformationViewModel => DataContext as DiscInformationViewModel ?? new DiscInformationViewModel(new Options(), new SubmissionInfo());
/// <summary>
/// Constructor
/// </summary>
public DiscInformationWindow(Options options, SubmissionInfo? submissionInfo)
{
#if NET40_OR_GREATER || NETCOREAPP
InitializeComponent();
#endif
#if NET452_OR_GREATER || NETCOREAPP
var chrome = new System.Windows.Shell.WindowChrome
{
CaptionHeight = 0,
ResizeBorderThickness = new Thickness(0),
};
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
#endif
DataContext = new DiscInformationViewModel(options, submissionInfo);
DiscInformationViewModel.Load();
// Limit lists, if necessary
if (options.EnableRedumpCompatibility)
{
DiscInformationViewModel.SetRedumpRegions();
DiscInformationViewModel.SetRedumpLanguages();
}
// Add handlers
#if NET35
_AcceptButton!.Click += OnAcceptClick;
_CancelButton!.Click += OnCancelClick;
_RingCodeGuideButton!.Click += OnRingCodeGuideClick;
#else
AcceptButton.Click += OnAcceptClick;
CancelButton.Click += OnCancelClick;
RingCodeGuideButton.Click += OnRingCodeGuideClick;
#endif
// Update UI with new values
ManipulateFields(options, submissionInfo);
}
#region Helpers
/// <summary>
/// Manipulate fields based on the current disc
/// </summary>
private void ManipulateFields(Options options, SubmissionInfo? submissionInfo)
{
// Enable tabs in all fields, if required
if (options.EnableTabsInInputFields)
EnableTabsInInputFields();
// Hide read-only fields that don't have values set
HideReadOnlyFields(submissionInfo);
// Different media types mean different fields available
UpdateFromDiscType(submissionInfo);
// Different systems mean different fields available
UpdateFromSystemType(submissionInfo);
}
/// <summary>
/// Enable tab entry on supported fields
/// </summary>
/// TODO: See if these can be done by binding
private void EnableTabsInInputFields()
{
// Additional Information
#if NET35
_CommentsTextBox!.Tab = true;
#else
CommentsTextBox.Tab = true;
#endif
// Contents
#if NET35
_GeneralContent!.Tab = true;
_GamesTextBox!.Tab = true;
_NetYarozeGamesTextBox!.Tab = true;
_PlayableDemosTextBox!.Tab = true;
_RollingDemosTextBox!.Tab = true;
_TechDemosTextBox!.Tab = true;
_GameFootageTextBox!.Tab = true;
_VideosTextBox!.Tab = true;
_PatchesTextBox!.Tab = true;
_SavegamesTextBox!.Tab = true;
_ExtrasTextBox!.Tab = true;
#else
GeneralContent.Tab = true;
GamesTextBox.Tab = true;
NetYarozeGamesTextBox.Tab = true;
PlayableDemosTextBox.Tab = true;
RollingDemosTextBox.Tab = true;
TechDemosTextBox.Tab = true;
GameFootageTextBox.Tab = true;
VideosTextBox.Tab = true;
PatchesTextBox.Tab = true;
SavegamesTextBox.Tab = true;
ExtrasTextBox.Tab = true;
#endif
// L0
#if NET35
_L0MasteringRing!.Tab = true;
_L0MasteringSID!.Tab = true;
_L0Toolstamp!.Tab = true;
_L0MouldSID!.Tab = true;
_L0AdditionalMould!.Tab = true;
#else
L0MasteringRing.Tab = true;
L0MasteringSID.Tab = true;
L0Toolstamp.Tab = true;
L0MouldSID.Tab = true;
L0AdditionalMould.Tab = true;
#endif
// L1
#if NET35
_L1MasteringRing!.Tab = true;
_L1MasteringSID!.Tab = true;
_L1Toolstamp!.Tab = true;
_L1MouldSID!.Tab = true;
_L1AdditionalMould!.Tab = true;
#else
L1MasteringRing.Tab = true;
L1MasteringSID.Tab = true;
L1Toolstamp.Tab = true;
L1MouldSID.Tab = true;
L1AdditionalMould.Tab = true;
#endif
// L2
#if NET35
_L2MasteringRing!.Tab = true;
_L2MasteringSID!.Tab = true;
_L2Toolstamp!.Tab = true;
#else
L2MasteringRing.Tab = true;
L2MasteringSID.Tab = true;
L2Toolstamp.Tab = true;
#endif
// L3
#if NET35
_L3MasteringRing!.Tab = true;
_L3MasteringSID!.Tab = true;
_L3Toolstamp!.Tab = true;
#else
L3MasteringRing.Tab = true;
L3MasteringSID.Tab = true;
L3Toolstamp.Tab = true;
#endif
}
/// <summary>
/// Hide any optional, read-only fields if they don't have a value
/// </summary>
/// TODO: Figure out how to bind the PartiallyMatchedIDs array to a text box
/// TODO: Convert visibility to a binding
private void HideReadOnlyFields(SubmissionInfo? submissionInfo)
{
// If there's no submission information
if (submissionInfo == null)
return;
#if NET35
if (submissionInfo.FullyMatchedID == null)
_FullyMatchedID!.Visibility = Visibility.Collapsed;
if (submissionInfo.PartiallyMatchedIDs == null)
_PartiallyMatchedIDs!.Visibility = Visibility.Collapsed;
else
_PartiallyMatchedIDs!.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs.Select(i => i.ToString()).ToArray());
if (submissionInfo.CopyProtection?.AntiModchip == null)
_AntiModchip!.Visibility = Visibility.Collapsed;
if (submissionInfo.TracksAndWriteOffsets?.OtherWriteOffsets == null)
_DiscOffset!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.Keys?.Contains(SiteCode.DMIHash) != true)
_DMIHash!.Visibility = Visibility.Collapsed;
if (submissionInfo.EDC?.EDC == null)
_EDC!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.ErrorsCount))
_ErrorsCount!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
_EXEDateBuildDate!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
_Filename!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.Header))
_Header!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
_InternalName!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalSerialName) != true)
_InternalSerialName!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Multisession) != true)
_Multisession!.Visibility = Visibility.Collapsed;
if (submissionInfo.CopyProtection?.LibCrypt == null)
_LibCrypt!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.LibCryptData))
_LibCryptData!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
_PFIHash!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.PIC))
_PIC!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.PVD))
_PVD!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
_RingNonZeroDataStart!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.SecuROMData))
_SecuROMData!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
_SSHash!.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.SecuritySectorRanges))
_SecuritySectorRanges!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
_SSVersion!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) != true)
_UniversalHash!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.VolumeLabel) != true)
_VolumeLabel!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XeMID) != true)
_XeMID!.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
_XMID!.Visibility = Visibility.Collapsed;
#else
if (submissionInfo.FullyMatchedID == null)
FullyMatchedID.Visibility = Visibility.Collapsed;
if (submissionInfo.PartiallyMatchedIDs == null)
PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
else
PartiallyMatchedIDs.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs.Select(i => i.ToString()).ToArray());
if (submissionInfo.CopyProtection?.AntiModchip == null)
AntiModchip.Visibility = Visibility.Collapsed;
if (submissionInfo.TracksAndWriteOffsets?.OtherWriteOffsets == null)
DiscOffset.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.Keys?.Contains(SiteCode.DMIHash) != true)
DMIHash.Visibility = Visibility.Collapsed;
if (submissionInfo.EDC?.EDC == null)
EDC.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.ErrorsCount))
ErrorsCount.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
EXEDateBuildDate.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
Filename.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.Header))
Header.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
InternalName.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalSerialName) != true)
InternalSerialName.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Multisession) != true)
Multisession.Visibility = Visibility.Collapsed;
if (submissionInfo.CopyProtection?.LibCrypt == null)
LibCrypt.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.LibCryptData))
LibCryptData.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
PFIHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.PIC))
PIC.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.PVD))
PVD.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
RingNonZeroDataStart.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.SecuROMData))
SecuROMData.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
SSHash.Visibility = Visibility.Collapsed;
if (string.IsNullOrEmpty(submissionInfo.Extras?.SecuritySectorRanges))
SecuritySectorRanges.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
SSVersion.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) != true)
UniversalHash.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.VolumeLabel) != true)
VolumeLabel.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XeMID) != true)
XeMID.Visibility = Visibility.Collapsed;
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
XMID.Visibility = Visibility.Collapsed;
#endif
}
/// <summary>
/// Update visible fields and sections based on the media type
/// </summary>
/// TODO: See if these can be done by binding
private void UpdateFromDiscType(SubmissionInfo? submissionInfo)
{
// Sony-printed discs have layers in the opposite order
var system = submissionInfo?.CommonDiscInfo?.System;
bool reverseOrder = system.HasReversedRingcodes();
switch (submissionInfo?.CommonDiscInfo?.Media)
{
case DiscType.CD:
case DiscType.GDROM:
#if NET35
_L0Info!.Header = "Data Side";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Mould SID";
_L0AdditionalMould!.Label = "Additional Mould";
#else
L0Info.Header = "Data Side";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Mould SID";
L0AdditionalMould.Label = "Additional Mould";
#endif
#if NET35
_L1Info!.Header = "Label Side";
_L1MasteringRing!.Visibility = Visibility.Collapsed;
_L1MasteringSID!.Visibility = Visibility.Collapsed;
_L1Toolstamp!.Visibility = Visibility.Collapsed;
_L1MouldSID!.Label = "Mould SID";
_L1AdditionalMould!.Label = "Additional Mould";
#else
L1Info.Header = "Label Side";
L1MasteringRing.Visibility = Visibility.Collapsed;
L1MasteringSID.Visibility = Visibility.Collapsed;
L1Toolstamp.Visibility = Visibility.Collapsed;
L1MouldSID.Label = "Mould SID";
L1AdditionalMould.Label = "Additional Mould";
#endif
break;
case DiscType.DVD5:
case DiscType.DVD9:
case DiscType.HDDVDSL:
case DiscType.HDDVDDL:
case DiscType.BD25:
case DiscType.BD33:
case DiscType.BD50:
case DiscType.BD66:
case DiscType.BD100:
case DiscType.BD128:
case DiscType.NintendoGameCubeGameDisc:
case DiscType.NintendoWiiOpticalDiscSL:
case DiscType.NintendoWiiOpticalDiscDL:
case DiscType.NintendoWiiUOpticalDiscSL:
case DiscType.UMDSL:
case DiscType.UMDDL:
// Quad-layer discs
if (submissionInfo?.SizeAndChecksums?.Layerbreak3 != default(long))
{
#if NET35
_L2Info!.Visibility = Visibility.Visible;
_L3Info!.Visibility = Visibility.Visible;
#else
L2Info.Visibility = Visibility.Visible;
L3Info.Visibility = Visibility.Visible;
#endif
#if NET35
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Data Side Mould SID";
_L0AdditionalMould!.Label = "Data Side Additional Mould";
#else
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
#endif
#if NET35
_L1Info!.Header = "Layer 1";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Label Side Mould SID";
_L1AdditionalMould!.Label = "Label Side Additional Mould";
#else
L1Info.Header = "Layer 1";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
#endif
#if NET35
_L2Info!.Header = "Layer 2";
_L2MasteringRing!.Label = "Mastering Ring";
_L2MasteringSID!.Label = "Mastering SID";
_L2Toolstamp!.Label = "Toolstamp/Mastering Code";
#else
L2Info.Header = "Layer 2";
L2MasteringRing.Label = "Mastering Ring";
L2MasteringSID.Label = "Mastering SID";
L2Toolstamp.Label = "Toolstamp/Mastering Code";
#endif
#if NET35
_L3Info!.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
_L3MasteringRing!.Label = "Mastering Ring";
_L3MasteringSID!.Label = "Mastering SID";
_L3Toolstamp!.Label = "Toolstamp/Mastering Code";
#else
L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
L3MasteringRing.Label = "Mastering Ring";
L3MasteringSID.Label = "Mastering SID";
L3Toolstamp.Label = "Toolstamp/Mastering Code";
#endif
}
// Triple-layer discs
else if (submissionInfo?.SizeAndChecksums?.Layerbreak2 != default(long))
{
#if NET35
_L2Info!.Visibility = Visibility.Visible;
#else
L2Info.Visibility = Visibility.Visible;
#endif
#if NET35
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Data Side Mould SID";
_L0AdditionalMould!.Label = "Data Side Additional Mould";
#else
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
#endif
#if NET35
_L1Info!.Header = "Layer 1";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Label Side Mould SID";
_L1AdditionalMould!.Label = "Label Side Additional Mould";
#else
L1Info.Header = "Layer 1";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
#endif
#if NET35
_L2Info!.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
_L2MasteringRing!.Label = "Mastering Ring";
_L2MasteringSID!.Label = "Mastering SID";
_L2Toolstamp!.Label = "Toolstamp/Mastering Code";
#else
L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
L2MasteringRing.Label = "Mastering Ring";
L2MasteringSID.Label = "Mastering SID";
L2Toolstamp.Label = "Toolstamp/Mastering Code";
#endif
}
// Double-layer discs
else if (submissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
{
#if NET35
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Data Side Mould SID";
_L0AdditionalMould!.Label = "Data Side Additional Mould";
#else
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Data Side Mould SID";
L0AdditionalMould.Label = "Data Side Additional Mould";
#endif
#if NET35
_L1Info!.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
_L1MasteringRing!.Label = "Mastering Ring";
_L1MasteringSID!.Label = "Mastering SID";
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
_L1MouldSID!.Label = "Label Side Mould SID";
_L1AdditionalMould!.Label = "Label Side Additional Mould";
#else
L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
L1MasteringRing.Label = "Mastering Ring";
L1MasteringSID.Label = "Mastering SID";
L1Toolstamp.Label = "Toolstamp/Mastering Code";
L1MouldSID.Label = "Label Side Mould SID";
L1AdditionalMould.Label = "Label Side Additional Mould";
#endif
}
// Single-layer discs
else
{
#if NET35
_L0Info!.Header = "Data Side";
_L0MasteringRing!.Label = "Mastering Ring";
_L0MasteringSID!.Label = "Mastering SID";
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
_L0MouldSID!.Label = "Mould SID";
_L0AdditionalMould!.Label = "Additional Mould";
#else
L0Info.Header = "Data Side";
L0MasteringRing.Label = "Mastering Ring";
L0MasteringSID.Label = "Mastering SID";
L0Toolstamp.Label = "Toolstamp/Mastering Code";
L0MouldSID.Label = "Mould SID";
L0AdditionalMould.Label = "Additional Mould";
#endif
#if NET35
_L1Info!.Header = "Label Side";
_L1MasteringRing!.Visibility = Visibility.Collapsed;
_L1MasteringSID!.Visibility = Visibility.Collapsed;
_L1Toolstamp!.Visibility = Visibility.Collapsed;
_L1MouldSID!.Label = "Mould SID";
_L1AdditionalMould!.Label = "Additional Mould";
#else
L1Info.Header = "Label Side";
L1MasteringRing.Visibility = Visibility.Collapsed;
L1MasteringSID.Visibility = Visibility.Collapsed;
L1Toolstamp.Visibility = Visibility.Collapsed;
L1MouldSID.Label = "Mould SID";
L1AdditionalMould.Label = "Additional Mould";
#endif
}
break;
// All other media we assume to have no rings
default:
#if NET35
_L0Info!.Visibility = Visibility.Collapsed;
_L1Info!.Visibility = Visibility.Collapsed;
_L2Info!.Visibility = Visibility.Collapsed;
_L3Info!.Visibility = Visibility.Collapsed;
#else
L0Info.Visibility = Visibility.Collapsed;
L1Info.Visibility = Visibility.Collapsed;
L2Info.Visibility = Visibility.Collapsed;
L3Info.Visibility = Visibility.Collapsed;
#endif
break;
}
}
/// <summary>
/// Update visible fields and sections based on the system type
/// </summary>
/// TODO: See if these can be done by binding
private void UpdateFromSystemType(SubmissionInfo? submissionInfo)
{
var system = submissionInfo?.CommonDiscInfo?.System;
switch (system)
{
case RedumpSystem.NintendoWiiU:
#if NET35
_DiscKeyTextBox!.Visibility = Visibility.Visible;
#else
DiscKeyTextBox.Visibility = Visibility.Visible;
#endif
break;
case RedumpSystem.SonyPlayStation2:
#if NET35
_LanguageSelectionGrid!.Visibility = Visibility.Visible;
#else
LanguageSelectionGrid.Visibility = Visibility.Visible;
#endif
break;
case RedumpSystem.SonyPlayStation3:
#if NET35
_DiscKeyTextBox!.Visibility = Visibility.Visible;
_DiscIDTextBox!.Visibility = Visibility.Visible;
#else
DiscKeyTextBox.Visibility = Visibility.Visible;
DiscIDTextBox.Visibility = Visibility.Visible;
#endif
break;
}
}
#endregion
#region Event Handlers
/// <summary>
/// Handler for AcceptButton Click event
/// </summary>
private void OnAcceptClick(object sender, RoutedEventArgs e)
{
DiscInformationViewModel.Save();
DialogResult = true;
Close();
}
/// <summary>
/// Handler for CancelButton Click event
/// </summary>
private void OnCancelClick(object sender, RoutedEventArgs e)
{
DialogResult = false;
Close();
}
/// <summary>
/// Handler for RingCodeGuideButton Click event
/// </summary>
private void OnRingCodeGuideClick(object sender, RoutedEventArgs e)
{
var ringCodeGuideWindow = new RingCodeGuideWindow()
{
Owner = this,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
};
ringCodeGuideWindow.Show();
}
#endregion
}
}

View File

@@ -0,0 +1,223 @@
<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.UI.Core.UserControls"
xmlns:core="clr-namespace:MPF.UI.Core"
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
mc:Ignorable="d"
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
BorderBrush="DarkGray" BorderThickness="2">
<Window.DataContext>
<viewModels:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<core:ElementConverter x:Key="ElementConverter" />
</Window.Resources>
<Grid>
<StackPanel Orientation="Vertical">
<Grid Margin="0,2,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="25"/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition Width="50"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
<StackPanel Panel.ZIndex="1" Grid.Column="1" VerticalAlignment="Center" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<Menu x:Name="TopMenuBar" Width="Auto" Height="20"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">
<MenuItem x:Name="FileMenuItem" Header="_File"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}">
<MenuItem x:Name="AppExitMenuItem" Header="E_xit" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
</MenuItem>
<MenuItem x:Name="ToolsMenuItem" Header="_Tools"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}">
<MenuItem x:Name="CheckDumpMenuItem" Header="_Check Dump" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"
IsEnabled="{Binding CheckDumpMenuItemEnabled}"/>
<MenuItem x:Name="CreateIRDMenuItem" Header="_Create PS3 IRD" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"
ToolTip="IRD Creation is only available on .NET Core 6 or newer."
ToolTipService.InitialShowDelay="0"
ToolTipService.IsEnabled="{Binding CannotCreateIRD}"
ToolTipService.ShowOnDisabled="True"
IsEnabled="{Binding CreateIRDMenuItemEnabled}"/>
<MenuItem x:Name="OptionsMenuItem" Header="_Options" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"
IsEnabled="{Binding OptionsMenuItemEnabled}"/>
<MenuItem x:Name="DebugViewMenuItem" Header="_Debug Info Window" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" Visibility="Collapsed" />
</MenuItem>
<MenuItem x:Name="HelpMenuItem" Header="_Help"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}">
<MenuItem x:Name="AboutMenuItem" Header="_About" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
<MenuItem x:Name="CheckForUpdatesMenuItem" Header="_Check for Updates" HorizontalAlignment="Left" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}" />
</MenuItem>
</Menu>
</StackPanel>
<Label Panel.ZIndex="0" Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
<Label.Content>
<TextBlock TextAlignment="Center"><Run FontWeight="Bold" Text="Media Preservation Frontend" /></TextBlock>
</Label.Content>
<Label.ContextMenu>
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Style="{DynamicResource CustomContextMenuStyle}">
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
Template="{DynamicResource CustomMenuItemTemplate}"/>
</ContextMenu>
</Label.ContextMenu>
</Label>
<Grid Grid.Column="5">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
</Button>
</Grid>
</Grid>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Settings">
<Grid Margin="5,5,5,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2.5*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label x:Name="SystemMediaTypeLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
ItemsSource="{Binding Systems}" SelectedItem="{Binding Path=CurrentSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding SystemTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHeader}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right"
ItemsSource="{Binding MediaTypes}" SelectedItem="{Binding Path=CurrentMediaType, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding MediaTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
<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"
Text="{Binding OutputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding OutputPathTextBoxEnabled}" />
<Button x:Name="OutputPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
IsEnabled="{Binding OutputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
<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"
ItemsSource="{Binding Drives}" SelectedItem="{Binding Path=CurrentDrive, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding DriveLetterComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Letter}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<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"
ItemsSource="{Binding DriveSpeeds}" SelectedItem="{Binding Path=DriveSpeed, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding DriveSpeedComboBoxEnabled}" 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"
ItemsSource="{Binding InternalPrograms}" SelectedItem="{Binding Path=CurrentProgram, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding DumpingProgramComboBoxEnabled}" 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" VerticalContentAlignment="Center"
Text="{Binding Parameters, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding ParametersCheckBoxEnabled}"/>
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right"
IsChecked="{Binding ParametersCheckBoxEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding EnableParametersCheckBoxEnabled}"/>
</Grid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Controls">
<UniformGrid Columns="4" Margin="5,5,5,5">
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True"
Content="{Binding StartStopButtonText, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{Binding StartStopButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="MediaScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for discs"
IsEnabled="{Binding MediaScanButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="UpdateVolumeLabel" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Update Label"
IsEnabled="{Binding UpdateVolumeLabelEnabled}" Style="{DynamicResource CustomButtonStyle}" />
<Button x:Name="CopyProtectScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for protection"
IsEnabled="{Binding CopyProtectScanButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
</UniformGrid>
</GroupBox>
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center"
Text="{Binding Status, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</UniformGrid>
</GroupBox>
<Expander Margin="5,5 5 5" MaxHeight="300" HorizontalAlignment="Stretch" Header="Log Output" x:Name="LogPanel"
IsExpanded="{Binding LogPanelExpanded}">
<controls:LogOutput x:Name="LogOutput"/>
</Expander>
</StackPanel>
</Grid>
</coreWindows:WindowBase>

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