mirror of
https://github.com/SabreTools/MPF.git
synced 2026-02-06 21:29:33 +00:00
Compare commits
387 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6a4b6d613a | ||
|
|
97aef5e29c | ||
|
|
9549178c3a | ||
|
|
c531539c87 | ||
|
|
fa04461631 | ||
|
|
83d230dfe1 | ||
|
|
ca18bbb72c | ||
|
|
de0f2c1ad9 | ||
|
|
2f0019282e | ||
|
|
72ca479c9f | ||
|
|
de3c4362e7 | ||
|
|
987348fee8 | ||
|
|
100e012fe6 | ||
|
|
af59ebe1ff | ||
|
|
0762c88655 | ||
|
|
bbe9c94545 | ||
|
|
c7efda7da8 | ||
|
|
5c78f9bc29 | ||
|
|
e7c17c7b4b | ||
|
|
7a2497f168 | ||
|
|
5172f6f253 | ||
|
|
9017472fa4 | ||
|
|
6659c410c6 | ||
|
|
8c7b66a2f5 | ||
|
|
8f57d78200 | ||
|
|
ad2ee9efa8 | ||
|
|
192964f65a | ||
|
|
672f30af35 | ||
|
|
a7a17298f2 | ||
|
|
e17ad8e4a1 | ||
|
|
2a544676e6 | ||
|
|
6abfa9581d | ||
|
|
a6de548e5d | ||
|
|
9dc3e579b5 | ||
|
|
c5be7d7f73 | ||
|
|
70c0da703b | ||
|
|
1cbe81fba6 | ||
|
|
420e356f34 | ||
|
|
0e0ff0cb80 | ||
|
|
9003d05ae2 | ||
|
|
c247225cac | ||
|
|
4bccaa8ecf | ||
|
|
b579fec7ab | ||
|
|
37e4525c98 | ||
|
|
2269537848 | ||
|
|
441fb91222 | ||
|
|
89e0473019 | ||
|
|
3e13b35c84 | ||
|
|
fd4910fc36 | ||
|
|
964685770f | ||
|
|
634d64b5a1 | ||
|
|
19ec1c448f | ||
|
|
e3532d6f02 | ||
|
|
aaf0aabb55 | ||
|
|
575a5936ca | ||
|
|
391d265353 | ||
|
|
f90d19821c | ||
|
|
b79babf57e | ||
|
|
a929bb0022 | ||
|
|
975eb97e27 | ||
|
|
49800cf0ed | ||
|
|
de18609e00 | ||
|
|
9911446bf9 | ||
|
|
a9223211ab | ||
|
|
54c4eeba03 | ||
|
|
a30ee3e6ff | ||
|
|
1fa19489a3 | ||
|
|
f6298dfe89 | ||
|
|
79802a53f6 | ||
|
|
54bf43fd6b | ||
|
|
bb2b2f668b | ||
|
|
ecca27e012 | ||
|
|
fe0699ca48 | ||
|
|
73b2f0921f | ||
|
|
c56230c3af | ||
|
|
919b62822d | ||
|
|
21b0c9a08d | ||
|
|
63fafd05b3 | ||
|
|
0a2493a953 | ||
|
|
87ab750714 | ||
|
|
5cf3eca9eb | ||
|
|
b4a079b65f | ||
|
|
7f2d501edf | ||
|
|
c981f94092 | ||
|
|
afba46b8b0 | ||
|
|
4e416df3c8 | ||
|
|
1b54e52351 | ||
|
|
5e1568a148 | ||
|
|
8f0ac56cf8 | ||
|
|
37f0f9d4a4 | ||
|
|
a48f75c704 | ||
|
|
58a683e3c9 | ||
|
|
907637b128 | ||
|
|
85f3e97a44 | ||
|
|
beae9691fd | ||
|
|
b7876d54cc | ||
|
|
5b24223cb5 | ||
|
|
88cadff9ef | ||
|
|
b92b39e7eb | ||
|
|
8d29a29591 | ||
|
|
daf516bf9c | ||
|
|
115d9857af | ||
|
|
b322146e9e | ||
|
|
b6e3c9da82 | ||
|
|
6abdc632dc | ||
|
|
335ca6d5ac | ||
|
|
8752426694 | ||
|
|
5357ba5900 | ||
|
|
f2686096bd | ||
|
|
c4ef14ea3c | ||
|
|
03668bd6af | ||
|
|
2d90a63ca7 | ||
|
|
11e6e37331 | ||
|
|
b229a2d59e | ||
|
|
c61af9316f | ||
|
|
02e3040e1b | ||
|
|
60bbe29435 | ||
|
|
3274ea08aa | ||
|
|
97a61dea32 | ||
|
|
6cccf20b03 | ||
|
|
0a7e17ed00 | ||
|
|
706f75c0eb | ||
|
|
022e87c4bb | ||
|
|
8b29ac7e47 | ||
|
|
4d5b8baf6f | ||
|
|
e199e5a08c | ||
|
|
6cc2351bf7 | ||
|
|
c391dbd3c8 | ||
|
|
709d980b67 | ||
|
|
0903855d5c | ||
|
|
6ad2505bf8 | ||
|
|
5db8756639 | ||
|
|
5d176408a2 | ||
|
|
ab0b569798 | ||
|
|
ee76d49e56 | ||
|
|
c75d2dcae2 | ||
|
|
7c411d36db | ||
|
|
ca767cf576 | ||
|
|
b57e0bb97e | ||
|
|
5f059253a4 | ||
|
|
e0f8443653 | ||
|
|
4c23a4bbf3 | ||
|
|
0c1486bbce | ||
|
|
6f41c9a331 | ||
|
|
2879dd29d6 | ||
|
|
42e1ef45b4 | ||
|
|
19493fdf0c | ||
|
|
5870ad0673 | ||
|
|
cbd2850d1b | ||
|
|
e7c36c104a | ||
|
|
960840d9ba | ||
|
|
b8ac1bc9d4 | ||
|
|
89edf9a8f6 | ||
|
|
eb8db0b311 | ||
|
|
b10cf8b78a | ||
|
|
1dd5c0d6d0 | ||
|
|
a1e00d23a4 | ||
|
|
1a9d38dd0e | ||
|
|
2f3e7d105d | ||
|
|
a72b3c32b1 | ||
|
|
a479b16ae2 | ||
|
|
a9e7b6f5b3 | ||
|
|
60605d7d00 | ||
|
|
9dc7f726e9 | ||
|
|
46134032d6 | ||
|
|
9847c8f351 | ||
|
|
7b506586cd | ||
|
|
40bbd422b7 | ||
|
|
285fd69ab4 | ||
|
|
b436e19bbb | ||
|
|
bf6e079289 | ||
|
|
b623866b6e | ||
|
|
df7046723c | ||
|
|
3cd3047790 | ||
|
|
abd60612c5 | ||
|
|
02292acee1 | ||
|
|
c923899898 | ||
|
|
fcdd2ad036 | ||
|
|
02d3af0ac1 | ||
|
|
0516bccece | ||
|
|
2d6389d54d | ||
|
|
60d5a117b5 | ||
|
|
9b7e2e35f5 | ||
|
|
ddfb383f23 | ||
|
|
a1707486f4 | ||
|
|
451101ec67 | ||
|
|
83c583b7e6 | ||
|
|
33cb3d3c97 | ||
|
|
3d98f345c1 | ||
|
|
de8ce5c110 | ||
|
|
fd9ed77316 | ||
|
|
d974b73cce | ||
|
|
da8a67fcad | ||
|
|
471bc60ed7 | ||
|
|
fef46be34c | ||
|
|
9a92dbdedb | ||
|
|
d6be0a4154 | ||
|
|
d3ae372903 | ||
|
|
7621ef1a13 | ||
|
|
148fdd0590 | ||
|
|
232256310a | ||
|
|
5e938791ef | ||
|
|
08405dd9b4 | ||
|
|
512c637ea3 | ||
|
|
909ebd72a1 | ||
|
|
123136e90e | ||
|
|
299d25af27 | ||
|
|
9eee2f6444 | ||
|
|
2afb10b73b | ||
|
|
1b016e5915 | ||
|
|
1c403e1748 | ||
|
|
a372a133ca | ||
|
|
08a7df504b | ||
|
|
39f9d9a86d | ||
|
|
bdd5af65ce | ||
|
|
92d7d2ab91 | ||
|
|
8d4d7ce449 | ||
|
|
d2d650cace | ||
|
|
772cefd700 | ||
|
|
e20350160b | ||
|
|
08f4e91b27 | ||
|
|
021237bc38 | ||
|
|
78c36db2f9 | ||
|
|
7fd2562cb5 | ||
|
|
15a3be2e66 | ||
|
|
9b6a540ec6 | ||
|
|
2b51085bc2 | ||
|
|
822134070b | ||
|
|
26cd874779 | ||
|
|
9f21b68541 | ||
|
|
e49d95663b | ||
|
|
2c70392ada | ||
|
|
1b2248b1e7 | ||
|
|
a322dc6353 | ||
|
|
7c78ae47c6 | ||
|
|
a14dec1b7e | ||
|
|
53b5862697 | ||
|
|
83d379c7b5 | ||
|
|
be33db8339 | ||
|
|
97e9924a0b | ||
|
|
7daab55639 | ||
|
|
9d70b7469a | ||
|
|
b5504902c4 | ||
|
|
f5e82ccd75 | ||
|
|
f4c4c21a10 | ||
|
|
661d2440f2 | ||
|
|
f4af8097f6 | ||
|
|
e1822905e7 | ||
|
|
e5154dad5b | ||
|
|
30f8932039 | ||
|
|
c4f0792c77 | ||
|
|
79c7f13ff9 | ||
|
|
dbeeb0c69b | ||
|
|
58c53ff5e2 | ||
|
|
87c441887a | ||
|
|
8ee4dab239 | ||
|
|
e01ebf8d8e | ||
|
|
e92bcd378c | ||
|
|
80156e73d1 | ||
|
|
97803cd860 | ||
|
|
9a5feee095 | ||
|
|
84410056bd | ||
|
|
acd0e41703 | ||
|
|
c7dfb9dca7 | ||
|
|
0efd82bd59 | ||
|
|
426ceff451 | ||
|
|
f2be7ed34c | ||
|
|
5dcc783b95 | ||
|
|
d1c641e934 | ||
|
|
59be63785d | ||
|
|
0bf85ec729 | ||
|
|
b54c2dc254 | ||
|
|
021fcd0641 | ||
|
|
f2dadae7a3 | ||
|
|
7ce7df2625 | ||
|
|
09fcd384ab | ||
|
|
5f8625a384 | ||
|
|
621011af7a | ||
|
|
a0b81941d1 | ||
|
|
e735335773 | ||
|
|
7b62572a56 | ||
|
|
0f921c926b | ||
|
|
4d8a4d23c0 | ||
|
|
9d7eaa46fd | ||
|
|
cc9664f7d6 | ||
|
|
573b3e9d1c | ||
|
|
9808694d89 | ||
|
|
d70d8f5b6e | ||
|
|
b75c2d80bf | ||
|
|
aa747ff651 | ||
|
|
bcbf5daf0d | ||
|
|
aee1c05a45 | ||
|
|
0bb96a8dd3 | ||
|
|
1e1d2c7b63 | ||
|
|
f12375cddc | ||
|
|
ed26e6611a | ||
|
|
98f77eca07 | ||
|
|
1b2b560f8f | ||
|
|
b49cc0c9bd | ||
|
|
4ba58ea861 | ||
|
|
3f52a20c90 | ||
|
|
580089d06e | ||
|
|
1397ab0fa6 | ||
|
|
87400793eb | ||
|
|
45f79d95b1 | ||
|
|
c8a4a61028 | ||
|
|
44091981b2 | ||
|
|
d3352643fc | ||
|
|
114c7fb38a | ||
|
|
dc7da708dc | ||
|
|
72e56aa1c7 | ||
|
|
99ceab07ad | ||
|
|
c0f6c072ce | ||
|
|
e039124f6c | ||
|
|
c96e4a4c7a | ||
|
|
622a08acf3 | ||
|
|
f44b6bf0d0 | ||
|
|
a6d75e15ea | ||
|
|
a02f03c4cb | ||
|
|
d48f5132fb | ||
|
|
ed4ac24efa | ||
|
|
9f3b8a7c2c | ||
|
|
612d4bb1f5 | ||
|
|
b58a50d246 | ||
|
|
af83811d57 | ||
|
|
66835fe6ab | ||
|
|
34cc1d33c6 | ||
|
|
a42d14e3b8 | ||
|
|
87aa165edf | ||
|
|
d217d62007 | ||
|
|
27bcc0d40a | ||
|
|
e1df075cde | ||
|
|
8358692e8d | ||
|
|
e1fae01dab | ||
|
|
d206ab140a | ||
|
|
9d8722ab17 | ||
|
|
c4fa40c403 | ||
|
|
1d0b06bfbe | ||
|
|
2cdf473dcb | ||
|
|
1af9e2c2da | ||
|
|
9a1815fa1e | ||
|
|
f601961c49 | ||
|
|
406acd34c5 | ||
|
|
31cdcbbc25 | ||
|
|
2215ce71c9 | ||
|
|
1872fbb1c8 | ||
|
|
d99f912ac2 | ||
|
|
00a76fb648 | ||
|
|
187e951a47 | ||
|
|
c0b9b27aae | ||
|
|
b76bb17396 | ||
|
|
2efa6d3623 | ||
|
|
3972ce633d | ||
|
|
0dc7901393 | ||
|
|
a25ba6eaa5 | ||
|
|
4ea48dfe57 | ||
|
|
8f7ad8b2ee | ||
|
|
3b9800df07 | ||
|
|
4c80d3234e | ||
|
|
9e4af1d66b | ||
|
|
73555df2ea | ||
|
|
3ca78604fd | ||
|
|
0138046923 | ||
|
|
2129184209 | ||
|
|
dd2116f8a6 | ||
|
|
814c2d9149 | ||
|
|
b3f7276044 | ||
|
|
ad88aa980b | ||
|
|
aca55e9203 | ||
|
|
cc3330bb27 | ||
|
|
1370909db7 | ||
|
|
08cc0c394b | ||
|
|
cb6692aea3 | ||
|
|
b3badb3a55 | ||
|
|
cbf73901d3 | ||
|
|
4822e45d58 | ||
|
|
1d930d36bf | ||
|
|
9effcc403d | ||
|
|
05dcc039bd | ||
|
|
cb08656abc | ||
|
|
69b22fc736 | ||
|
|
bf857f6ce7 | ||
|
|
7ebf2378b3 | ||
|
|
aec25dab37 | ||
|
|
e11969780d | ||
|
|
02c98b1547 | ||
|
|
c864589478 |
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -10,7 +10,7 @@ assignees: mnadareski
|
||||
**Before You Submit**
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
|
||||
- .NET Core 3.1 and .NET 5.0 have known limitations, so make sure that what you're asking for isn't already in another build.
|
||||
- .NET 6.0 has known limitations, so make sure that what you're asking for isn't already in another build.
|
||||
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
|
||||
|
||||
If none of those apply, then continue...
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/informational.md
vendored
2
.github/ISSUE_TEMPLATE/informational.md
vendored
@@ -10,7 +10,7 @@ assignees: mnadareski
|
||||
**Before You Submit**
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
|
||||
- .NET Core 3.1 and .NET 5.0 have known limitations, so make sure that what you're giving information on isn't already in another build.
|
||||
- .NET 6.0 has known limitations, so make sure that what you're giving information on isn't already in another build.
|
||||
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
|
||||
|
||||
If none of those apply, then continue...
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/issue-report.md
vendored
5
.github/ISSUE_TEMPLATE/issue-report.md
vendored
@@ -10,7 +10,7 @@ assignees: mnadareski
|
||||
**Before You Submit**
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the issue has already been addressed.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
|
||||
- .NET Core 3.1 and .NET 5.0 have known issues, please try using another build to reproduce the error
|
||||
- .NET 6.0 has known issues, please try using another build to reproduce the error
|
||||
- Check multiple discs to help narrow down the issue
|
||||
- Check the Options to see if changing any of those affects your issue.
|
||||
|
||||
@@ -25,9 +25,8 @@ What version are you using?
|
||||
**Build**
|
||||
What runtime version are you using?
|
||||
|
||||
- [ ] .NET Framework 4.7.2 running on (Operating System)
|
||||
- [ ] .NET Framework 4.8 running on (Operating System)
|
||||
- [ ] .NET Core 3.1 running on (Operating System)
|
||||
- [ ] .NET 6.0 running on (Operating System)
|
||||
|
||||
**Describe the issue**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "MPF.Library/Aaru/CICMMetadata"]
|
||||
path = CICMMetadata
|
||||
url = https://github.com/claunia/CICMMetadata
|
||||
2
.vscode/launch.json
vendored
2
.vscode/launch.json
vendored
@@ -10,7 +10,7 @@
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/MPF/bin/Debug/netcoreapp3.1/MPF.dll",
|
||||
"program": "${workspaceFolder}/MPF/bin/Debug/net6.0-windows/MPF.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/MPF",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"omnisharp.projectLoadTimeout": 480
|
||||
}
|
||||
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
@@ -7,7 +7,7 @@
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/MPF/MPF.csproj",
|
||||
"${workspaceFolder}/MPF.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
|
||||
373
CHANGELIST.md
373
CHANGELIST.md
@@ -1,4 +1,372 @@
|
||||
### WIP (xxxx-xx-xx)
|
||||
|
||||
### 2.5 (2023-03-12)
|
||||
|
||||
- Add _drive file to zip for UIC
|
||||
- Add Xbox Series X and PS5 to list, fix Acorn
|
||||
- Add Xbox Series X short name to list
|
||||
- Remove windows from test target
|
||||
- Remove .NET 6.0 from tests, add TODO
|
||||
- Add .NET 6.0 to tests, remove msbuild args
|
||||
- Add HD-DVD to speed definitions
|
||||
- Add PS3 internal serial and version parsing (tjanas)
|
||||
- Update Nuget packages to newest stable
|
||||
- Simplify path selection in UI
|
||||
- Fix broken normalization test
|
||||
- Update drive info before dumping
|
||||
- Update redumper strings
|
||||
- Add new parameter and mode validation
|
||||
- Add redumper to the UI
|
||||
- Update redumper to build 81
|
||||
- Fix incorrect naming in Options window
|
||||
- Initial attempt at parsing redumper outputs
|
||||
- Update README
|
||||
- Reenable write offset for all CDs
|
||||
- Add missing redumper output file
|
||||
- Force a filename for redumper
|
||||
- Fix incorrect SetParameters
|
||||
- Fix a couple redumper things
|
||||
- Update AppVeyor version
|
||||
- Output security sectors to info
|
||||
- Remove x86 requirement for build
|
||||
- Fix XGD media type outputs
|
||||
- Fix typo in ToInternalProgram
|
||||
- Fix redumper error count parsing
|
||||
- Update to Aaru v5.3.2 LTS
|
||||
- Update options loader with sane defaults
|
||||
- Fix AppVeyor pathing
|
||||
- Skip during detection
|
||||
- Address some UI concerns
|
||||
- Tweak AppVeyor, show Check 6.0 builds
|
||||
- More strict when custom parameters editing
|
||||
- Use msbuild for .NET Framework 4.8
|
||||
- Update nuget packages
|
||||
- ReadAllText not ReadAllLines
|
||||
- Add drive format (fs) to log
|
||||
- Go back to pre .NET 7 Aaru
|
||||
- Be smarter about old paths
|
||||
- Fix relative paths for DIC
|
||||
- Add nicer failure message
|
||||
- Update to DIC 20230201
|
||||
- Use relative path output for DIC
|
||||
- Remove usage of Aaru
|
||||
- Remove Aaru as submodule
|
||||
- Fix Aaru removal
|
||||
- Enable .NET 6 Windows builds
|
||||
- Update Redumper to build_106
|
||||
- Reformat CICM for old .NET versions
|
||||
- Attempt to handle no drives
|
||||
- Handle no drives better
|
||||
- Handle no drives betterer
|
||||
- Packaging requires `-windows` for framework
|
||||
- Fix other `-windows` places for .NET 6
|
||||
- Fix typo in DICMultiSectorReadValue
|
||||
- Semi-unify drive finding
|
||||
- Introduce cross-platform MMI
|
||||
- Remove System.Management
|
||||
- Add Redumper Universal Hash support
|
||||
- Fix Redumper write offset support
|
||||
- Add Redumper non-zero data start
|
||||
- Use media size for type detection on .NET 6
|
||||
- Trim PIC for PS3
|
||||
- Get the version of redumper (fuzz6001)
|
||||
- Update VSCode config files
|
||||
- Can't publish single file for UI
|
||||
- Handle missing extension gracefully
|
||||
- Fix incorrect option slider display
|
||||
- Handle undetected discs on refresh
|
||||
- Originally intended behaviour of the Update Label button (IcySon55)
|
||||
- Update to BurnOutSharp 2.7.0
|
||||
- Get error count from recent redumper log (fuzz6001)
|
||||
- Minor cosmetic changes
|
||||
- Set saner defaults for dumping speeds
|
||||
- Attempt to support Windows 7
|
||||
- Add `win7-x64` to identifier list
|
||||
- Remove unsupported identifiers
|
||||
- Add identifiers in more places
|
||||
- Move drive finding inside of the try/catch
|
||||
- Update to DIC 20230309
|
||||
- Fix errant forward slashes
|
||||
- Add TOC back as optional file
|
||||
- Fix Redumper path generation
|
||||
- Use and trim quotes for Redumper
|
||||
- Fix incorrect image name setting
|
||||
- Ensure Redumper parameters are set
|
||||
- Ensure min values are taken care of
|
||||
- Update parameters with `=` handling
|
||||
- Ensure drive and speed are set
|
||||
- Handle quotes embedded
|
||||
- Return list of missing files for Redumper
|
||||
- Readd accidentally deleted line
|
||||
- Detect EOF during the search for error counts (fuzz6001)
|
||||
|
||||
### 2.4 (2022-10-26)
|
||||
|
||||
- Update to DIC 20211001
|
||||
- Fix Redump disc title pulling
|
||||
- Add /mr default flag options
|
||||
- Make FillFromRedump private again
|
||||
- Fix DVD layer finding corner case
|
||||
- Fix failing module tests
|
||||
- Add option to limit region and language selections
|
||||
- Cap X360 directory check to 500MB
|
||||
- Assign normalized path to parameters
|
||||
- Move path normalization to better place
|
||||
- Specifically include Unsafe Nuget package
|
||||
- Update Nuget packages to newest stable
|
||||
- Add Xbox One system detection
|
||||
- Update to DIC 20220301
|
||||
- Force internal drive refresh
|
||||
- Add single drive refresh button (IcySon55)
|
||||
- Fix "missing" option in window
|
||||
- Track last used drive on refresh
|
||||
- Clean up pre-dump validation
|
||||
- Update to Aaru v5.3.1 LTS
|
||||
- Add upper case `<TAB>` processing
|
||||
- Reorder event handers
|
||||
- Handle sanitized protections edge case
|
||||
- Update Aaru Nuget package
|
||||
- Return faster on empty protection sets
|
||||
- Remove redundant check around volume label
|
||||
- Fix tabs in Games and Videos boxes
|
||||
- Make fully and partially matching IDs more apparent
|
||||
- Add write offset as read-only field
|
||||
- Explicitly clear list, just in case
|
||||
- Add both fully and partially matching to info file
|
||||
- Fix submission info clone
|
||||
- Clear out fully matched IDs from the partial list
|
||||
- Refine copy protection section showing
|
||||
- Update Nuget packages
|
||||
- Normalize newlines in comments and contents
|
||||
- Increase JSON accuracy for disc types
|
||||
- Further separate out protection scan outputs
|
||||
- Compress JSON for artifacts alone
|
||||
- Even stricter output for copy protection section
|
||||
- Report dictionary to InfoTool
|
||||
- Even even stricter copy protection output
|
||||
- Disable PVD creation for Aaru
|
||||
- Explicitly sanitize '?' from path
|
||||
- Combine cases in protection scan
|
||||
- Convert status label to TextBlock
|
||||
- Add option for copy protection file output
|
||||
- Make protection field user-editable
|
||||
- Add warning to tooltip
|
||||
- Create core UI library
|
||||
- Rename MPF.GUI to MPF.UI
|
||||
- Add multisession pseudo-tag
|
||||
- Add multisession helper method skeleton
|
||||
- Move and update options loader; clean up Check
|
||||
- Move helper methods around
|
||||
- Consolidate Redump login testing
|
||||
- Separate common arguments to new helper
|
||||
- Parse and format CD multisession data
|
||||
- Convert triple space to tab
|
||||
- Fix clone issue with copy protection
|
||||
- Be more picky about multisession
|
||||
- Sanitize whitespace around tabs
|
||||
- Convert internal libraries to .NET Standard 2.0
|
||||
- Update AppVeyor to VS2022
|
||||
- Update Nuget packages
|
||||
- Remove needless csproj constants
|
||||
- Update copyright date to 2022
|
||||
- Revert AppVeyor to VS2019 for now
|
||||
- Add size-based media type detection for non-Framework
|
||||
- Gate ManagmentObject use further
|
||||
- Use built-in NETFRAMEWORK directive
|
||||
- Update to BurnOutSharp 2.1.0
|
||||
- .NET 6.0 and Cleanup
|
||||
- Remove .NET Core 3.1 from test project for now
|
||||
- Remove .NET Core 3.1 entirely
|
||||
- Add filesystem logging for .NET 6
|
||||
- Avoid whitespace changes for PVD, Header, and Cuesheet
|
||||
- Sync to newest CICM
|
||||
- Update solution file for VS2022
|
||||
- Add Aaru as a submodule for .NET 6
|
||||
- Multisession is multi-line
|
||||
- Use Aaru for media type retrival (.NET 6)
|
||||
- Fix CD-R multisession info
|
||||
- Better get drive list (.NET 6)
|
||||
- Add optical media support method (.NET 6)
|
||||
- Simplify IsOptical (.NET 6)
|
||||
- Reorganize GetMediaType
|
||||
- Possibly fix tab regex replacement
|
||||
- Add PIC.bin to log zip
|
||||
- Organize projects in solution
|
||||
- Implement Drive.Create for safety
|
||||
- Fix incomplete system name detection
|
||||
- Add Sharp X68k detection
|
||||
- Add Bandai Playdia QIS detection
|
||||
- Framework for XBONE filenames
|
||||
- Add files for XBONE
|
||||
- Update to DIC 20220707
|
||||
- Fix .bin file paths; update internal filename generation
|
||||
- Disable nonstandard BD-ROM sizes
|
||||
- Trim leading file paths for XBONE
|
||||
- Give .NET 6 priority for web calls
|
||||
- Update to BurnOutSharp 2.3.0 (nw)
|
||||
- Add Mattel Fisher-Price iXL detection
|
||||
- Update to BurnOutSharp 2.3.1 (nw)
|
||||
- Fix serial parsing for Dreamcast
|
||||
- Fix missing parenthesis
|
||||
- Add internal name for Cleanrip outputs
|
||||
- Fix psxt001z namespace
|
||||
- Fix missing assignment
|
||||
- Update to new constructor
|
||||
- Update PSX content check
|
||||
- Update Aaru submodule to latest devel
|
||||
- Add Sony Electronic Book system
|
||||
- Verify GD-ROM outputs, finally
|
||||
- Electronic not Electric
|
||||
- Fix ringcode guide images
|
||||
- Add skeleton for Redumper
|
||||
- Download Redumper with AppVeyor
|
||||
- Add Redumper constants
|
||||
- Add Redumper parameter values
|
||||
- Add Redumper command support and reset
|
||||
- Add Redumper dumping command
|
||||
- Add Redumper command validation
|
||||
- Add Redumper command generation
|
||||
- Create Redumper extensions class
|
||||
- Minor Redumper cleanup
|
||||
- Add important Redumper note
|
||||
- Update to DIC 20220909
|
||||
- Possibly fix PIC parsing
|
||||
- Add PS3 folder/file checks for detection
|
||||
- Use default directory for folder browsing, if possible
|
||||
- Add unused command filename parser
|
||||
- Add initial framework for reporting dumping program
|
||||
- Report specific DIC version, if possible
|
||||
- Add dumping info section skeleton
|
||||
- Update Aaru submodules
|
||||
- Fix missing info reference change
|
||||
- Enable separated protection info output by default
|
||||
- Try adding MPF logs to zip
|
||||
- Create MPF log helper and filter for deletion
|
||||
- Fix missing info in Aaru
|
||||
- Add hardware info to DIC and Aaru
|
||||
- Fix hardware info
|
||||
- Add specialized CDS/SafeDisc filter
|
||||
- Add unused article formatter
|
||||
- Add language filtering to formatter
|
||||
- Add multi-language helper for filter
|
||||
- Update Nuget packages
|
||||
- Remove deprecated protection setting
|
||||
- Update BurnOutSharp to 2.3.4
|
||||
- Add compression result reason to log
|
||||
- Add System.Memory package to MPF.Library
|
||||
- Add CodePages package to MPF.Library
|
||||
- Remove extraneous packages
|
||||
- Change location of dumping info
|
||||
- Add MS ZipFile package to MPF.Library
|
||||
- Add + to positive offsets
|
||||
- Disable layerbreak generation for BD
|
||||
- Disable XGD version reporting
|
||||
- Disable XGD layerbreak reporting
|
||||
- Disable XGD1 PVD reporting
|
||||
- Put Redump limitations behind existing flag
|
||||
- Add framework for reported disc type
|
||||
- Add disc type parsing for Aaru and DIC
|
||||
- Fix multiple DiscType for DIC
|
||||
- Fix multiple DiscType* for DIC
|
||||
- Add logging to !submissionInfo writing failure
|
||||
- Add logging to !submissionInfo formatting failure
|
||||
- Update issue templates to be more accurate
|
||||
- Fix NRE with offsets
|
||||
- Fix readonly Filename info display
|
||||
- Fix layerbreak-based checks
|
||||
- Add PS4 serial finding (tjanas)
|
||||
- Add unused notification method
|
||||
- Move to unused Chime class
|
||||
- Add PS5 serial finding (tjanas)
|
||||
- Fix offset formatting (fuzz6001)
|
||||
|
||||
### 2.3 (2022-02-05)
|
||||
|
||||
- Start overhauling Redump information pulling, again
|
||||
- Add internal structure for special site codes
|
||||
- Add new tabs for special site information
|
||||
- Clean up default handling of fields
|
||||
- Try to handle multi-line fields during parsing
|
||||
- Fix CSSKey log handling
|
||||
- Show most read-only fields in new tab
|
||||
- Add horizontal scroll to user input
|
||||
- Tweak new disc information fields and tabs
|
||||
- Allow internal serial and volume label to be hidden
|
||||
- Be smarter about showing update checks
|
||||
- Ensure all fields are read-only on read-only tab
|
||||
- Add model for 2-layer ringcode guide
|
||||
- Add first attempt 2-layer ringcode guide
|
||||
- Add even more safety to clone
|
||||
- Sanitize filename after check
|
||||
- Handle pulled linebreaks better
|
||||
- Skip anti-modchip string in some cases
|
||||
- Handle pulled linebreaks better, again
|
||||
- Tweak more Disc Info window formatting
|
||||
- Skip unnecessary newlines in parsing
|
||||
- Force scroll visibility, tweak text sizes again
|
||||
- Fix newline skipping
|
||||
- Add missing continue statement
|
||||
- Add newlines for mutliline special fields
|
||||
- Omit volume label for "Audio CD"
|
||||
- Remove Enter/Escape registration on disc info window
|
||||
- Logically group more things in disc info window
|
||||
- Remove tab key from disc info window
|
||||
- Add `<tab>` processing
|
||||
- Unban newly opened consoles
|
||||
- Tweak minimalized layout a bit more
|
||||
- Add tab setting
|
||||
- Further disc info window tweaks
|
||||
- Changed IsEnabled to IsReadOnly
|
||||
- Fix scrolling issues in disc info window
|
||||
- Convert postgap and VCD fields to checkboxes
|
||||
- Adjust width ratios for disc info window
|
||||
- Fix IsReadOnly
|
||||
- Only include booleans if the value is true
|
||||
- Add hidden debug option for "ShowDebugViewMenuItem"
|
||||
- Fix incorrect header check
|
||||
- Make protection read-only field multiline
|
||||
- Reformat Saturn internal date
|
||||
- Fix Sega CD internal serial reading
|
||||
- Fix crash on invalid parameters
|
||||
- Differentiate XMID and XeMID
|
||||
- Conditionally pull region from Redump
|
||||
- Be smarter about volume labels
|
||||
- Use volume label in checks, not formatted version
|
||||
- Sync with Redump region and language selection
|
||||
- Try to delete old log archive before writing
|
||||
- Add support for all ISO language codes
|
||||
- Add support for all ISO region codes
|
||||
- Ensure ordering in output site tags
|
||||
- Make site code formatting helper method
|
||||
- Better helper method organization
|
||||
- Start supporting ordered tags and non-tags
|
||||
- Add more non-tag support; rearrange info window
|
||||
- Fix incorrect language three-letter code
|
||||
- Hook up additional Xbox field to disc info window
|
||||
- Fix non-tag tag shortnames
|
||||
- Fix parsing of non-tag tags again
|
||||
- Disable unnecessary cuesheet parsing
|
||||
- Fix incorrect region two-letter code
|
||||
- Adjust long names for some languages
|
||||
- Add Sierra ID to list of pseudo-tags
|
||||
- Add another hand-formatted version of SS tag
|
||||
- Move internal serial before volume label
|
||||
- Check for $SystemUpdate folder for X360 discs
|
||||
- Slightly rename UK and USA regions for UI
|
||||
- Add another hand-formatted version of SS tag
|
||||
- Add verification reminders for pulled tags
|
||||
- Read longer string for Saturn internal serial
|
||||
- Ensure version only pulled if one doesn't exist
|
||||
- Ensure Games pseudo-tag is multi-line
|
||||
- Add alternate pseudo-tag for Playable Demos
|
||||
- Make error clearer if something is unsupported in Check
|
||||
- Ensure drive is not null for volume labels
|
||||
- Check explicitly for no matches on Redump pull
|
||||
- Normalize PS1/PS2 executable names
|
||||
- Adjust paths for DIC just before dumping
|
||||
|
||||
### 2.2 (2021-12-30)
|
||||
|
||||
- Fix Saturn header finding
|
||||
- Add Pocket PC support
|
||||
- Add HD-DVD-Video support
|
||||
@@ -50,6 +418,7 @@
|
||||
- Add safety around volume labels
|
||||
|
||||
### 2.1 (2021-07-22)
|
||||
|
||||
- Enum, no more
|
||||
- Sony works backward
|
||||
- Add experimental dark mode
|
||||
@@ -79,6 +448,7 @@
|
||||
- Update to BurnOutSharp 1.7.0
|
||||
|
||||
### 2.0 (2021-04-23)
|
||||
|
||||
- Rename DICUI to Media Preservation Frontend (MPF)
|
||||
- Add handling for BEh drive _mainInfo.txt changes
|
||||
- Fix multiline regex fields during info pulling
|
||||
@@ -133,6 +503,7 @@
|
||||
- Update to BurnOutSharp 1.6.1
|
||||
|
||||
### 1.18 (2020-11-10)
|
||||
|
||||
- Add more information extraction and generation for Aaru
|
||||
- Remove instances of CD Check from copy protection (again, sorry)
|
||||
- Fix multiline submission info outputs
|
||||
@@ -157,6 +528,7 @@
|
||||
- Added HD-DVD-Video detection
|
||||
|
||||
### 1.17.1 (2020-09-14)
|
||||
|
||||
- Shuffled some shared, internal UI variables
|
||||
- Synced WPF and Avalonia UI internals
|
||||
- Made the disc information window less prone to bugs
|
||||
@@ -164,6 +536,7 @@
|
||||
- Added support for old(?) DIC flags: `/fix` and `/re`
|
||||
|
||||
### 1.17 (2020-09-12)
|
||||
|
||||
- Updated to Aaru version 5.1
|
||||
- Updated to BurnOutSharp version 1.4.0
|
||||
- Updated to DIC version 20200716
|
||||
|
||||
Submodule CICMMetadata deleted from 7944bca8e6
@@ -1,16 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Title>MPF Check</Title>
|
||||
<AssemblyName>MPF.Check</AssemblyName>
|
||||
<Description>Validator for various dumping programs</Description>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.2</Version>
|
||||
<Version>2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
@@ -28,10 +28,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.0" GeneratePathProperty="true">
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.7.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -42,10 +42,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj">
|
||||
<Project>{51ab0928-13f9-44bf-a407-b6957a43a056}</Project>
|
||||
<Name>MPF.Library</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BurnOutSharp;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
@@ -15,126 +14,17 @@ namespace MPF.Check
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Help options
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
|
||||
{
|
||||
DisplayHelp();
|
||||
// Try processing the standalone arguments
|
||||
if (ProcessStandaloneArguments(args))
|
||||
return;
|
||||
}
|
||||
|
||||
// List options
|
||||
if (args[0] == "-lm" || args[0] == "--listmedia")
|
||||
{
|
||||
ListMediaTypes();
|
||||
Console.ReadLine();
|
||||
// Try processing the common arguments
|
||||
(bool success, MediaType mediaType, RedumpSystem? knownSystem) = ProcessCommonArguments(args);
|
||||
if (!success)
|
||||
return;
|
||||
}
|
||||
else if (args[0] == "-lp" || args[0] == "--listprograms")
|
||||
{
|
||||
ListPrograms();
|
||||
Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
else if (args[0] == "-ls" || args[0] == "--listsystems")
|
||||
{
|
||||
ListSystems();
|
||||
Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal operation check
|
||||
if (args.Length < 3)
|
||||
{
|
||||
DisplayHelp("Invalid number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the MediaType
|
||||
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
|
||||
if (mediaType == MediaType.NONE)
|
||||
{
|
||||
DisplayHelp($"{args[0]} is not a recognized media type");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the RedumpSystem
|
||||
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
|
||||
if (knownSystem == null)
|
||||
{
|
||||
DisplayHelp($"{args[1]} is not a recognized system");
|
||||
return;
|
||||
}
|
||||
|
||||
// Default values
|
||||
string username = null, password = null;
|
||||
string internalProgram = "DiscImageCreator";
|
||||
string path = string.Empty;
|
||||
bool scan = false, compress = false, json = false;
|
||||
|
||||
// Loop through and process options
|
||||
int startIndex = 2;
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
{
|
||||
// Redump login
|
||||
if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
{
|
||||
string[] credentials = args[startIndex].Split('=')[1].Split(';');
|
||||
username = credentials[0];
|
||||
password = credentials[1];
|
||||
}
|
||||
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
|
||||
{
|
||||
username = args[startIndex + 1];
|
||||
password = args[startIndex + 2];
|
||||
startIndex += 2;
|
||||
}
|
||||
|
||||
// Use specific program
|
||||
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
internalProgram = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
internalProgram = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Use a device path for physical checks
|
||||
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
|
||||
{
|
||||
path = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
|
||||
{
|
||||
path = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Scan for protection (requires device path)
|
||||
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
|
||||
{
|
||||
scan = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
|
||||
{
|
||||
json = true;
|
||||
}
|
||||
|
||||
// Compress log and extraneous files
|
||||
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
|
||||
{
|
||||
compress = true;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
(Options options, string path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
|
||||
|
||||
// Make new Progress objects
|
||||
var resultProgress = new Progress<Result>();
|
||||
@@ -142,20 +32,14 @@ namespace MPF.Check
|
||||
var protectionProgress = new Progress<ProtectionProgress>();
|
||||
protectionProgress.ProgressChanged += ProgressUpdated;
|
||||
|
||||
// If credentials are invalid, alert the user
|
||||
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
using (RedumpWebClient wc = new RedumpWebClient())
|
||||
{
|
||||
bool? loggedIn = wc.Login(username, password);
|
||||
if (loggedIn == true)
|
||||
Console.WriteLine("Redump username and password accepted!");
|
||||
else if (loggedIn == false)
|
||||
Console.WriteLine("Redump username and password denied!");
|
||||
else
|
||||
Console.WriteLine("An error occurred validating your crendentials!");
|
||||
}
|
||||
}
|
||||
// Validate the supplied credentials
|
||||
#if NET48 || NETSTANDARD2_1
|
||||
(bool? _, string message) = RedumpWebClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword);
|
||||
#else
|
||||
(bool? _, string message) = RedumpHttpClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
Console.WriteLine(message);
|
||||
|
||||
// Loop through all the rest of the args
|
||||
for (int i = startIndex; i < args.Length; i++)
|
||||
@@ -171,24 +55,11 @@ namespace MPF.Check
|
||||
string filepath = Path.GetFullPath(args[i].Trim('"'));
|
||||
|
||||
// Now populate an environment
|
||||
var options = new Options
|
||||
{
|
||||
InternalProgram = EnumConverter.ToInternalProgram(internalProgram),
|
||||
ScanForProtection = scan && !string.IsNullOrWhiteSpace(path),
|
||||
PromptForDiscInformation = false,
|
||||
ShowDiscEjectReminder = false,
|
||||
OutputSubmissionJSON = json,
|
||||
CompressLogFiles = compress,
|
||||
|
||||
RedumpUsername = username,
|
||||
RedumpPassword = password,
|
||||
};
|
||||
|
||||
Drive drive = null;
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
drive = new Drive(null, new DriveInfo(path));
|
||||
drive = Drive.Create(null, path);
|
||||
|
||||
var env = new DumpEnvironment(options, "", filepath, drive, knownSystem, mediaType, null);
|
||||
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, null);
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
@@ -214,61 +85,94 @@ namespace MPF.Check
|
||||
Console.WriteLine("-ls, --listsystems List supported system types");
|
||||
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Check Options:");
|
||||
Console.WriteLine("-c, --credentials <user> <pw> Redump username and password");
|
||||
Console.WriteLine("-u, --use <program> Dumping program output type");
|
||||
Console.WriteLine("-p, --path <drivepath> Physical drive path for additional checks");
|
||||
Console.WriteLine("-s, --scan Enable copy protection scan (requires --path)");
|
||||
Console.WriteLine("-j, --json Enable submission JSON output");
|
||||
Console.WriteLine("-z, --zip Enable log file compression");
|
||||
var supportedArguments = OptionsLoader.PrintSupportedArguments();
|
||||
foreach (string argument in supportedArguments)
|
||||
{
|
||||
Console.WriteLine(argument);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all media types with their short usable names
|
||||
/// Process common arguments for all functionality
|
||||
/// </summary>
|
||||
private static void ListMediaTypes()
|
||||
/// <returns>True if all arguments pass, false otherwise</returns>
|
||||
private static (bool, MediaType, RedumpSystem?) ProcessCommonArguments(string[] args)
|
||||
{
|
||||
Console.WriteLine("Supported Media Types:");
|
||||
foreach (var val in Enum.GetValues(typeof(MediaType)))
|
||||
// All other use requires at least 3 arguments
|
||||
if (args.Length < 3)
|
||||
{
|
||||
if (((MediaType)val) == MediaType.NONE)
|
||||
continue;
|
||||
|
||||
Console.WriteLine($"{((MediaType?)val).ShortName()} - {((MediaType?)val).LongName()}");
|
||||
DisplayHelp("Invalid number of arguments");
|
||||
return (false, MediaType.NONE, null);
|
||||
}
|
||||
|
||||
// Check the MediaType
|
||||
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
|
||||
if (mediaType == MediaType.NONE)
|
||||
{
|
||||
DisplayHelp($"{args[0]} is not a recognized media type");
|
||||
return (false, MediaType.NONE, null);
|
||||
}
|
||||
|
||||
// Check the RedumpSystem
|
||||
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
|
||||
if (knownSystem == null)
|
||||
{
|
||||
DisplayHelp($"{args[1]} is not a recognized system");
|
||||
return (false, MediaType.NONE, null);
|
||||
}
|
||||
|
||||
return (true, mediaType, knownSystem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all programs with their short usable names
|
||||
/// Process any standalone arguments for the program
|
||||
/// </summary>
|
||||
private static void ListPrograms()
|
||||
/// <returns>True if one of the arguments was processed, false otherwise</returns>
|
||||
private static bool ProcessStandaloneArguments(string[] args)
|
||||
{
|
||||
Console.WriteLine("Supported Programs:");
|
||||
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
|
||||
// Help options
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
|
||||
{
|
||||
if (((InternalProgram)val) == InternalProgram.NONE)
|
||||
continue;
|
||||
|
||||
Console.WriteLine($"{((InternalProgram?)val).LongName()}");
|
||||
DisplayHelp();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all systems with their short usable names
|
||||
/// </summary>
|
||||
private static void ListSystems()
|
||||
{
|
||||
Console.WriteLine("Supported Known Systems:");
|
||||
var knownSystems = Enum.GetValues(typeof(RedumpSystem))
|
||||
.OfType<RedumpSystem?>()
|
||||
.Where(s => s != null && !s.IsMarker() && s.GetCategory() != SystemCategory.NONE)
|
||||
.OrderBy(s => s.LongName() ?? string.Empty);
|
||||
|
||||
foreach (var val in knownSystems)
|
||||
// List options
|
||||
if (args[0] == "-lm" || args[0] == "--listmedia")
|
||||
{
|
||||
Console.WriteLine($"{val.ShortName()} - {val.LongName()}");
|
||||
Console.WriteLine("Supported Media Types:");
|
||||
foreach (string mediaType in Extensions.ListMediaTypes())
|
||||
{
|
||||
Console.WriteLine(mediaType);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-lp" || args[0] == "--listprograms")
|
||||
{
|
||||
Console.WriteLine("Supported Programs:");
|
||||
foreach (string program in EnumExtensions.ListPrograms())
|
||||
{
|
||||
Console.WriteLine(program);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-ls" || args[0] == "--listsystems")
|
||||
{
|
||||
Console.WriteLine("Supported Systems:");
|
||||
foreach (string system in Extensions.ListSystems())
|
||||
{
|
||||
Console.WriteLine(system);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#endif
|
||||
using MPF.Core.Data;
|
||||
@@ -34,7 +34,7 @@ namespace MPF.Core.Converters
|
||||
}
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Convert IMAPI physical media type to a MediaType
|
||||
/// </summary>
|
||||
@@ -135,6 +135,8 @@ namespace MPF.Core.Converters
|
||||
return "dd";
|
||||
case InternalProgram.DiscImageCreator:
|
||||
return "DiscImageCreator";
|
||||
case InternalProgram.Redumper:
|
||||
return "redumper";
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -183,6 +185,9 @@ namespace MPF.Core.Converters
|
||||
return InternalProgram.DiscImageCreator;
|
||||
case "dd":
|
||||
return InternalProgram.DD;
|
||||
case "rd":
|
||||
case "redumper":
|
||||
return InternalProgram.Redumper;
|
||||
|
||||
// Verification support only
|
||||
case "cleanrip":
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace MPF.Core.Data
|
||||
// Private lists of known drive speed ranges
|
||||
private static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
private static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
|
||||
private static IReadOnlyList<int> Unknown { get; } = new List<int> { 1 };
|
||||
|
||||
@@ -35,10 +36,11 @@ namespace MPF.Core.Data
|
||||
case MediaType.GDROM:
|
||||
return CD;
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return DVD;
|
||||
case MediaType.HDDVD:
|
||||
return HDDVD;
|
||||
case MediaType.BluRay:
|
||||
return BD;
|
||||
default:
|
||||
@@ -82,13 +84,19 @@ namespace MPF.Core.Data
|
||||
|
||||
// Automatic Information
|
||||
|
||||
public const string DumpingProgramField = "Dumping Program";
|
||||
public const string DumpingDriveManufacturer = "Manufacturer";
|
||||
public const string DumpingDriveModel = "Model";
|
||||
public const string DumpingDriveFirmware = "Firmware";
|
||||
public const string ReportedDiscType = "Reported Disc Type";
|
||||
public const string PVDField = "Primary Volume Descriptor (PVD)";
|
||||
public const string DATField = "DAT";
|
||||
public const string SizeField = "Size";
|
||||
public const string CRC32Field = "CRC32";
|
||||
public const string MD5Field = "MD5";
|
||||
public const string SHA1Field = "SHA1";
|
||||
public const string MatchingIDsField = "Matching IDs";
|
||||
public const string FullyMatchingIDField = "Fully Matching ID";
|
||||
public const string PartiallyMatchingIDsField = "Partially Matching IDs";
|
||||
public const string ErrorCountField = "Error Count";
|
||||
public const string CuesheetField = "Cuesheet";
|
||||
public const string SubIntentionField = "SubIntention Data (SecuROM/LibCrypt)";
|
||||
@@ -100,12 +108,7 @@ namespace MPF.Core.Data
|
||||
public const string PlayStationEDCField = "EDC";
|
||||
public const string PlayStationAntiModchipField = "Anti-modchip";
|
||||
public const string PlayStationLibCryptField = "LibCrypt";
|
||||
public const string XBOXDMIHash = "DMI";
|
||||
public const string XBOXPFIHash = "PFI";
|
||||
public const string XBOXSSHash = "SS";
|
||||
public const string XBOXSSRanges = "Security Sector Ranges";
|
||||
public const string XBOXSSVersion = "Security Sector Version";
|
||||
public const string XBOXXeMID = "XeMID";
|
||||
|
||||
// Default values
|
||||
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using Microsoft.Management.Infrastructure;
|
||||
using Microsoft.Management.Infrastructure.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Utilities;
|
||||
using RedumpLib.Data;
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#else
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using AaruDevices = Aaru.Devices;
|
||||
#endif
|
||||
|
||||
namespace MPF.Core.Data
|
||||
@@ -18,8 +16,14 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Represents information for a single drive
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TODO: This needs to be less Windows-centric. Devices do not always have a single letter that can be used.
|
||||
/// TODO: Can the Aaru models be used instead of the ones I've created here?
|
||||
/// </remarks>
|
||||
public class Drive
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Represents drive type
|
||||
/// </summary>
|
||||
@@ -28,41 +32,32 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Drive partition format
|
||||
/// </summary>
|
||||
public string DriveFormat => driveInfo?.DriveFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Windows drive letter
|
||||
/// </summary>
|
||||
public char Letter => driveInfo?.Name[0] ?? '\0';
|
||||
public string DriveFormat { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Windows drive path
|
||||
/// </summary>
|
||||
public string Name => driveInfo?.Name;
|
||||
public string Name { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Represents if Windows has marked the drive as active
|
||||
/// </summary>
|
||||
public bool MarkedActive => driveInfo?.IsReady ?? false;
|
||||
public bool MarkedActive { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the total size of the drive
|
||||
/// </summary>
|
||||
public long TotalSize { get; private set; } = default;
|
||||
|
||||
/// <summary>
|
||||
/// Media label as read by Windows
|
||||
/// </summary>
|
||||
/// <remarks>The try/catch is needed because Windows will throw an exception if the drive is not marked as active</remarks>
|
||||
public string VolumeLabel
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return driveInfo?.VolumeLabel;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public string VolumeLabel { get; private set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Derived Fields
|
||||
|
||||
/// <summary>
|
||||
/// Media label as read by Windows, formatted to avoid odd outputs
|
||||
@@ -72,12 +67,12 @@ namespace MPF.Core.Data
|
||||
get
|
||||
{
|
||||
string volumeLabel = Template.DiscNotDetected;
|
||||
if (driveInfo.IsReady)
|
||||
if (this.MarkedActive)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(driveInfo.VolumeLabel))
|
||||
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
|
||||
volumeLabel = "track";
|
||||
else
|
||||
volumeLabel = driveInfo.VolumeLabel;
|
||||
volumeLabel = this.VolumeLabel;
|
||||
}
|
||||
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
@@ -88,80 +83,94 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DriveInfo object representing the drive, if possible
|
||||
/// Windows drive letter
|
||||
/// </summary>
|
||||
private readonly DriveInfo driveInfo;
|
||||
public char Letter => this.Name == null || this.Name.Length == 0 ? '\0' : this.Name[0];
|
||||
|
||||
public Drive(InternalDriveType? driveType, DriveInfo driveInfo)
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Protected constructor
|
||||
/// </summary>
|
||||
protected Drive() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new Drive object from a drive type and device path
|
||||
/// </summary>
|
||||
/// <param name="driveType">InternalDriveType value representing the drive type</param>
|
||||
/// <param name="devicePath">Path to the device according to the local machine</param>
|
||||
public static Drive Create(InternalDriveType? driveType, string devicePath)
|
||||
{
|
||||
this.InternalDriveType = driveType;
|
||||
this.driveInfo = driveInfo;
|
||||
// Create a new, empty drive object
|
||||
var drive = new Drive()
|
||||
{
|
||||
InternalDriveType = driveType,
|
||||
};
|
||||
|
||||
// If we have an invalid device path, return null
|
||||
if (string.IsNullOrWhiteSpace(devicePath))
|
||||
return null;
|
||||
|
||||
// Sanitize a Windows-formatted long device path
|
||||
if (devicePath.StartsWith("\\\\.\\"))
|
||||
devicePath = devicePath.Substring("\\\\.\\".Length);
|
||||
|
||||
// Create and validate the drive info object
|
||||
var driveInfo = new DriveInfo(devicePath);
|
||||
if (driveInfo == null || driveInfo == default)
|
||||
return null;
|
||||
|
||||
// Fill in the rest of the data
|
||||
drive.PopulateFromDriveInfo(driveInfo);
|
||||
|
||||
return drive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate all fields from a DriveInfo object
|
||||
/// </summary>
|
||||
/// <param name="driveInfo">DriveInfo object to populate from</param>
|
||||
private void PopulateFromDriveInfo(DriveInfo driveInfo)
|
||||
{
|
||||
// If we have an invalid DriveInfo, just return
|
||||
if (driveInfo == null || driveInfo == default)
|
||||
return;
|
||||
|
||||
// Populate the data fields
|
||||
this.Name = driveInfo.Name;
|
||||
this.MarkedActive = driveInfo.IsReady;
|
||||
if (this.MarkedActive)
|
||||
{
|
||||
this.DriveFormat = driveInfo.DriveFormat;
|
||||
this.TotalSize = driveInfo.TotalSize;
|
||||
this.VolumeLabel = driveInfo.VolumeLabel;
|
||||
}
|
||||
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">Ture to ignore fixed drives from population, false otherwise</param>
|
||||
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
|
||||
/// <returns>Active drives, matched to labels, if possible</returns>
|
||||
/// <remarks>
|
||||
/// https://stackoverflow.com/questions/3060796/how-to-distinguish-between-usb-and-floppy-devices?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
|
||||
/// https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx
|
||||
/// </remarks>
|
||||
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
|
||||
{
|
||||
var desiredDriveTypes = new List<DriveType>() { DriveType.CDRom };
|
||||
if (!ignoreFixedDrives)
|
||||
{
|
||||
desiredDriveTypes.Add(DriveType.Fixed);
|
||||
desiredDriveTypes.Add(DriveType.Removable);
|
||||
}
|
||||
|
||||
// Get all supported drive types
|
||||
var drives = DriveInfo.GetDrives()
|
||||
.Where(d => desiredDriveTypes.Contains(d.DriveType))
|
||||
.Select(d => new Drive(EnumConverter.ToInternalDriveType(d.DriveType), d))
|
||||
.ToList();
|
||||
|
||||
// TODO: Management searcher stuff is not supported on other platforms
|
||||
// Get the floppy drives and set the flag from removable
|
||||
try
|
||||
{
|
||||
ManagementObjectSearcher searcher =
|
||||
new ManagementObjectSearcher("root\\CIMV2",
|
||||
"SELECT * FROM Win32_LogicalDisk");
|
||||
|
||||
var collection = searcher.Get();
|
||||
foreach (ManagementObject queryObj in collection)
|
||||
{
|
||||
uint? mediaType = (uint?)queryObj["MediaType"];
|
||||
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
|
||||
{
|
||||
char devId = queryObj["DeviceID"].ToString()[0];
|
||||
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
|
||||
// Order the drives by drive letter
|
||||
drives = drives.OrderBy(i => i.Letter).ToList();
|
||||
|
||||
var drives = GetDriveList(ignoreFixedDrives);
|
||||
drives = drives?.OrderBy(i => i.Letter)?.ToList();
|
||||
return drives;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current media type from drive letter
|
||||
/// </summary>
|
||||
/// <param name="drive"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This may eventually be replaced by Aaru.Devices being able to be about 10x more accurate.
|
||||
/// This will also end up making it so that IMAPI2 is no longer necessary. Unfortunately, that
|
||||
/// will only work for .NET Core 3.1 and beyond.
|
||||
/// </remarks>
|
||||
public (MediaType?, string) GetMediaType()
|
||||
{
|
||||
// Take care of the non-optical stuff first
|
||||
@@ -172,22 +181,25 @@ namespace MPF.Core.Data
|
||||
return (MediaType.HardDisk, null);
|
||||
else if (this.InternalDriveType == Data.InternalDriveType.Removable)
|
||||
return (MediaType.FlashDrive, null);
|
||||
#if NET6_0_OR_GREATER
|
||||
else
|
||||
return GetMediaTypeFromSize();
|
||||
#endif
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
// Get the current drive information
|
||||
string deviceId = null;
|
||||
bool loaded = false;
|
||||
try
|
||||
{
|
||||
// Get the device ID first
|
||||
var searcher = new ManagementObjectSearcher(
|
||||
"root\\CIMV2",
|
||||
$"SELECT * FROM Win32_CDROMDrive WHERE Id = '{this.Letter}:\'");
|
||||
CimSession session = CimSession.Create(null);
|
||||
var collection = session.QueryInstances("root\\CIMV2", "WQL", $"SELECT * FROM Win32_CDROMDrive WHERE Id = '{this.Letter}:\'");
|
||||
|
||||
foreach (ManagementObject queryObj in searcher.Get())
|
||||
foreach (CimInstance instance in collection)
|
||||
{
|
||||
deviceId = (string)queryObj["DeviceID"];
|
||||
loaded = (bool)queryObj["MediaLoaded"];
|
||||
CimKeyedCollection<CimProperty> properties = instance.CimInstanceProperties;
|
||||
deviceId = (string)properties["DeviceID"]?.Value;
|
||||
loaded = (bool)properties["MediaLoaded"]?.Value;
|
||||
}
|
||||
|
||||
// If we got no valid device, we don't care and just return
|
||||
@@ -196,6 +208,8 @@ namespace MPF.Core.Data
|
||||
else if (!loaded)
|
||||
return (null, "Device is not reporting media loaded");
|
||||
|
||||
#if NETFRAMEWORK
|
||||
|
||||
MsftDiscMaster2 discMaster = new MsftDiscMaster2();
|
||||
deviceId = deviceId.ToLower().Replace('\\', '#').Replace('/', '#');
|
||||
string id = null;
|
||||
@@ -223,31 +237,17 @@ namespace MPF.Core.Data
|
||||
|
||||
var media = dataWriter.CurrentPhysicalMediaType;
|
||||
return (media.IMAPIToMediaType(), null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (null, ex.Message);
|
||||
}
|
||||
|
||||
#else
|
||||
try
|
||||
{
|
||||
var device = new AaruDevices.Device(this.Name);
|
||||
if (device.Error)
|
||||
return (null, "Could not open device");
|
||||
else if (device.Type != DeviceType.ATAPI && device.Type != DeviceType.SCSI)
|
||||
return (null, "Device does not support media type detection");
|
||||
|
||||
// TODO: In order to get the disc type, Aaru.Core will need to be included as a
|
||||
// package. Unfortunately, it currently has a conflict with one of the required libraries:
|
||||
// System.Text.Encoding.CodePages (BOS uses >= 5.0.0, DotNetZip uses >= 4.5.0 && < 5.0.0)
|
||||
return (null, "IMAPI2 recorder not supported");
|
||||
|
||||
#endif
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (null, ex.Message);
|
||||
}
|
||||
|
||||
return (null, "Media detection only supported on .NET Framework");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -268,57 +268,55 @@ namespace MPF.Core.Data
|
||||
if (this.InternalDriveType != Data.InternalDriveType.Optical)
|
||||
return RedumpSystem.IBMPCcompatible;
|
||||
|
||||
// Audio CD
|
||||
if (this.FormattedVolumeLabel.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return RedumpSystem.AudioCD;
|
||||
}
|
||||
// Check volume labels first
|
||||
RedumpSystem? systemFromLabel = GetRedumpSystemFromVolumeLabel();
|
||||
if (systemFromLabel != null)
|
||||
return systemFromLabel;
|
||||
|
||||
// BD-Video
|
||||
if (Directory.Exists(Path.Combine(drivePath, "BDMV")))
|
||||
{
|
||||
// Technically BD-Audio has this as well, but it's hard to split that out right now
|
||||
return RedumpSystem.BDVideo;
|
||||
}
|
||||
// Get a list of files for quicker checking
|
||||
#region Consoles
|
||||
|
||||
// DVD-Audio
|
||||
// Bandai Playdia Quick Interactive System
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "AUDIO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "AUDIO_TS")).Any())
|
||||
List<string> files = Directory.EnumerateFiles(drivePath, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
|
||||
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
|
||||
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return RedumpSystem.DVDAudio;
|
||||
return RedumpSystem.BandaiPlaydiaQuickInteractiveSystem;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// DVD-Video and Xbox
|
||||
// Mattel Fisher-Price iXL
|
||||
if (File.Exists(Path.Combine(drivePath, "iXL", "iXLUpdater.exe")))
|
||||
{
|
||||
return RedumpSystem.MattelFisherPriceiXL;
|
||||
}
|
||||
|
||||
// Microsoft Xbox 360
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "VIDEO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VIDEO_TS")).Any())
|
||||
if (Directory.Exists(Path.Combine(drivePath, "$SystemUpdate"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any()
|
||||
&& this.TotalSize <= 500_000_000)
|
||||
{
|
||||
// TODO: Maybe add video track hashes to compare for Xbox and X360?
|
||||
if (this.FormattedVolumeLabel.StartsWith("SEP13011042", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox;
|
||||
|
||||
return RedumpSystem.DVDVideo;
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// HD-DVD-Video
|
||||
// Microsoft Xbox One
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "HVDVD_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "HVDVD_TS")).Any())
|
||||
if (Directory.Exists(Path.Combine(drivePath, "MSXC"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "MSXC")).Any())
|
||||
{
|
||||
return RedumpSystem.HDDVDVideo;
|
||||
return RedumpSystem.MicrosoftXboxOne;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Sega Dreamcast
|
||||
if (File.Exists(Path.Combine(drivePath, "IP.BIN")))
|
||||
@@ -365,29 +363,89 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
// Sony PlayStation 3
|
||||
if (this.FormattedVolumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
try
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
if (Directory.Exists(Path.Combine(drivePath, "PS3_GAME"))
|
||||
|| Directory.Exists(Path.Combine(drivePath, "PS3_UPDATE"))
|
||||
|| File.Exists(Path.Combine(drivePath, "PS3_DISC.SFB")))
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Sony PlayStation 4
|
||||
if (this.FormattedVolumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
// There are more possible paths that could be checked.
|
||||
// There are some entries that can be found on most PS4 discs:
|
||||
// "/app/GAME_SERIAL/app.pkg"
|
||||
// "/bd/param.sfo"
|
||||
// "/license/rif"
|
||||
// There are also extra files that can be found on some discs:
|
||||
// "/patch/GAME_SERIAL/patch.pkg" can be found in Redump entry 66816.
|
||||
// Originally on disc as "/patch/CUSA11302/patch.pkg".
|
||||
// Is used as an on-disc update for the base game app without needing to get update from the internet.
|
||||
// "/addcont/GAME_SERIAL/CONTENT_ID/ac.pkg" can be found in Redump entry 97619.
|
||||
// Originally on disc as "/addcont/CUSA00288/FFXIVEXPS400001A/ac.pkg".
|
||||
if (File.Exists(Path.Combine(drivePath, "PS4", "UPDATE", "PS4UPDATE.PUP")))
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation4;
|
||||
}
|
||||
|
||||
// Sony PlayStation 5
|
||||
if (this.FormattedVolumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation5;
|
||||
}
|
||||
|
||||
// V.Tech V.Flash / V.Smile Pro
|
||||
if (File.Exists(Path.Combine(drivePath, "0SYSTEM")))
|
||||
{
|
||||
return RedumpSystem.VTechVFlashVSmilePro;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Computers
|
||||
|
||||
// Sharp X68000
|
||||
if (File.Exists(Path.Combine(drivePath, "COMMAND.X")))
|
||||
{
|
||||
return RedumpSystem.SharpX68000;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Video Formats
|
||||
|
||||
// BD-Video
|
||||
if (Directory.Exists(Path.Combine(drivePath, "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(drivePath, "AUDIO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "AUDIO_TS")).Any())
|
||||
{
|
||||
return RedumpSystem.DVDAudio;
|
||||
}
|
||||
|
||||
else if (Directory.Exists(Path.Combine(drivePath, "VIDEO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VIDEO_TS")).Any())
|
||||
{
|
||||
return RedumpSystem.DVDVideo;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// HD-DVD-Video
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "HVDVD_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "HVDVD_TS")).Any())
|
||||
{
|
||||
return RedumpSystem.HDDVDVideo;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// VCD
|
||||
try
|
||||
{
|
||||
@@ -399,10 +457,63 @@ namespace MPF.Core.Data
|
||||
}
|
||||
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 RedumpSystem? GetRedumpSystemFromVolumeLabel()
|
||||
{
|
||||
// If the volume label is empty, we can't do anything
|
||||
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
|
||||
return null;
|
||||
|
||||
// Audio CD
|
||||
if (this.VolumeLabel.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.AudioCD;
|
||||
|
||||
// Microsoft Xbox
|
||||
if (this.VolumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox;
|
||||
else if (this.VolumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox;
|
||||
|
||||
// Microsoft Xbox 360
|
||||
if (this.VolumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
else if (this.VolumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
|
||||
// Microsoft Xbox 360 - Too overly broad even if a lot of discs use this
|
||||
//if (this.VolumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
// return RedumpSystem.MicrosoftXbox360; // Also for Xbox One?
|
||||
//if (this.VolumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
// return RedumpSystem.MicrosoftXbox360;
|
||||
|
||||
// Sega Mega-CD / Sega-CD
|
||||
if (this.VolumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SegaMegaCDSegaCD;
|
||||
|
||||
// Sony PlayStation 3
|
||||
if (this.VolumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
|
||||
// Sony PlayStation 4
|
||||
if (this.VolumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SonyPlayStation4;
|
||||
|
||||
// Sony PlayStation 5
|
||||
if (this.VolumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SonyPlayStation5;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a sector with a specified size from the drive
|
||||
/// </summary>
|
||||
@@ -412,7 +523,7 @@ namespace MPF.Core.Data
|
||||
public byte[] ReadSector(long num, int size = 2048)
|
||||
{
|
||||
// Missing drive leter is not supported
|
||||
if (string.IsNullOrEmpty(this.driveInfo?.Name))
|
||||
if (string.IsNullOrEmpty(this.Name))
|
||||
return null;
|
||||
|
||||
// We don't support negative sectors
|
||||
@@ -444,5 +555,85 @@ namespace MPF.Core.Data
|
||||
fs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the current drive information based on path
|
||||
/// </summary>
|
||||
public void RefreshDrive()
|
||||
{
|
||||
var driveInfo = DriveInfo.GetDrives().FirstOrDefault(d => d?.Name == this.Name);
|
||||
this.PopulateFromDriveInfo(driveInfo);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Get the media type for a device path based on size
|
||||
/// </summary>
|
||||
/// <returns>MediaType, null on error</returns>
|
||||
private (MediaType?, string) GetMediaTypeFromSize()
|
||||
{
|
||||
if (this.TotalSize >= 0 && this.TotalSize < 800_000_000 && this.DriveFormat == "CDFS")
|
||||
return (MediaType.CDROM, null);
|
||||
else if (this.TotalSize >= 400_000_000 && this.TotalSize <= 8_540_000_000 && this.DriveFormat == "UDF")
|
||||
return (MediaType.DVD, null);
|
||||
else
|
||||
return (MediaType.BluRay, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all current attached Drives
|
||||
/// </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
|
||||
|
||||
try
|
||||
{
|
||||
// Get all supported drive types
|
||||
var drives = DriveInfo.GetDrives()
|
||||
.Where(d => desiredDriveTypes.Contains(d.DriveType))
|
||||
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name))
|
||||
.ToList();
|
||||
|
||||
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)[0];
|
||||
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
|
||||
}
|
||||
}
|
||||
|
||||
return drives;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return new List<Drive>();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
Aaru,
|
||||
DD,
|
||||
DiscImageCreator,
|
||||
Redumper,
|
||||
|
||||
// Verification support only
|
||||
CleanRip,
|
||||
|
||||
@@ -39,6 +39,15 @@ namespace MPF.Core.Data
|
||||
set { _settings["DDPath"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Path to Redumper
|
||||
/// </summary>
|
||||
public string RedumperPath
|
||||
{
|
||||
get { return GetStringSetting(_settings, "RedumperPath", "Programs\\Redumper\\redumper.exe"); }
|
||||
set { _settings["RedumperPath"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected dumping program
|
||||
/// </summary>
|
||||
@@ -78,6 +87,15 @@ namespace MPF.Core.Data
|
||||
set { _settings["CheckForUpdatesOnStartup"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast update label - Skips disc checks and updates path only
|
||||
/// </summary>
|
||||
public bool FastUpdateLabel
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "FastUpdateLabel", false); }
|
||||
set { _settings["FastUpdateLabel"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default output path for dumps
|
||||
/// </summary>
|
||||
@@ -104,6 +122,16 @@ namespace MPF.Core.Data
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
@@ -113,7 +141,7 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedCD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedCD", 72); }
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedCD", 24); }
|
||||
set { _settings["PreferredDumpSpeedCD"] = value.ToString(); }
|
||||
}
|
||||
|
||||
@@ -122,16 +150,25 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedDVD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedDVD", 24); }
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedDVD", 16); }
|
||||
set { _settings["PreferredDumpSpeedDVD"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default HD-DVD dumping speed
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedHDDVD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedHDDVD", 8); }
|
||||
set { _settings["PreferredDumpSpeedHDDVD"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default BD dumping speed
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedBD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedBD", 16); }
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedBD", 8); }
|
||||
set { _settings["PreferredDumpSpeedBD"] = value.ToString(); }
|
||||
}
|
||||
|
||||
@@ -188,6 +225,24 @@ namespace MPF.Core.Data
|
||||
|
||||
#region DiscImageCreator
|
||||
|
||||
/// <summary>
|
||||
/// Enable multi-sector read flag by default
|
||||
/// </summary>
|
||||
public bool DICMultiSectorRead
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "DICMultiSectorRead", false); }
|
||||
set { _settings["DICMultiSectorRead"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Include a default multi-sector read value
|
||||
/// </summary>
|
||||
public int DICMultiSectorReadValue
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "DICMultiSectorReadValue", 0); }
|
||||
set { _settings["DICMultiSectorReadValue"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable overly-secure dumping flags by default
|
||||
/// </summary>
|
||||
@@ -220,6 +275,15 @@ namespace MPF.Core.Data
|
||||
set { _settings["DICRereadCount"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default number of DVD/HD-DVD/BD rereads
|
||||
/// </summary>
|
||||
public int DICDVDRereadCount
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "DICDVDRereadCount", 10); }
|
||||
set { _settings["DICDVDRereadCount"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset drive after dumping (useful for older drives)
|
||||
/// </summary>
|
||||
@@ -240,6 +304,19 @@ namespace MPF.Core.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region Redumper
|
||||
|
||||
/// <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>
|
||||
@@ -251,6 +328,15 @@ namespace MPF.Core.Data
|
||||
set { _settings["ScanForProtection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output all found protections to a separate file in the directory
|
||||
/// </summary>
|
||||
public bool OutputSeparateProtectionFile
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "OutputSeparateProtectionFile", true); }
|
||||
set { _settings["OutputSeparateProtectionFile"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add placeholder values in the submission info
|
||||
/// </summary>
|
||||
@@ -269,6 +355,24 @@ namespace MPF.Core.Data
|
||||
set { _settings["PromptForDiscInformation"] = 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>
|
||||
@@ -376,15 +480,6 @@ namespace MPF.Core.Data
|
||||
set { _settings["ScanPackersForProtection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force scanning all files for protection
|
||||
/// </summary>
|
||||
public bool ForceScanningForProtection
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "ForceScanningForProtection", false); }
|
||||
set { _settings["ForceScanningForProtection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Include debug information with scan results
|
||||
/// </summary>
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace MPF.Core.Data
|
||||
/// Contains information specific to an XGD disc
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// XGD1 XeMID Format Information:
|
||||
/// XGD1 XMID Format Information:
|
||||
///
|
||||
/// AABBBCCD
|
||||
/// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details)
|
||||
@@ -37,9 +37,9 @@ namespace MPF.Core.Data
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Raw XeMID string that all other information is derived from
|
||||
/// Raw XMID/XeMID string that all other information is derived from
|
||||
/// </summary>
|
||||
public string XeMID { get; private set; }
|
||||
public string XMID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 2-character publisher identifier
|
||||
@@ -112,27 +112,27 @@ namespace MPF.Core.Data
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Populate a set of XGD information from a Master ID (XeMID) string
|
||||
/// Populate a set of XGD information from a Master ID (XMID/XeMID) string
|
||||
/// </summary>
|
||||
/// <param name="xemid">XeMID string representing the DMI information</param>
|
||||
/// <param name="xmid">XMID/XeMID string representing the DMI information</param>
|
||||
/// <param name="validate">True if value validation should be performed, false otherwise</param>
|
||||
public XgdInfo(string xemid, bool validate = false)
|
||||
public XgdInfo(string xmid, bool validate = false)
|
||||
{
|
||||
this.Initialized = false;
|
||||
if (string.IsNullOrWhiteSpace(xemid))
|
||||
if (string.IsNullOrWhiteSpace(xmid))
|
||||
return;
|
||||
|
||||
this.XeMID = xemid.TrimEnd('\0');
|
||||
if (string.IsNullOrWhiteSpace(this.XeMID))
|
||||
this.XMID = xmid.TrimEnd('\0');
|
||||
if (string.IsNullOrWhiteSpace(this.XMID))
|
||||
return;
|
||||
|
||||
// XGD1 information is 8 characters
|
||||
if (this.XeMID.Length == 8)
|
||||
this.Initialized = ParseXGD1XeMID(this.XeMID, validate);
|
||||
if (this.XMID.Length == 8)
|
||||
this.Initialized = ParseXGD1XMID(this.XMID, validate);
|
||||
|
||||
// XGD2/3 information is semi-variable length
|
||||
else if (this.XeMID.Length == 13 || this.XeMID.Length == 14 || this.XeMID.Length == 21 || this.XeMID.Length == 22)
|
||||
this.Initialized = ParseXGD23XeMID(this.XeMID, validate);
|
||||
else if (this.XMID.Length == 13 || this.XMID.Length == 14 || this.XMID.Length == 21 || this.XMID.Length == 22)
|
||||
this.Initialized = ParseXGD23XeMID(this.XMID, validate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -191,23 +191,23 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an XGD1 XeMID string
|
||||
/// Parse an XGD1 XMID string
|
||||
/// </summary>
|
||||
/// <param name="xemid">XeMID string to attempt to parse</param>
|
||||
/// <param name="xmid">XMID string to attempt to parse</param>
|
||||
/// <param name="validate">True if value validation should be performed, false otherwise</param>
|
||||
/// <returns>True if the XeMID could be parsed, false otherwise</returns>
|
||||
private bool ParseXGD1XeMID(string xemid, bool validate)
|
||||
/// <returns>True if the XMID could be parsed, false otherwise</returns>
|
||||
private bool ParseXGD1XMID(string xmid, bool validate)
|
||||
{
|
||||
if (xemid == null || xemid.Length != 8)
|
||||
if (xmid == null || xmid.Length != 8)
|
||||
return false;
|
||||
|
||||
this.PublisherIdentifier = xemid.Substring(0, 2);
|
||||
this.PublisherIdentifier = xmid.Substring(0, 2);
|
||||
if (validate && string.IsNullOrEmpty(this.PublisherName))
|
||||
return false;
|
||||
|
||||
this.GameID = xemid.Substring(2, 3);
|
||||
this.SKU = xemid.Substring(5, 2);
|
||||
this.RegionIdentifier = xemid[7];
|
||||
this.GameID = xmid.Substring(2, 3);
|
||||
this.SKU = xmid.Substring(5, 2);
|
||||
this.RegionIdentifier = xmid[7];
|
||||
if (validate && this.InternalRegion == null)
|
||||
return false;
|
||||
|
||||
@@ -408,7 +408,7 @@ namespace MPF.Core.Data
|
||||
switch (region)
|
||||
{
|
||||
case 'W': return Region.World;
|
||||
case 'A': return Region.USA;
|
||||
case 'A': return Region.UnitedStatesOfAmerica;
|
||||
case 'J': return Region.JapanAsia;
|
||||
case 'E': return Region.Europe;
|
||||
case 'K': return Region.USAJapan;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.2</Version>
|
||||
<Version>2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
@@ -20,10 +20,6 @@
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<DefineConstants>NET_FRAMEWORK</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<COMReference Include="IMAPI2">
|
||||
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
|
||||
@@ -50,16 +46,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="System.Management" Version="6.0.0-rc.1.21451.13" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'!='net48'">
|
||||
<PackageReference Include="Aaru.Devices" Version="5.3.0-rc2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Management" />
|
||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
396
MPF.Core/Utilities/Chime.cs
Normal file
396
MPF.Core/Utilities/Chime.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
using System;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Methods to deal with outputting tones to the PC speaker
|
||||
/// </summary>
|
||||
public class Chime
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard duration to play a single tone
|
||||
/// </summary>
|
||||
private const int standardDurationMs = 200;
|
||||
|
||||
#region Octave 0
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(0)
|
||||
/// </summary>
|
||||
private const int noteC0 = 16; // 16.35
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(0)
|
||||
/// </summary>
|
||||
private const int noteD0 = 18; // 18.35
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(0)
|
||||
/// </summary>
|
||||
private const int noteE0 = 21; // 20.60
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(0)
|
||||
/// </summary>
|
||||
private const int noteF0 = 22; // 21.83
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(0)
|
||||
/// </summary>
|
||||
private const int noteG0 = 25; // 24.50
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(0)
|
||||
/// </summary>
|
||||
private const int noteA0 = 28; // 27.50
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(0)
|
||||
/// </summary>
|
||||
private const int noteB0 = 31; // 30.87
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 1
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(1)
|
||||
/// </summary>
|
||||
private const int noteC1 = 33; // 32.70
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(1)
|
||||
/// </summary>
|
||||
private const int noteD1 = 37; // 36.71
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(1)
|
||||
/// </summary>
|
||||
private const int noteE1 = 41; // 41.20
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(1)
|
||||
/// </summary>
|
||||
private const int noteF1 = 44; // 43.65
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(1)
|
||||
/// </summary>
|
||||
private const int noteG1 = 49; // 49.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(1)
|
||||
/// </summary>
|
||||
private const int noteA1 = 55; // 55.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(1)
|
||||
/// </summary>
|
||||
private const int noteB1 = 62; // 61.74
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 2
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(2)
|
||||
/// </summary>
|
||||
private const int noteC2 = 65; // 65.41
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(2)
|
||||
/// </summary>
|
||||
private const int noteD2 = 73; // 73.42
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(2)
|
||||
/// </summary>
|
||||
private const int noteE2 = 82; // 82.41
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(2)
|
||||
/// </summary>
|
||||
private const int noteF2 = 87; // 87.31
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(2)
|
||||
/// </summary>
|
||||
private const int noteG2 = 98; // 98.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(2)
|
||||
/// </summary>
|
||||
private const int noteA2 = 110; // 110.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(2)
|
||||
/// </summary>
|
||||
private const int noteB2 = 123; // 123.47
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 3
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(3)
|
||||
/// </summary>
|
||||
private const int noteC3 = 131; // 130.81
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(3)
|
||||
/// </summary>
|
||||
private const int noteD3 = 147; // 146.83
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(3)
|
||||
/// </summary>
|
||||
private const int noteE3 = 165; // 164.81
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(3)
|
||||
/// </summary>
|
||||
private const int noteF3 = 175; // 174.61
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(3)
|
||||
/// </summary>
|
||||
private const int noteG3 = 196; // 196.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(3)
|
||||
/// </summary>
|
||||
private const int noteA3 = 220; // 220.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(3)
|
||||
/// </summary>
|
||||
private const int noteB3 = 247; // 246.94
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 4
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(4)
|
||||
/// </summary>
|
||||
private const int noteC4 = 262; // 261.63
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(4)
|
||||
/// </summary>
|
||||
private const int noteD4 = 294; // 293.66
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(4)
|
||||
/// </summary>
|
||||
private const int noteE4 = 330; // 329.63
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(4)
|
||||
/// </summary>
|
||||
private const int noteF4 = 349; // 349.23
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(4)
|
||||
/// </summary>
|
||||
private const int noteG4 = 392; // 392.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(4)
|
||||
/// </summary>
|
||||
private const int noteA4 = 440; // 440.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(4)
|
||||
/// </summary>
|
||||
private const int noteB4 = 494; // 493.88
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 5
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(5)
|
||||
/// </summary>
|
||||
private const int noteC5 = 523; // 523.25
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(5)
|
||||
/// </summary>
|
||||
private const int noteD5 = 587; // 587.33
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(5)
|
||||
/// </summary>
|
||||
private const int noteE5 = 659; // 659.25
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(5)
|
||||
/// </summary>
|
||||
private const int noteF5 = 698; // 698.46
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(5)
|
||||
/// </summary>
|
||||
private const int noteG5 = 783; // 783.99
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(5)
|
||||
/// </summary>
|
||||
private const int noteA5 = 880; // 880.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(5)
|
||||
/// </summary>
|
||||
private const int noteB5 = 988; // 987.77
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 6
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(6)
|
||||
/// </summary>
|
||||
private const int noteC6 = 1047; // 1046.50
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(6)
|
||||
/// </summary>
|
||||
private const int noteD6 = 1175; // 1174.66
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(6)
|
||||
/// </summary>
|
||||
private const int noteE6 = 1319; // 1318.51
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(6)
|
||||
/// </summary>
|
||||
private const int noteF6 = 1397; // 1396.91
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(6)
|
||||
/// </summary>
|
||||
private const int noteG6 = 1568; // 1567.98
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(6)
|
||||
/// </summary>
|
||||
private const int noteA6 = 1760; // 1760.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(6)
|
||||
/// </summary>
|
||||
private const int noteB6 = 1976; // 1975.53
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 7
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(7)
|
||||
/// </summary>
|
||||
private const int noteC7 = 2093; // 2093.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(7)
|
||||
/// </summary>
|
||||
private const int noteD7 = 2349; // 2349.32
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(7)
|
||||
/// </summary>
|
||||
private const int noteE7 = 2637; // 2637.02
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(7)
|
||||
/// </summary>
|
||||
private const int noteF7 = 2794; // 2793.83
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(7)
|
||||
/// </summary>
|
||||
private const int noteG7 = 3136; // 3135.96
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(7)
|
||||
/// </summary>
|
||||
private const int noteA7 = 3520; // 3520.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(7)
|
||||
/// </summary>
|
||||
private const int noteB7 = 3951; // 3951.07
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 8
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(8)
|
||||
/// </summary>
|
||||
private const int noteC8 = 4186; // 4186.01
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(8)
|
||||
/// </summary>
|
||||
private const int noteD8 = 4699; // 4698.63
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(8)
|
||||
/// </summary>
|
||||
private const int noteE8 = 5274; // 5274.04
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(8)
|
||||
/// </summary>
|
||||
private const int noteF8 = 5588; // 5587.65
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(8)
|
||||
/// </summary>
|
||||
private const int noteG8 = 6272; // 6271.93
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(8)
|
||||
/// </summary>
|
||||
private const int noteA8 = 7040; // 7040.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(8)
|
||||
/// </summary>
|
||||
private const int noteB8 = 7902; // 7902.13
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Output a series of beeps for completion, similar to DiscImageCreator
|
||||
/// </summary>
|
||||
/// <param name="success">True if the upward series should play, false otherwise</param>
|
||||
public static void StandardCompletion(bool success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
Console.Beep(noteC4, standardDurationMs);
|
||||
Console.Beep(noteD4, standardDurationMs);
|
||||
Console.Beep(noteE4, standardDurationMs);
|
||||
Console.Beep(noteF4, standardDurationMs);
|
||||
Console.Beep(noteG4, standardDurationMs);
|
||||
Console.Beep(noteA4, standardDurationMs);
|
||||
Console.Beep(noteB4, standardDurationMs);
|
||||
Console.Beep(noteC5, standardDurationMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Beep(noteC5, standardDurationMs);
|
||||
Console.Beep(noteB4, standardDurationMs);
|
||||
Console.Beep(noteA4, standardDurationMs);
|
||||
Console.Beep(noteG4, standardDurationMs);
|
||||
Console.Beep(noteF4, standardDurationMs);
|
||||
Console.Beep(noteE4, standardDurationMs);
|
||||
Console.Beep(noteD4, standardDurationMs);
|
||||
Console.Beep(noteC4, standardDurationMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
using RedumpLib.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
@@ -102,26 +106,6 @@ namespace MPF.Core.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a system is a marker value
|
||||
/// </summary>
|
||||
/// <param name="system">RedumpSystem value to check</param>
|
||||
/// <returns>True if the system is a marker value, false otherwise</returns>
|
||||
public static bool IsMarker(this RedumpSystem? system)
|
||||
{
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.MarkerArcadeEnd:
|
||||
case RedumpSystem.MarkerComputerEnd:
|
||||
case RedumpSystem.MarkerDiscBasedConsoleEnd:
|
||||
// case RedumpSystem.MarkerOtherConsoleEnd:
|
||||
case RedumpSystem.MarkerOtherEnd:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a system is considered XGD
|
||||
/// </summary>
|
||||
@@ -140,5 +124,23 @@ namespace MPF.Core.Utilities
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all programs with their short usable names
|
||||
/// </summary>
|
||||
public static List<string> ListPrograms()
|
||||
{
|
||||
var programs = new List<string>();
|
||||
|
||||
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
|
||||
{
|
||||
if (((InternalProgram)val) == InternalProgram.NONE)
|
||||
continue;
|
||||
|
||||
programs.Add($"{((InternalProgram?)val).LongName()}");
|
||||
}
|
||||
|
||||
return programs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
182
MPF.Core/Utilities/OptionsLoader.cs
Normal file
182
MPF.Core/Utilities/OptionsLoader.cs
Normal file
@@ -0,0 +1,182 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
public static class OptionsLoader
|
||||
{
|
||||
#region Arguments
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from application arguments
|
||||
/// </summary>
|
||||
public static (Options, string, int) LoadFromArguments(string[] args, int startIndex = 0)
|
||||
{
|
||||
// Create the output values with defaults
|
||||
var options = new Options()
|
||||
{
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
OutputSubmissionJSON = false,
|
||||
CompressLogFiles = false,
|
||||
};
|
||||
|
||||
string parsedPath = null;
|
||||
|
||||
// These values require multiple parts to be active
|
||||
bool scan = false, protectFile = false;
|
||||
|
||||
// If we have no arguments, just return
|
||||
if (args == null || args.Length == 0)
|
||||
return (options, null, 0);
|
||||
|
||||
// If we have an invalid start index, just return
|
||||
if (startIndex < 0 || startIndex >= args.Length)
|
||||
return (options, null, startIndex);
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
{
|
||||
// Redump login
|
||||
if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
{
|
||||
string[] credentials = args[startIndex].Split('=')[1].Split(';');
|
||||
options.RedumpUsername = credentials[0];
|
||||
options.RedumpPassword = credentials[1];
|
||||
}
|
||||
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
|
||||
{
|
||||
options.RedumpUsername = args[startIndex + 1];
|
||||
options.RedumpPassword = args[startIndex + 2];
|
||||
startIndex += 2;
|
||||
}
|
||||
|
||||
// Use specific program
|
||||
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
string internalProgram = args[startIndex].Split('=')[1];
|
||||
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
string internalProgram = args[startIndex + 1];
|
||||
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Use a device path for physical checks
|
||||
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
|
||||
{
|
||||
parsedPath = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
|
||||
{
|
||||
parsedPath = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Scan for protection (requires device path)
|
||||
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
|
||||
{
|
||||
scan = true;
|
||||
}
|
||||
|
||||
// Output protection to separate file (requires scan for protection)
|
||||
else if (args[startIndex].Equals("-f") || args[startIndex].Equals("--protect-file"))
|
||||
{
|
||||
protectFile = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
|
||||
{
|
||||
options.OutputSubmissionJSON = true;
|
||||
}
|
||||
|
||||
// Compress log and extraneous files
|
||||
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
|
||||
{
|
||||
options.CompressLogFiles = true;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We default to DiscImageCreator currently
|
||||
if (options.InternalProgram == InternalProgram.NONE)
|
||||
options.InternalProgram = InternalProgram.DiscImageCreator;
|
||||
|
||||
// Now deal with the complex options
|
||||
options.ScanForProtection = scan && !string.IsNullOrWhiteSpace(parsedPath);
|
||||
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrWhiteSpace(parsedPath);
|
||||
|
||||
return (options, parsedPath, startIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a list of supported arguments and descriptions
|
||||
/// </summary>
|
||||
public static List<string> PrintSupportedArguments()
|
||||
{
|
||||
var supportedArguments = new List<string>();
|
||||
|
||||
supportedArguments.Add("-c, --credentials <user> <pw> Redump username and password");
|
||||
supportedArguments.Add("-u, --use <program> Dumping program output type");
|
||||
supportedArguments.Add("-p, --path <drivepath> Physical drive path for additional checks");
|
||||
supportedArguments.Add("-s, --scan Enable copy protection scan (requires --path)");
|
||||
supportedArguments.Add("-f, --protect-file Output protection to separate file (requires --scan)");
|
||||
supportedArguments.Add("-j, --json Enable submission JSON output");
|
||||
supportedArguments.Add("-z, --zip Enable log file compression");
|
||||
|
||||
return supportedArguments;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configuration
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from the application configuration
|
||||
/// </summary>
|
||||
public static Options LoadFromConfig()
|
||||
{
|
||||
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||||
|
||||
var settings = configFile.AppSettings.Settings;
|
||||
var dict = new Dictionary<string, string>();
|
||||
|
||||
foreach (string key in settings.AllKeys)
|
||||
{
|
||||
dict[key] = settings[key]?.Value ?? string.Empty;
|
||||
}
|
||||
|
||||
return new Options(dict);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current set of options to the application configuration
|
||||
/// </summary>
|
||||
public static void SaveToConfig(Options options)
|
||||
{
|
||||
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||||
|
||||
// Loop through all settings in Options and save them, overwriting existing settings
|
||||
foreach (var kvp in options)
|
||||
{
|
||||
configFile.AppSettings.Settings.Remove(kvp.Key);
|
||||
configFile.AppSettings.Settings.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
configFile.Save(ConfigurationSaveMode.Modified);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using MPF.Core.Data;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -162,8 +161,15 @@ namespace MPF.Core.Utilities
|
||||
/// </summary>
|
||||
public static string GetCurrentVersion()
|
||||
{
|
||||
var assemblyVersion = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
|
||||
return assemblyVersion.InformationalVersion;
|
||||
try
|
||||
{
|
||||
var assemblyVersion = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
|
||||
return assemblyVersion.InformationalVersion;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ex.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -171,7 +177,8 @@ namespace MPF.Core.Utilities
|
||||
/// </summary>
|
||||
private static (string tag, string url) GetRemoteVersionAndUrl()
|
||||
{
|
||||
using (WebClient wc = new WebClient())
|
||||
#if NETFRAMEWORK
|
||||
using (System.Net.WebClient wc = new System.Net.WebClient())
|
||||
{
|
||||
wc.Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0";
|
||||
|
||||
@@ -184,6 +191,21 @@ namespace MPF.Core.Utilities
|
||||
|
||||
return (latestTag, releaseUrl);
|
||||
}
|
||||
#else
|
||||
using (System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient())
|
||||
{
|
||||
// TODO: Figure out a better way than having this hardcoded...
|
||||
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
|
||||
var message = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
|
||||
message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
|
||||
string latestReleaseJsonString = hc.Send(message)?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
var latestReleaseJson = JObject.Parse(latestReleaseJsonString);
|
||||
string latestTag = latestReleaseJson["tag_name"].ToString();
|
||||
string releaseUrl = latestReleaseJson["html_url"].ToString();
|
||||
|
||||
return (latestTag, releaseUrl);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.2</Version>
|
||||
<Version>2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
@@ -21,7 +21,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -19,14 +19,9 @@ namespace MPF.Library
|
||||
#region Output paths
|
||||
|
||||
/// <summary>
|
||||
/// Base output directory to write files to
|
||||
/// Base output file path to write files to
|
||||
/// </summary>
|
||||
public string OutputDirectory { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Base output filename for output
|
||||
/// </summary>
|
||||
public string OutputFilename { get; private set; }
|
||||
public string OutputPath { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -87,15 +82,13 @@ namespace MPF.Library
|
||||
/// Constructor for a full DumpEnvironment object from user information
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="outputDirectory"></param>
|
||||
/// <param name="outputFilename"></param>
|
||||
/// <param name="outputPath"></param>
|
||||
/// <param name="drive"></param>
|
||||
/// <param name="system"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="parameters"></param>
|
||||
public DumpEnvironment(Options options,
|
||||
string outputDirectory,
|
||||
string outputFilename,
|
||||
string outputPath,
|
||||
Drive drive,
|
||||
RedumpSystem? system,
|
||||
MediaType? type,
|
||||
@@ -105,7 +98,7 @@ namespace MPF.Library
|
||||
this.Options = options;
|
||||
|
||||
// Output paths
|
||||
(this.OutputDirectory, this.OutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename);
|
||||
this.OutputPath = InfoTool.NormalizeOutputPaths(outputPath);
|
||||
|
||||
// UI information
|
||||
this.Drive = drive;
|
||||
@@ -118,6 +111,43 @@ namespace MPF.Library
|
||||
|
||||
#region Public Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Adjust output paths if we're using DiscImageCreator
|
||||
/// </summary>
|
||||
public void AdjustPathsForDiscImageCreator()
|
||||
{
|
||||
// Only DiscImageCreator has issues with paths
|
||||
if (this.Parameters.InternalProgram != InternalProgram.DiscImageCreator)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// Normalize the output path
|
||||
string outputPath = InfoTool.NormalizeOutputPaths(this.OutputPath);
|
||||
|
||||
// Replace all instances in the output directory
|
||||
string outputDirectory = Path.GetDirectoryName(outputPath);
|
||||
outputDirectory = outputDirectory.Replace(".", "_");
|
||||
|
||||
// Replace all instances in the output filename
|
||||
string outputFilename = Path.GetFileNameWithoutExtension(outputPath);
|
||||
outputFilename = outputFilename.Replace(".", "_");
|
||||
|
||||
// Get the extension for recreating the path
|
||||
string outputExtension = Path.GetExtension(outputPath).TrimStart('.');
|
||||
|
||||
// Rebuild the output path
|
||||
if (!string.IsNullOrWhiteSpace(outputExtension))
|
||||
this.OutputPath = Path.Combine(outputDirectory, $"{outputFilename}.{outputExtension}");
|
||||
else
|
||||
this.OutputPath = Path.Combine(outputDirectory, outputFilename);
|
||||
|
||||
// Assign the path to the filename as well for dumping
|
||||
((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = this.OutputPath;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the parameters object based on the internal program and parameters string
|
||||
/// </summary>
|
||||
@@ -139,6 +169,10 @@ namespace MPF.Library
|
||||
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
|
||||
break;
|
||||
|
||||
case InternalProgram.Redumper:
|
||||
this.Parameters = new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath };
|
||||
break;
|
||||
|
||||
// Verification support only
|
||||
case InternalProgram.CleanRip:
|
||||
this.Parameters = new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null };
|
||||
@@ -178,24 +212,27 @@ namespace MPF.Library
|
||||
return null;
|
||||
|
||||
// Set the proper parameters
|
||||
string filename = OutputDirectory + Path.DirectorySeparatorChar + OutputFilename;
|
||||
switch (Options.InternalProgram)
|
||||
{
|
||||
case InternalProgram.Aaru:
|
||||
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
case InternalProgram.DD:
|
||||
Parameters = new Modules.DD.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
Parameters = new Modules.DD.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
case InternalProgram.DiscImageCreator:
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
case InternalProgram.Redumper:
|
||||
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
default:
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -247,7 +284,7 @@ namespace MPF.Library
|
||||
|
||||
// Execute internal tool
|
||||
progress?.Report(Result.Success($"Executing {Options.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
|
||||
Directory.CreateDirectory(OutputDirectory);
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
|
||||
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
|
||||
progress?.Report(Result.Success($"{Options.InternalProgram} has finished!"));
|
||||
|
||||
@@ -280,8 +317,12 @@ namespace MPF.Library
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
|
||||
|
||||
// Get the output directory and filename separately
|
||||
string outputDirectory = Path.GetDirectoryName(this.OutputPath);
|
||||
string outputFilename = Path.GetFileName(this.OutputPath);
|
||||
|
||||
// Check to make sure that the output had all the correct files
|
||||
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(this.OutputDirectory, this.OutputFilename, this.Parameters, false);
|
||||
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, this.Parameters, false);
|
||||
if (!foundFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
|
||||
@@ -291,8 +332,7 @@ namespace MPF.Library
|
||||
// Extract the information from the output files
|
||||
resultProgress?.Report(Result.Success("Extracting output information from output files..."));
|
||||
SubmissionInfo submissionInfo = await InfoTool.ExtractOutputInformation(
|
||||
this.OutputDirectory,
|
||||
this.OutputFilename,
|
||||
this.OutputPath,
|
||||
this.Drive,
|
||||
this.System,
|
||||
this.Type,
|
||||
@@ -330,39 +370,58 @@ namespace MPF.Library
|
||||
resultProgress?.Report(Result.Success("Disc information skipped!"));
|
||||
}
|
||||
|
||||
// Process special fields for site codes
|
||||
resultProgress?.Report(Result.Success("Processing site codes..."));
|
||||
InfoTool.ProcessSpecialFields(submissionInfo);
|
||||
resultProgress?.Report(Result.Success("Processing complete!"));
|
||||
|
||||
// Format the information for the text output
|
||||
resultProgress?.Report(Result.Success("Formatting information..."));
|
||||
List<string> formattedValues = InfoTool.FormatOutputData(submissionInfo);
|
||||
resultProgress?.Report(Result.Success("Formatting complete!"));
|
||||
(List<string> formattedValues, string formatResult) = InfoTool.FormatOutputData(submissionInfo, this.Options);
|
||||
if (formattedValues == null)
|
||||
resultProgress?.Report(Result.Success(formatResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(formatResult));
|
||||
|
||||
// Write the text output
|
||||
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
|
||||
bool success = InfoTool.WriteOutputData(this.OutputDirectory, formattedValues);
|
||||
if (success)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, formattedValues);
|
||||
if (txtSuccess)
|
||||
resultProgress?.Report(Result.Success(txtResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Writing could not complete!"));
|
||||
resultProgress?.Report(Result.Failure(txtResult));
|
||||
|
||||
// Write the JSON output, if required
|
||||
if (Options.OutputSubmissionJSON)
|
||||
// Write the copy protection output
|
||||
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.json.gz..."));
|
||||
success = InfoTool.WriteOutputData(this.OutputDirectory, submissionInfo);
|
||||
if (success)
|
||||
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, submissionInfo);
|
||||
if (scanSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Writing could not complete!"));
|
||||
}
|
||||
|
||||
// Conpress the logs, if required
|
||||
// Write the JSON output, if required
|
||||
if (Options.OutputSubmissionJSON)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Writing information to !submissionInfo.json{(Options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
|
||||
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, submissionInfo, Options.IncludeArtifacts);
|
||||
if (jsonSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Writing could not complete!"));
|
||||
}
|
||||
|
||||
// Compress the logs, if required
|
||||
if (Options.CompressLogFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Compressing log files..."));
|
||||
success = InfoTool.CompressLogFiles(this.OutputDirectory, this.OutputFilename, this.Parameters);
|
||||
if (success)
|
||||
resultProgress?.Report(Result.Success("Compression complete!"));
|
||||
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, outputFilename, this.Parameters);
|
||||
if (compressSuccess)
|
||||
resultProgress?.Report(Result.Success(compressResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Compression could not complete!"));
|
||||
resultProgress?.Report(Result.Failure(compressResult));
|
||||
}
|
||||
|
||||
resultProgress?.Report(Result.Success("Submission information process complete!"));
|
||||
@@ -439,12 +498,11 @@ namespace MPF.Library
|
||||
return Result.Failure("Error! Current configuration is not supported!");
|
||||
|
||||
// Fix the output paths, just in case
|
||||
(OutputDirectory, OutputFilename) = InfoTool.NormalizeOutputPaths(OutputDirectory, OutputFilename);
|
||||
this.OutputPath = InfoTool.NormalizeOutputPaths(this.OutputPath);
|
||||
|
||||
// Validate that the output path isn't on the dumping drive
|
||||
string fullOutputPath = Path.GetFullPath(Path.Combine(OutputDirectory, OutputFilename));
|
||||
if (fullOutputPath[0] == Drive.Letter)
|
||||
return Result.Failure($"Error! Cannot output to same drive that is being dumped!");
|
||||
if (this.OutputPath[0] == Drive.Letter)
|
||||
return Result.Failure("Error! Cannot output to same drive that is being dumped!");
|
||||
|
||||
// Validate that the required program exists
|
||||
if (!File.Exists(Parameters.ExecutablePath))
|
||||
@@ -453,7 +511,7 @@ namespace MPF.Library
|
||||
// Validate that the dumping drive doesn't contain the executable
|
||||
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath);
|
||||
if (fullExecutablePath[0] == Drive.Letter)
|
||||
return Result.Failure("$Error! Cannot dump same drive that executable resides on!");
|
||||
return Result.Failure("Error! Cannot dump same drive that executable resides on!");
|
||||
|
||||
// Validate that the current configuration is supported
|
||||
return Tools.GetSupportStatus(System, Type);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<AssemblyName>MPF.Library</AssemblyName>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.2</Version>
|
||||
<Version>2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
@@ -21,10 +21,6 @@
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<DefineConstants>NET_FRAMEWORK</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
|
||||
@@ -32,12 +28,14 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.0" GeneratePathProperty="true">
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.7.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -5,57 +5,83 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using BurnOutSharp;
|
||||
using BurnOutSharp.External.psxt001z;
|
||||
using BurnOutSharp.ProtectionType;
|
||||
using MPF.Core.Data;
|
||||
using psxt001z;
|
||||
|
||||
namespace MPF.Library
|
||||
{
|
||||
public static class Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// Run protection scan on a given dump environment
|
||||
/// Run protection scan on a given path
|
||||
/// </summary>
|
||||
/// <param name="path">Path to scan for protection</param>
|
||||
/// <param name="options">Options object that determines what to scan</param>
|
||||
/// <param name="progress">Optional progress callback</param>
|
||||
/// <returns>TCopy protection detected in the envirionment, if any</returns>
|
||||
public static async Task<(bool, string)> RunProtectionScanOnPath(string path, Options options, IProgress<ProtectionProgress> progress = null)
|
||||
/// <returns>Set of all detected copy protections with an optional error string</returns>
|
||||
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Options options, IProgress<ProtectionProgress> progress = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var found = await Task.Run(() =>
|
||||
{
|
||||
var scanner = new Scanner(progress)
|
||||
{
|
||||
IncludeDebug = options.IncludeDebugProtectionInformation,
|
||||
ScanAllFiles = options.ForceScanningForProtection,
|
||||
ScanArchives = options.ScanArchivesForProtection,
|
||||
ScanPackers = options.ScanPackersForProtection,
|
||||
};
|
||||
var scanner = new Scanner(
|
||||
options.ScanArchivesForProtection,
|
||||
scanContents: true, // Hardcoded value to avoid issues
|
||||
options.ScanPackersForProtection,
|
||||
scanPaths: true, // Hardcoded value to avoid issues
|
||||
options.IncludeDebugProtectionInformation,
|
||||
progress);
|
||||
|
||||
return scanner.GetProtections(path);
|
||||
});
|
||||
|
||||
if (found == null || found.Count() == 0)
|
||||
return (true, "None found");
|
||||
// If nothing was returned, return
|
||||
if (found == null || !found.Any())
|
||||
return (null, null);
|
||||
|
||||
// Get an ordered list of distinct found protections
|
||||
var orderedDistinctProtections = found
|
||||
// Filter out any empty protections
|
||||
var filteredProtections = found
|
||||
.Where(kvp => kvp.Value != null && kvp.Value.Any())
|
||||
.SelectMany(kvp => kvp.Value)
|
||||
.Distinct()
|
||||
.OrderBy(p => p);
|
||||
.ToDictionary(
|
||||
kvp => kvp.Key,
|
||||
kvp => kvp.Value.OrderBy(s => s).ToList());
|
||||
|
||||
// Sanitize and join protections for writing
|
||||
string protections = SanitizeFoundProtections(orderedDistinctProtections);
|
||||
return (true, protections);
|
||||
// Return the filtered set of protections
|
||||
return (filteredProtections, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, ex.ToString());
|
||||
return (null, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format found protections to a deduplicated, ordered string
|
||||
/// </summary>
|
||||
/// <param name="protections">Dictionary of file to list of protection mappings</param>
|
||||
/// <returns>Detected protections, if any</returns>
|
||||
public static string FormatProtections(Dictionary<string, List<string>> protections)
|
||||
{
|
||||
// If the filtered list is empty in some way, return
|
||||
if (protections == null || !protections.Any())
|
||||
return "None found";
|
||||
|
||||
// Get an ordered list of distinct found protections
|
||||
var orderedDistinctProtections = protections
|
||||
.SelectMany(kvp => kvp.Value)
|
||||
.Distinct()
|
||||
.OrderBy(p => p);
|
||||
|
||||
// Sanitize and join protections for writing
|
||||
string protectionString = SanitizeFoundProtections(orderedDistinctProtections);
|
||||
if (string.IsNullOrWhiteSpace(protectionString))
|
||||
return "None found";
|
||||
|
||||
return protectionString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the existence of an anti-modchip string from a PlayStation disc, if possible
|
||||
/// </summary>
|
||||
@@ -73,7 +99,7 @@ namespace MPF.Library
|
||||
try
|
||||
{
|
||||
byte[] fileContent = File.ReadAllBytes(file);
|
||||
string protection = antiModchip.CheckContents(file, fileContent, false, null, null);
|
||||
string protection = antiModchip.CheckContents(file, fileContent, false);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
return true;
|
||||
}
|
||||
@@ -177,7 +203,18 @@ namespace MPF.Library
|
||||
// SafeCast
|
||||
// TODO: Figure this one out
|
||||
|
||||
// Cactus Data Shield / SafeDisc
|
||||
if (foundProtections.Any(p => p == "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)"))
|
||||
{
|
||||
foundProtections = foundProtections
|
||||
.Where(p => p != "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)");
|
||||
|
||||
if (foundProtections.Any(p => !p.StartsWith("SafeDisc")))
|
||||
foundProtections = foundProtections.Append("Cactus Data Shield 300");
|
||||
}
|
||||
|
||||
// SafeDisc
|
||||
// TODO: Update based on new internal naming schemes
|
||||
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
|
||||
{
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}")))
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml.Serialization;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.CueSheets;
|
||||
using RedumpLib.Data;
|
||||
@@ -183,29 +184,58 @@ namespace MPF.Modules.Aaru
|
||||
break;
|
||||
|
||||
default:
|
||||
return (false, missingFiles); // TODO: Figure out more formats
|
||||
missingFiles.Add("Media and system combination not supported for Aaru");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
// TODO: Fill in submission info specifics for Aaru
|
||||
string outputDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
// TODO: Determine if there's an Aaru version anywhere
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
|
||||
// Deserialize the sidecar, if possible
|
||||
var sidecar = GenerateSidecar(basePath + ".cicm.xml");
|
||||
|
||||
// Fill in the hardware data
|
||||
if (GetHardwareInfo(sidecar, out string manufacturer, out string model, out string firmware))
|
||||
{
|
||||
info.DumpingInfo.Manufacturer = manufacturer;
|
||||
info.DumpingInfo.Model = model;
|
||||
info.DumpingInfo.Firmware = firmware;
|
||||
}
|
||||
|
||||
// Fill in the disc type data
|
||||
if (GetDiscType(sidecar, out string discType, out string discSubType))
|
||||
{
|
||||
string fullDiscType = string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
|
||||
fullDiscType = $"{discType} ({discSubType})";
|
||||
else if (!string.IsNullOrWhiteSpace(discType) && string.IsNullOrWhiteSpace(discSubType))
|
||||
fullDiscType = discType;
|
||||
else if (string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
|
||||
fullDiscType = discSubType;
|
||||
|
||||
info.DumpingInfo.ReportedDiscType = fullDiscType;
|
||||
}
|
||||
|
||||
// Fill in the hash data
|
||||
info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(sidecar, basePath);
|
||||
|
||||
switch (this.Type)
|
||||
{
|
||||
// TODO: Can this do GD-ROM?
|
||||
case MediaType.CDROM:
|
||||
// TODO: Can this do GD-ROM?
|
||||
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
|
||||
// TODO: Re-enable once PVD generation / finding is fixed
|
||||
// Generate / obtain the PVD
|
||||
//info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
|
||||
long errorCount = -1;
|
||||
if (File.Exists(basePath + ".resume.xml"))
|
||||
@@ -232,6 +262,10 @@ namespace MPF.Modules.Aaru
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
}
|
||||
|
||||
// TODO: Re-enable once PVD generation / finding is fixed
|
||||
// Generate / obtain the PVD
|
||||
//info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
|
||||
// Deal with the layerbreak
|
||||
string layerbreak = null;
|
||||
if (this.Type == MediaType.DVD)
|
||||
@@ -242,12 +276,11 @@ namespace MPF.Modules.Aaru
|
||||
// If we have a single-layer disc
|
||||
if (string.IsNullOrWhiteSpace(layerbreak))
|
||||
{
|
||||
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
// Currently no-op
|
||||
}
|
||||
// If we have a dual-layer disc
|
||||
else
|
||||
{
|
||||
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
info.SizeAndChecksums.Layerbreak = Int64.Parse(layerbreak);
|
||||
}
|
||||
|
||||
@@ -274,7 +307,8 @@ namespace MPF.Modules.Aaru
|
||||
case RedumpSystem.KonamiPython2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"{Constants.InternalSerialNameCommentField} {pythonTwoSerial}\n";
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
|
||||
}
|
||||
@@ -283,12 +317,12 @@ namespace MPF.Modules.Aaru
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXbox:
|
||||
if (GetXgdAuxInfo(sidecar, out string dmihash, out string pfihash, out string sshash, out string ss, out string ssver))
|
||||
if (GetXgdAuxInfo(sidecar, out string xgd1DMIHash, out string xgd1PFIHash, out string xgd1SSHash, out string ss, out string xgd1SSVer))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"{Template.XBOXDMIHash}: {dmihash ?? ""}\n" +
|
||||
$"{Template.XBOXPFIHash}: {pfihash ?? ""}\n" +
|
||||
$"{Template.XBOXSSHash}: {sshash ?? ""}\n" +
|
||||
$"{Template.XBOXSSVersion}: {ssver ?? ""}\n";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd1DMIHash;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer;
|
||||
info.Extras.SecuritySectorRanges = ss ?? "";
|
||||
}
|
||||
|
||||
@@ -302,12 +336,12 @@ namespace MPF.Modules.Aaru
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXbox360:
|
||||
if (GetXgdAuxInfo(sidecar, out string dmi360hash, out string pfi360hash, out string ss360hash, out string ss360, out string ssver360))
|
||||
if (GetXgdAuxInfo(sidecar, out string xgd23DMIHash, out string xgd23PFIHash, out string xgd23SSHash, out string ss360, out string xgd23SSVer))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"{Template.XBOXDMIHash}: {dmi360hash ?? ""}\n" +
|
||||
$"{Template.XBOXPFIHash}: {pfi360hash ?? ""}\n" +
|
||||
$"{Template.XBOXSSHash}: {ss360hash ?? ""}\n" +
|
||||
$"{Template.XBOXSSVersion}: {ssver360 ?? ""}\n";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = xgd23DMIHash;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer;
|
||||
info.Extras.SecuritySectorRanges = ss360 ?? "";
|
||||
}
|
||||
|
||||
@@ -322,7 +356,8 @@ namespace MPF.Modules.Aaru
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"{Constants.InternalSerialNameCommentField} {playstationSerial}\n";
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
|
||||
}
|
||||
@@ -332,7 +367,8 @@ namespace MPF.Modules.Aaru
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"{Constants.InternalSerialNameCommentField} {playstationTwoSerial}\n";
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
|
||||
}
|
||||
@@ -340,12 +376,19 @@ namespace MPF.Modules.Aaru
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? "";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? "";
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2854,6 +2897,37 @@ namespace MPF.Modules.Aaru
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get reported disc type information, if possible
|
||||
/// </summary>
|
||||
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
|
||||
/// <returns>True if disc type info was set, false otherwise</returns>
|
||||
private static bool GetDiscType(CICMMetadataType cicmSidecar, out string discType, out string discSubType)
|
||||
{
|
||||
// Set the default values
|
||||
discType = null; discSubType = null;
|
||||
|
||||
// If the object is null, we can't get information from it
|
||||
if (cicmSidecar == null)
|
||||
return false;
|
||||
|
||||
// Only care about OpticalDisc types
|
||||
if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0)
|
||||
return false;
|
||||
|
||||
// Find and return the hardware info, if possible
|
||||
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
|
||||
{
|
||||
// Store the first instance of each value
|
||||
if (string.IsNullOrEmpty(discType) && !string.IsNullOrEmpty(opticalDisc.DiscType))
|
||||
discType = opticalDisc.DiscType;
|
||||
if (string.IsNullOrEmpty(discSubType) && !string.IsNullOrEmpty(opticalDisc.DiscSubType))
|
||||
discSubType = opticalDisc.DiscSubType;
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(discType) || !string.IsNullOrEmpty(discSubType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the DVD protection information, if possible
|
||||
/// </summary>
|
||||
@@ -2947,6 +3021,50 @@ namespace MPF.Modules.Aaru
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hardware information, if possible
|
||||
/// </summary>
|
||||
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
|
||||
/// <returns>True if hardware info was set, false otherwise</returns>
|
||||
private static bool GetHardwareInfo(CICMMetadataType cicmSidecar, out string manufacturer, out string model, out string firmware)
|
||||
{
|
||||
// Set the default values
|
||||
manufacturer = null; model = null; firmware = null;
|
||||
|
||||
// If the object is null, we can't get information from it
|
||||
if (cicmSidecar == null)
|
||||
return false;
|
||||
|
||||
// Only care about OpticalDisc types
|
||||
if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0)
|
||||
return false;
|
||||
|
||||
// Find and return the hardware info, if possible
|
||||
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
|
||||
{
|
||||
// If there's no hardware information, skip
|
||||
if (opticalDisc.DumpHardwareArray == null || !opticalDisc.DumpHardwareArray.Any())
|
||||
continue;
|
||||
|
||||
foreach (DumpHardwareType hardware in opticalDisc.DumpHardwareArray)
|
||||
{
|
||||
// If the hardware information is invalid, skip
|
||||
if (hardware == null)
|
||||
continue;
|
||||
|
||||
// Store the first instance of each value
|
||||
if (string.IsNullOrEmpty(manufacturer) && !string.IsNullOrEmpty(hardware.Manufacturer))
|
||||
manufacturer = hardware.Manufacturer;
|
||||
if (string.IsNullOrEmpty(model) && !string.IsNullOrEmpty(hardware.Model))
|
||||
model = hardware.Model;
|
||||
if (string.IsNullOrEmpty(firmware) && !string.IsNullOrEmpty(hardware.Firmware))
|
||||
firmware = hardware.Firmware;
|
||||
}
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(manufacturer) || !string.IsNullOrEmpty(model) || !string.IsNullOrEmpty(firmware);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the layerbreak from the input file, if possible
|
||||
/// </summary>
|
||||
|
||||
7391
MPF.Modules/Aaru/cicm.cs
Normal file
7391
MPF.Modules/Aaru/cicm.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
@@ -153,10 +153,11 @@ namespace MPF.Modules
|
||||
/// Generate a SubmissionInfo for the output files
|
||||
/// </summary>
|
||||
/// <param name="submissionInfo">Base submission info to fill in specifics for</param>
|
||||
/// <param name="options">Options object representing user-defined options</param>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <param name="drive">Drive representing the disc to get information from</param>
|
||||
/// <param name="includeArtifacts">True to include output files as encoded artifacts, false otherwise</param>
|
||||
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, string basePath, Drive drive, bool includeArtifacts);
|
||||
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, Options options, string basePath, Drive drive, bool includeArtifacts);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -241,7 +242,7 @@ namespace MPF.Modules
|
||||
/// </summary>
|
||||
/// <param name="parameters">String possibly representing parameters</param>
|
||||
/// <returns>True if the parameters were set correctly, false otherwise</returns>
|
||||
protected virtual bool ValidateAndSetParameters(string parameters) => true;
|
||||
protected virtual bool ValidateAndSetParameters(string parameters) => !string.IsNullOrWhiteSpace(parameters);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -343,7 +344,7 @@ namespace MPF.Modules
|
||||
return BitConverter.ToString(bytes).Replace("-", string.Empty);
|
||||
}
|
||||
|
||||
return string.Join("\n", File.ReadAllLines(filename));
|
||||
return File.ReadAllText(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -621,6 +622,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (sbyte)(sbyte.Parse(value) * factor);
|
||||
}
|
||||
@@ -697,6 +699,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (short)(short.Parse(value) * factor);
|
||||
}
|
||||
@@ -773,6 +776,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (int)(int.Parse(value) * factor);
|
||||
}
|
||||
@@ -849,6 +853,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return long.Parse(value) * factor;
|
||||
}
|
||||
@@ -931,6 +936,84 @@ namespace MPF.Modules
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a byte parameter
|
||||
/// </summary>
|
||||
/// <param name="parts">List of parts to be referenced</param>
|
||||
/// <param name="flagString">Flag string, if available</param>
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>Byte value if success, Byte.MinValue if skipped, null on error/returns>
|
||||
protected byte? ProcessUInt8Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
|
||||
=> ProcessUInt8Parameter(parts, null, flagString, ref i, missingAllowed);
|
||||
|
||||
/// <summary>
|
||||
/// Process a byte parameter
|
||||
/// </summary>
|
||||
/// <param name="parts">List of parts to be referenced</param>
|
||||
/// <param name="shortFlagString">Short flag string, if available</param>
|
||||
/// <param name="longFlagString">Long flag string, if available</param>
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>Byte value if success, Byte.MinValue if skipped, null on error/returns>
|
||||
protected byte? ProcessUInt8Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
{
|
||||
if (parts == null)
|
||||
return null;
|
||||
|
||||
if (parts[i] == shortFlagString || parts[i] == longFlagString)
|
||||
{
|
||||
if (!IsFlagSupported(longFlagString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
if (missingAllowed)
|
||||
this[longFlagString] = true;
|
||||
|
||||
return null;
|
||||
}
|
||||
else if (IsFlagSupported(parts[i + 1]))
|
||||
{
|
||||
if (missingAllowed)
|
||||
this[longFlagString] = true;
|
||||
|
||||
return null;
|
||||
}
|
||||
else if (!IsValidInt8(parts[i + 1]))
|
||||
{
|
||||
if (missingAllowed)
|
||||
this[longFlagString] = true;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
this[longFlagString] = true;
|
||||
i++;
|
||||
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return (byte)(byte.Parse(value) * factor);
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
if (!IsFlagSupported(longFlagString))
|
||||
return null;
|
||||
|
||||
string[] commandParts = parts[i].Split('=');
|
||||
if (commandParts.Length != 2)
|
||||
return null;
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (byte)(byte.Parse(value) * factor);
|
||||
}
|
||||
|
||||
return Byte.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get yhe trimmed value and multiplication factor from a value
|
||||
/// </summary>
|
||||
@@ -1176,11 +1259,18 @@ namespace MPF.Modules
|
||||
if (!string.IsNullOrEmpty(bootValue))
|
||||
{
|
||||
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)");
|
||||
if (match != null && match.Groups.Count > 1)
|
||||
if (match.Groups.Count > 1)
|
||||
{
|
||||
exeName = match.Groups[1].Value;
|
||||
exeName = exeName.Split(';')[0];
|
||||
serial = exeName.Replace('_', '-').Replace(".", string.Empty);
|
||||
// EXE name may have a trailing `;` after
|
||||
// EXE name should always be in all caps
|
||||
exeName = match.Groups[1].Value
|
||||
.Split(';')[0]
|
||||
.ToUpperInvariant();
|
||||
|
||||
// Serial is most of the EXE name normalized
|
||||
serial = exeName
|
||||
.Replace('_', '-')
|
||||
.Replace(".", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1237,6 +1327,117 @@ namespace MPF.Modules
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
protected static string GetPlayStation3Serial(char? driveLetter)
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find PARAM.SFO, we don't have a PlayStation 3 disc
|
||||
string paramSfoPath = Path.Combine(drivePath, "PS3_GAME", "PARAM.SFO");
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading PARAM.SFO to find the serial at the end of the file
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(File.OpenRead(paramSfoPath)))
|
||||
{
|
||||
br.BaseStream.Seek(-0x18, SeekOrigin.End);
|
||||
return new string(br.ReadChars(9));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
protected static string GetPlayStation3Version(char? driveLetter)
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find PARAM.SFO, we don't have a PlayStation 3 disc
|
||||
string paramSfoPath = Path.Combine(drivePath, "PS3_GAME", "PARAM.SFO");
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading PARAM.SFO to find the version at the end of the file
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(File.OpenRead(paramSfoPath)))
|
||||
{
|
||||
br.BaseStream.Seek(-0x08, SeekOrigin.End);
|
||||
return new string(br.ReadChars(5));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 4 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
protected static string GetPlayStation4Serial(char? driveLetter)
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find param.sfo, we don't have a PlayStation 4 disc
|
||||
string paramSfoPath = Path.Combine(drivePath, "bd", "param.sfo");
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.sfo to find the serial at the end of the file
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(File.OpenRead(paramSfoPath)))
|
||||
{
|
||||
br.BaseStream.Seek(-0x14, SeekOrigin.End);
|
||||
return new string(br.ReadChars(9));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 4 disc, if possible
|
||||
/// </summary>
|
||||
@@ -1274,6 +1475,43 @@ namespace MPF.Modules
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 5 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
protected static string GetPlayStation5Serial(char? driveLetter)
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find param.json, we don't have a PlayStation 5 disc
|
||||
string paramJsonPath = Path.Combine(drivePath, "bd", "param.json");
|
||||
if (!File.Exists(paramJsonPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.json to find the serial in the unencrypted JSON
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(File.OpenRead(paramJsonPath)))
|
||||
{
|
||||
br.BaseStream.Seek(0x82E, SeekOrigin.Begin);
|
||||
return new string(br.ReadChars(9));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 5 disc, if possible
|
||||
/// </summary>
|
||||
@@ -1353,9 +1591,9 @@ namespace MPF.Modules
|
||||
case 'C': return Region.China;
|
||||
case 'E': return Region.Europe;
|
||||
case 'J': return Region.JapanKorea;
|
||||
case 'K': return Region.Korea;
|
||||
case 'K': return Region.SouthKorea;
|
||||
case 'P': return Region.Japan;
|
||||
case 'U': return Region.USA;
|
||||
case 'U': return Region.UnitedStatesOfAmerica;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1384,4 +1622,4 @@ namespace MPF.Modules
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
|
||||
@@ -50,15 +51,18 @@ namespace MPF.Modules.CleanRip
|
||||
break;
|
||||
|
||||
default:
|
||||
return (false, missingFiles);
|
||||
missingFiles.Add("Media and system combination not supported for CleanRip");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
// TODO: Determine if there's a CleanRip version anywhere
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
info.TracksAndWriteOffsets.ClrMameProData = GetCleanripDatfile(basePath + ".iso", basePath + "-dumpinfo.txt");
|
||||
|
||||
// Get the individual hash data, as per internal
|
||||
@@ -83,10 +87,11 @@ namespace MPF.Modules.CleanRip
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
info.Extras.BCA = GetBCA(basePath + ".bca");
|
||||
|
||||
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out string gcVersion))
|
||||
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out string gcVersion, out string gcName))
|
||||
{
|
||||
info.CommonDiscInfo.Region = gcRegion ?? info.CommonDiscInfo.Region;
|
||||
info.VersionAndEditions.Version = gcVersion ?? info.VersionAndEditions.Version;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = gcName ?? string.Empty;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -203,10 +208,11 @@ namespace MPF.Modules.CleanRip
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <param name="region">Output region, if possible</param>
|
||||
/// <param name="version">Output internal version of the game</param>
|
||||
/// <param name="name">Output internal name of the game</param>
|
||||
/// <returns></returns>
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string version)
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string version, out string name)
|
||||
{
|
||||
region = null; version = null;
|
||||
region = null; version = null; name = null;
|
||||
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(dumpinfo))
|
||||
@@ -226,11 +232,15 @@ namespace MPF.Modules.CleanRip
|
||||
string line = sr.ReadLine().Trim();
|
||||
if (line.StartsWith("Version"))
|
||||
{
|
||||
version = line.Substring(9);
|
||||
version = line.Substring("Version: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("Internal Name"))
|
||||
{
|
||||
name = line.Substring("Internal Name: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("Filename"))
|
||||
{
|
||||
string serial = line.Substring(10);
|
||||
string serial = line.Substring("Filename: ".Length);
|
||||
|
||||
// char gameType = serial[0];
|
||||
// string gameid = serial[1] + serial[2];
|
||||
@@ -245,7 +255,7 @@ namespace MPF.Modules.CleanRip
|
||||
region = Region.Germany;
|
||||
break;
|
||||
case 'E':
|
||||
region = Region.USA;
|
||||
region = Region.UnitedStatesOfAmerica;
|
||||
break;
|
||||
case 'F':
|
||||
region = Region.France;
|
||||
@@ -257,7 +267,7 @@ namespace MPF.Modules.CleanRip
|
||||
region = Region.Japan;
|
||||
break;
|
||||
case 'K':
|
||||
region = Region.Korea;
|
||||
region = Region.SouthKorea;
|
||||
break;
|
||||
case 'L':
|
||||
region = Region.Europe; // Japanese import to Europe
|
||||
@@ -266,22 +276,22 @@ namespace MPF.Modules.CleanRip
|
||||
region = Region.Europe; // American import to Europe
|
||||
break;
|
||||
case 'N':
|
||||
region = Region.USA; // Japanese import to USA
|
||||
region = Region.UnitedStatesOfAmerica; // Japanese import to USA
|
||||
break;
|
||||
case 'P':
|
||||
region = Region.Europe;
|
||||
break;
|
||||
case 'R':
|
||||
region = Region.Russia;
|
||||
region = Region.RussianFederation;
|
||||
break;
|
||||
case 'S':
|
||||
region = Region.Spain;
|
||||
break;
|
||||
case 'Q':
|
||||
region = Region.Korea; // Korea with Japanese language
|
||||
region = Region.SouthKorea; // Korea with Japanese language
|
||||
break;
|
||||
case 'T':
|
||||
region = Region.Korea; // Korea with English language
|
||||
region = Region.SouthKorea; // Korea with English language
|
||||
break;
|
||||
case 'X':
|
||||
region = null; // Not a real region code
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
|
||||
@@ -75,11 +76,14 @@ namespace MPF.Modules.DD
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
// TODO: Fill in submission info specifics for DD
|
||||
string outputDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
// TODO: Determine if there's a DD version anywhere
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
|
||||
switch (this.Type)
|
||||
{
|
||||
// Determine type-specific differences
|
||||
@@ -90,7 +94,8 @@ namespace MPF.Modules.DD
|
||||
case RedumpSystem.KonamiPython2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"{Constants.InternalSerialNameCommentField} {pythonTwoSerial}\n";
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
|
||||
}
|
||||
@@ -101,7 +106,8 @@ namespace MPF.Modules.DD
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"{Constants.InternalSerialNameCommentField} {playstationSerial}\n";
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
|
||||
}
|
||||
@@ -111,7 +117,8 @@ namespace MPF.Modules.DD
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate))
|
||||
{
|
||||
info.CommonDiscInfo.Comments += $"{Constants.InternalSerialNameCommentField} {playstationTwoSerial}\n";
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
|
||||
}
|
||||
@@ -119,12 +126,19 @@ namespace MPF.Modules.DD
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? "";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,9 @@ namespace MPF.Modules.DiscImageCreator
|
||||
public const string C2Opcode = "/c2";
|
||||
public const string CopyrightManagementInformation = "/c";
|
||||
public const string D8Opcode = "/d8";
|
||||
public const string DatExpand = "/d";
|
||||
public const string DisableBeep = "/q";
|
||||
public const string DVDReread = "/rr";
|
||||
public const string ExtractMicroSoftCabFile = "/mscf";
|
||||
public const string Fix = "/fix";
|
||||
public const string ForceUnitAccess = "/f";
|
||||
@@ -67,6 +69,7 @@ namespace MPF.Modules.DiscImageCreator
|
||||
public const string SkipSector = "/sk";
|
||||
public const string SubchannelReadLevel = "/s";
|
||||
public const string UseAnchorVolumeDescriptorPointer = "/avdp";
|
||||
public const string VerifyAudio = "/vrfy";
|
||||
public const string VideoNow = "/vn";
|
||||
public const string VideoNowColor = "/vnc";
|
||||
public const string VideoNowXP = "/vnx";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.2</Version>
|
||||
<Version>2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
@@ -20,27 +20,19 @@
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<DefineConstants>NET_FRAMEWORK</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.CueSheets\MPF.CueSheets.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<ProjectReference Include="..\CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
||||
65
MPF.Modules/Redumper/Constants.cs
Normal file
65
MPF.Modules/Redumper/Constants.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
namespace MPF.Modules.Redumper
|
||||
{
|
||||
/// <summary>
|
||||
/// Top-level commands for Redumper
|
||||
/// </summary>
|
||||
public static class CommandStrings
|
||||
{
|
||||
public const string NONE = "";
|
||||
public const string CD = "cd";
|
||||
public const string Dump = "dump";
|
||||
public const string Info = "info";
|
||||
public const string Protection = "protection";
|
||||
public const string Refine = "refine";
|
||||
//public const string Rings = "rings";
|
||||
public const string Split = "split";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumping flags for Redumper
|
||||
/// </summary>
|
||||
public static class FlagStrings
|
||||
{
|
||||
// General
|
||||
public const string HelpLong = "--help";
|
||||
public const string HelpShort = "-h";
|
||||
public const string Verbose = "--verbose";
|
||||
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 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";
|
||||
|
||||
// Split
|
||||
public const string ForceSplit = "--force-split";
|
||||
public const string LeaveUnchanged = "--leave-unchanged";
|
||||
public const string ForceQTOC = "--force-qtoc";
|
||||
public const string SkipFill = "--skip-fill";
|
||||
public const string ISO9660Trim = "--iso9660-trim";
|
||||
public const string CDiReadyNormalize = "--cdi-ready-normalize";
|
||||
|
||||
// Miscellaneous
|
||||
public const string LBAStart = "--lba-start";
|
||||
public const string LBAEnd = "--lba-end";
|
||||
public const string RefineSubchannel = "--refine-subchannel";
|
||||
public const string Skip = "--skip";
|
||||
}
|
||||
}
|
||||
22
MPF.Modules/Redumper/Converters.cs
Normal file
22
MPF.Modules/Redumper/Converters.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.Redumper
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
#region Cross-enumeration conversions
|
||||
|
||||
/// <summary>
|
||||
/// Get the default extension for a given disc type
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType value to check</param>
|
||||
/// <returns>Valid extension (with leading '.'), null on error</returns>
|
||||
public static string Extension(MediaType? type)
|
||||
{
|
||||
// TODO: Determine what extensions are used for each supported type
|
||||
return ".bin";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1213
MPF.Modules/Redumper/Parameters.cs
Normal file
1213
MPF.Modules/Redumper/Parameters.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
|
||||
@@ -41,6 +42,8 @@ namespace MPF.Modules.UmdImageCreator
|
||||
{
|
||||
if (!File.Exists($"{basePath}_disc.txt"))
|
||||
missingFiles.Add($"{basePath}_disc.txt");
|
||||
if (!File.Exists($"{basePath}_drive.txt"))
|
||||
missingFiles.Add($"{basePath}_drive.txt");
|
||||
if (!File.Exists($"{basePath}_mainError.txt"))
|
||||
missingFiles.Add($"{basePath}_mainError.txt");
|
||||
if (!File.Exists($"{basePath}_mainInfo.txt"))
|
||||
@@ -52,15 +55,19 @@ namespace MPF.Modules.UmdImageCreator
|
||||
break;
|
||||
|
||||
default:
|
||||
return (false, missingFiles);
|
||||
missingFiles.Add("Media and system combination not supported for UmdImageCreator");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
// TODO: Determine if there's a UMDImageCreator version anywhere
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (this.Type)
|
||||
{
|
||||
@@ -94,6 +101,8 @@ namespace MPF.Modules.UmdImageCreator
|
||||
{
|
||||
if (File.Exists(basePath + "_disc.txt"))
|
||||
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt"));
|
||||
if (File.Exists(basePath + "_drive.txt"))
|
||||
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt"));
|
||||
if (File.Exists(basePath + "_mainError.txt"))
|
||||
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt"));
|
||||
if (File.Exists(basePath + "_mainInfo.txt"))
|
||||
@@ -112,6 +121,8 @@ namespace MPF.Modules.UmdImageCreator
|
||||
case MediaType.UMD:
|
||||
if (File.Exists($"{basePath}_disc.txt"))
|
||||
logFiles.Add($"{basePath}_disc.txt");
|
||||
if (File.Exists($"{basePath}_drive.txt"))
|
||||
logFiles.Add($"{basePath}_drive.txt");
|
||||
if (File.Exists($"{basePath}_mainError.txt"))
|
||||
logFiles.Add($"{basePath}_mainError.txt");
|
||||
if (File.Exists($"{basePath}_mainInfo.txt"))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#endif
|
||||
using MPF.Core.Converters;
|
||||
@@ -25,7 +25,7 @@ namespace MPF.Test.Core.Converters
|
||||
DriveType.Removable,
|
||||
};
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// IMAPI_MEDIA_PHYSICAL_TYPE values that map to MediaType
|
||||
/// </summary>
|
||||
@@ -71,7 +71,7 @@ namespace MPF.Test.Core.Converters
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Check that every supported IMAPI_MEDIA_PHYSICAL_TYPE maps to an MediaType
|
||||
/// </summary>
|
||||
@@ -108,7 +108,7 @@ namespace MPF.Test.Core.Converters
|
||||
return testData;
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Generate a test set of IMAPI_MEDIA_PHYSICAL_TYPE values
|
||||
/// </summary>
|
||||
|
||||
@@ -18,11 +18,13 @@ namespace MPF.Test.Library
|
||||
public void ParametersValidTest(string parameters, char letter, bool isFloppy, MediaType? mediaType, bool expected)
|
||||
{
|
||||
var options = new Options() { InternalProgram = InternalProgram.DiscImageCreator };
|
||||
var drive = isFloppy
|
||||
? new Drive(InternalDriveType.Floppy, new DriveInfo(letter.ToString()))
|
||||
: new Drive(InternalDriveType.Optical, new DriveInfo(letter.ToString()));
|
||||
|
||||
var env = new DumpEnvironment(options, string.Empty, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, parameters);
|
||||
// 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, parameters);
|
||||
|
||||
bool actual = env.ParametersValid();
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using MPF.Library;
|
||||
using System.Collections.Generic;
|
||||
using MPF.Library;
|
||||
using RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
@@ -24,9 +25,9 @@ namespace MPF.Test.Library
|
||||
[InlineData(MediaType.DVD, 12345, 1, 2, 3, "DVD-ROM-9")]
|
||||
[InlineData(MediaType.BluRay, 0, 0, 0, 0, "BD-ROM-25")]
|
||||
[InlineData(MediaType.BluRay, 12345, 0, 0, 0, "BD-ROM-25")]
|
||||
[InlineData(MediaType.BluRay, 26_843_531_857, 0, 0, 0, "BD-ROM-33")]
|
||||
//[InlineData(MediaType.BluRay, 26_843_531_857, 0, 0, 0, "BD-ROM-33")]
|
||||
[InlineData(MediaType.BluRay, 12345, 1, 0, 0, "BD-ROM-50")]
|
||||
[InlineData(MediaType.BluRay, 53_687_063_713, 1, 0, 0, "BD-ROM-66")]
|
||||
//[InlineData(MediaType.BluRay, 53_687_063_713, 1, 0, 0, "BD-ROM-66")]
|
||||
[InlineData(MediaType.BluRay, 12345, 1, 2, 0, "BD-ROM-100")]
|
||||
[InlineData(MediaType.BluRay, 12345, 1, 2, 3, "BD-ROM-128")]
|
||||
[InlineData(MediaType.UMD, 0, 0, 0, 0, "UMD-SL")]
|
||||
@@ -47,19 +48,148 @@ namespace MPF.Test.Library
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, null, null)]
|
||||
[InlineData(" ", "", " ", "")]
|
||||
[InlineData("super", "blah.bin", "super", "blah.bin")]
|
||||
[InlineData("super\\hero", "blah.bin", "super\\hero", "blah.bin")]
|
||||
[InlineData("super.hero", "blah.bin", "super.hero", "blah.bin")]
|
||||
[InlineData("superhero", "blah.rev.bin", "superhero", "blah.rev.bin")]
|
||||
[InlineData("super&hero", "blah.bin", "super&hero", "blah.bin")]
|
||||
[InlineData("superhero", "blah&foo.bin", "superhero", "blah&foo.bin")]
|
||||
public void NormalizeOutputPathsTest(string outputDirectory, string outputFilename, string expectedOutputDirectory, string expectedOutputFilename)
|
||||
[InlineData(null, null)]
|
||||
[InlineData(" ", " ")]
|
||||
[InlineData("super\\blah.bin", "super\\blah.bin")]
|
||||
[InlineData("super\\hero\\blah.bin", "super\\hero\\blah.bin")]
|
||||
[InlineData("super.hero\\blah.bin", "super.hero\\blah.bin")]
|
||||
[InlineData("superhero\\blah.rev.bin", "superhero\\blah.rev.bin")]
|
||||
[InlineData("super&hero\\blah.bin", "super&hero\\blah.bin")]
|
||||
[InlineData("superhero\\blah&foo.bin", "superhero\\blah&foo.bin")]
|
||||
public void NormalizeOutputPathsTest(string outputPath, string expectedPath)
|
||||
{
|
||||
(string actualOutputDirectory, string actualOutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename);
|
||||
Assert.Equal(expectedOutputDirectory, actualOutputDirectory);
|
||||
Assert.Equal(expectedOutputFilename, actualOutputFilename);
|
||||
string actualPath = InfoTool.NormalizeOutputPaths(outputPath);
|
||||
Assert.Equal(expectedPath, actualPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessSpecialFieldsCompleteTest()
|
||||
{
|
||||
// Create a new SubmissionInfo object
|
||||
SubmissionInfo 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
|
||||
InfoTool.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
|
||||
SubmissionInfo info = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = null,
|
||||
};
|
||||
|
||||
// Process the special fields
|
||||
InfoTool.ProcessSpecialFields(info);
|
||||
|
||||
// Validate
|
||||
Assert.Null(info.CommonDiscInfo);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessSpecialFieldsNullCommentsContentsTest()
|
||||
{
|
||||
// Create a new SubmissionInfo object
|
||||
SubmissionInfo 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
|
||||
InfoTool.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
|
||||
SubmissionInfo 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
|
||||
InfoTool.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0-windows</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<DefineConstants>NET_FRAMEWORK</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<COMReference Include="IMAPI2">
|
||||
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
|
||||
@@ -39,18 +34,21 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.4.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.core" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PackageReference Include="xunit.analyzers" Version="1.1.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.core" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.4.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -19,38 +19,122 @@ namespace MPF.Test.Modules
|
||||
[InlineData(RedumpSystem.RawThrillsVarious, MediaType.GDROM, null)]
|
||||
public void ParametersFromSystemAndTypeTest(RedumpSystem? knownSystem, MediaType? mediaType, string expected)
|
||||
{
|
||||
var options = new Options { };
|
||||
var options = new Options();
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
Assert.Equal(expected, actual.BaseCommand);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RedumpSystem.AppleMacintosh, MediaType.LaserDisc, true, 20, null, null)]
|
||||
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, false, 20, null, new string[] { FlagStrings.Raw })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, 20, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
|
||||
/* paranoid mode tests */
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, 1000, 2, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
|
||||
[InlineData(RedumpSystem.AppleMacintosh, MediaType.CDROM, false, 20, null, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
|
||||
/* reread c2 */
|
||||
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, false, 1000, null, new string[] { FlagStrings.C2Opcode })]
|
||||
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, false, -1, null, new string[] { FlagStrings.C2Opcode })]
|
||||
public void ParametersFromOptionsTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoid, int rereadC2, int? subchannelLevel, string[] expected)
|
||||
[InlineData(RedumpSystem.AppleMacintosh, MediaType.LaserDisc, null)] // Deliberately unsupported
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, new string[] { FlagStrings.Raw })]
|
||||
public void ParametersFromOptionsSpecialDefaultTest(RedumpSystem? knownSystem, MediaType? mediaType,string[] expected)
|
||||
{
|
||||
var options = new Options { DICParanoidMode = paranoid, DICRereadCount = rereadC2 };
|
||||
var options = new Options();
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
HashSet<string> actualSet = new HashSet<string>(actual.Keys ?? new string[0]);
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, 1000, new string[] { FlagStrings.C2Opcode })]
|
||||
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, -1, new string[] { FlagStrings.C2Opcode })]
|
||||
public void ParametersFromOptionsC2RereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadC2, string[] expected)
|
||||
{
|
||||
var options = new Options { DICRereadCount = rereadC2 };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (rereadC2 == -1 || !knownSystem.MediaTypes().Contains(mediaType))
|
||||
Assert.Null(actual.C2OpcodeValue[0]);
|
||||
else
|
||||
Assert.Equal(rereadC2, actual.C2OpcodeValue[0]);
|
||||
Assert.Equal(subchannelLevel, actual.SubchannelReadLevelValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, 1000, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, -1, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.BluRay, 1000, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.BluRay, -1, new string[] { FlagStrings.DVDReread })]
|
||||
public void ParametersFromOptionsDVDRereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadDVDBD, string[] expected)
|
||||
{
|
||||
var options = new Options { DICDVDRereadCount = rereadDVDBD };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (rereadDVDBD == -1 || !knownSystem.MediaTypes().Contains(mediaType))
|
||||
Assert.Null(actual.DVDRereadValue);
|
||||
else
|
||||
Assert.Equal(rereadDVDBD, actual.DVDRereadValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, true, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, false, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.MultiSectorRead, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, false, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, new string[] { FlagStrings.DVDReread })]
|
||||
public void ParametersFromOptionsMultiSectorReadTest(RedumpSystem? knownSystem, MediaType? mediaType, bool multiSectorRead, string[] expected)
|
||||
{
|
||||
var options = new Options { DICMultiSectorRead = multiSectorRead };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (expectedSet.Count != 1 && multiSectorRead)
|
||||
Assert.Equal(0, actual.MultiSectorReadValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, true, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, false, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, false, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, new string[] { FlagStrings.DVDReread, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, new string[] { FlagStrings.DVDReread })]
|
||||
public void ParametersFromOptionsParanoidModeTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoidMode, string[] expected)
|
||||
{
|
||||
var options = new Options { DICParanoidMode = paranoidMode };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (paranoidMode)
|
||||
{
|
||||
if (actualSet.Contains(FlagStrings.ScanSectorProtect))
|
||||
Assert.True(actual[FlagStrings.ScanSectorProtect]);
|
||||
|
||||
if (actualSet.Contains(FlagStrings.SubchannelReadLevel))
|
||||
{
|
||||
Assert.True(actual[FlagStrings.SubchannelReadLevel]);
|
||||
Assert.Equal(2, actual.SubchannelReadLevelValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (actualSet.Contains(FlagStrings.ScanSectorProtect))
|
||||
Assert.False(actual[FlagStrings.ScanSectorProtect]);
|
||||
|
||||
if (actualSet.Contains(FlagStrings.SubchannelReadLevel))
|
||||
Assert.False(actual[FlagStrings.SubchannelReadLevel]);
|
||||
|
||||
Assert.Null(actual.SubchannelReadLevelValue);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -78,7 +162,7 @@ namespace MPF.Test.Modules
|
||||
[InlineData(MediaType.NONE, null)]
|
||||
public void MediaTypeToExtensionTest(MediaType? mediaType, string expected)
|
||||
{
|
||||
string actual = MPF.Modules.DiscImageCreator.Converters.Extension(mediaType);
|
||||
string actual = Converters.Extension(mediaType);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
@@ -102,7 +186,7 @@ namespace MPF.Test.Modules
|
||||
[InlineData(CommandStrings.XBOX, MediaType.DVD)]
|
||||
public void BaseCommandToMediaTypeTest(string command, MediaType? expected)
|
||||
{
|
||||
MediaType? actual = MPF.Modules.DiscImageCreator.Converters.ToMediaType(command);
|
||||
MediaType? actual = Converters.ToMediaType(command);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
@@ -126,7 +210,7 @@ namespace MPF.Test.Modules
|
||||
[InlineData(CommandStrings.XBOX, RedumpSystem.MicrosoftXbox)]
|
||||
public void BaseCommandToRedumpSystemTest(string command, RedumpSystem? expected)
|
||||
{
|
||||
RedumpSystem? actual = MPF.Modules.DiscImageCreator.Converters.ToRedumpSystem(command);
|
||||
RedumpSystem? actual = Converters.ToRedumpSystem(command);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
@@ -161,5 +245,25 @@ namespace MPF.Test.Modules
|
||||
Assert.NotNull(newParameters);
|
||||
Assert.Equal(originalParameters, newParameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a HashSet of keys that are considered to be set
|
||||
/// </summary>
|
||||
/// <param name="parameters">Parameters object to get keys from</param>
|
||||
/// <returns>HashSet representing the strings</returns>
|
||||
private static HashSet<string> GenerateUsedKeys(Parameters parameters)
|
||||
{
|
||||
HashSet<string> usedKeys = new HashSet<string>();
|
||||
if (parameters?.Keys == null)
|
||||
return usedKeys;
|
||||
|
||||
foreach (string key in parameters.Keys)
|
||||
{
|
||||
if (parameters[key] == true)
|
||||
usedKeys.Add(key);
|
||||
}
|
||||
|
||||
return usedKeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using RedumpLib.Data;
|
||||
using Xunit;
|
||||
@@ -17,12 +18,17 @@ namespace MPF.Test.RedumpLib
|
||||
private static readonly DiscType?[] _mappableDiscTypes = new DiscType?[]
|
||||
{
|
||||
DiscType.BD25,
|
||||
//DiscType.BD33,
|
||||
DiscType.BD50,
|
||||
//DiscType.BD66,
|
||||
DiscType.BD100,
|
||||
DiscType.BD128,
|
||||
DiscType.CD,
|
||||
DiscType.DVD5,
|
||||
DiscType.DVD9,
|
||||
DiscType.GDROM,
|
||||
DiscType.HDDVDSL,
|
||||
DiscType.HDDVDDL,
|
||||
DiscType.NintendoGameCubeGameDisc,
|
||||
DiscType.NintendoWiiOpticalDiscSL,
|
||||
DiscType.NintendoWiiOpticalDiscDL,
|
||||
@@ -250,6 +256,87 @@ namespace MPF.Test.RedumpLib
|
||||
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)
|
||||
{
|
||||
string code = language.TwoLetterCode();
|
||||
if (string.IsNullOrWhiteSpace(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)
|
||||
{
|
||||
string code = language.ThreeLetterCode();
|
||||
if (string.IsNullOrWhiteSpace(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)
|
||||
{
|
||||
string code = language.ThreeLetterCodeAlt();
|
||||
if (string.IsNullOrWhiteSpace(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>
|
||||
@@ -392,6 +479,33 @@ namespace MPF.Test.RedumpLib
|
||||
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)
|
||||
{
|
||||
string code = region.ShortName();
|
||||
if (string.IsNullOrWhiteSpace(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>
|
||||
@@ -409,18 +523,60 @@ namespace MPF.Test.RedumpLib
|
||||
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
#region Site Code
|
||||
|
||||
/// <summary>
|
||||
/// RedumpSystem values that are considered markers and not real systems
|
||||
/// Check that every SiteCode has a long name provided
|
||||
/// </summary>
|
||||
private static readonly RedumpSystem?[] _markerSystemTypes = new RedumpSystem?[]
|
||||
/// <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)
|
||||
{
|
||||
RedumpSystem.MarkerArcadeEnd,
|
||||
RedumpSystem.MarkerComputerEnd,
|
||||
RedumpSystem.MarkerDiscBasedConsoleEnd,
|
||||
RedumpSystem.MarkerOtherEnd,
|
||||
};
|
||||
string 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)
|
||||
{
|
||||
string 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
|
||||
@@ -472,7 +628,7 @@ namespace MPF.Test.RedumpLib
|
||||
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
// We want to skip all markers for this
|
||||
if (_markerSystemTypes.Contains(redumpSystem))
|
||||
if (redumpSystem.IsMarker())
|
||||
continue;
|
||||
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
|
||||
@@ -42,7 +42,8 @@ namespace MPF.Test.RedumpLib
|
||||
var submissionInfo = new SubmissionInfo()
|
||||
{
|
||||
SchemaVersion = 1,
|
||||
MatchedIDs = new List<int> { 0, 1, 2, 3 },
|
||||
FullyMatchedID = 3,
|
||||
PartiallyMatchedIDs = new List<int> { 0, 1, 2, 3 },
|
||||
Added = DateTime.UtcNow,
|
||||
LastModified = DateTime.UtcNow,
|
||||
|
||||
@@ -79,8 +80,16 @@ namespace MPF.Test.RedumpLib
|
||||
Barcode = "UPC Barcode",
|
||||
EXEDateBuildDate = "19xx-xx-xx",
|
||||
ErrorsCount = "0",
|
||||
Comments = "[T:ISBN] ISBN\r\nComment data",
|
||||
Contents = "Demos:\r\n- Game Demo 1",
|
||||
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()
|
||||
@@ -124,7 +133,7 @@ namespace MPF.Test.RedumpLib
|
||||
|
||||
DumpersAndStatus = new DumpersAndStatusSection()
|
||||
{
|
||||
Status = DumpStatus.TwoOrMoHumanReadablesGreen,
|
||||
Status = DumpStatus.TwoOrMoreGreen,
|
||||
Dumpers = new string[] { "Dumper1", "Dumper2" },
|
||||
OtherDumpers = "Dumper3",
|
||||
},
|
||||
@@ -148,6 +157,15 @@ namespace MPF.Test.RedumpLib
|
||||
SHA1 = "SHA1",
|
||||
},
|
||||
|
||||
DumpingInfo = new DumpingInfoSection()
|
||||
{
|
||||
DumpingProgram = "DiscImageCreator 20500101",
|
||||
Manufacturer = "ATAPI",
|
||||
Model = "Optical Drive",
|
||||
Firmware = "1.23",
|
||||
ReportedDiscType = "CD-R",
|
||||
},
|
||||
|
||||
Artifacts = new Dictionary<string, string>()
|
||||
{
|
||||
["Sample Artifact"] = "Sample Data",
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
|
||||
namespace MPF
|
||||
namespace MPF.UI.Core.ComboBoxItems
|
||||
{
|
||||
/// <summary>
|
||||
/// A generic combo box element
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF
|
||||
namespace MPF.UI.Core.ComboBoxItems
|
||||
{
|
||||
public interface IElement
|
||||
{
|
||||
|
Before Width: | Height: | Size: 423 KiB After Width: | Height: | Size: 423 KiB |
BIN
MPF.UI.Core/Images/ring-code-guide-2-layer.png
Normal file
BIN
MPF.UI.Core/Images/ring-code-guide-2-layer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
62
MPF.UI.Core/MPF.UI.Core.csproj
Normal file
62
MPF.UI.Core/MPF.UI.Core.csproj
Normal file
@@ -0,0 +1,62 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win-x86;win-x64</RuntimeIdentifiers>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
<Title>MPF.UI.Core</Title>
|
||||
<AssemblyName>MPF.UI.Core</AssemblyName>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<Version>2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
<UserSecretsId>27abb4ca-bf7a-431e-932f-49153303d5ff</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\ring-code-guide-1-layer.png" />
|
||||
<Resource Include="Images\ring-code-guide-2-layer.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationFramework.Aero" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Images\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="UserControls\UserInput.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="Windows\DiscInformationWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="Windows\RingCodeGuideWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
<UserControl x:Class="MPF.UserControls.UserInput"
|
||||
<UserControl x:Class="MPF.UI.Core.UserControls.UserInput"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
@@ -6,7 +6,7 @@
|
||||
d:DesignHeight="450" d:DesignWidth="800" x:Name="userInput">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<ColumnDefinition Width="0.75*" />
|
||||
<ColumnDefinition Width="1.25*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
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}"
|
||||
VerticalScrollBarVisibility="{Binding ElementName=userInput, Path=ScrollBarVisibility}"/>
|
||||
HorizontalScrollBarVisibility="{Binding ElementName=userInput, Path=HorizontalScrollBarVisibility}"
|
||||
VerticalScrollBarVisibility="{Binding ElementName=userInput, Path=VerticalScrollBarVisibility}"/>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace MPF.UserControls
|
||||
namespace MPF.UI.Core.UserControls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for UserInput.xaml
|
||||
@@ -28,11 +28,17 @@ namespace MPF.UserControls
|
||||
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 ScrollBarVisibilityProperty =
|
||||
DependencyProperty.Register("ScrollBarVisibility", typeof(ScrollBarVisibility), 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
|
||||
|
||||
@@ -74,21 +80,32 @@ namespace MPF.UserControls
|
||||
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 ScrollBarVisibility
|
||||
public ScrollBarVisibility HorizontalScrollBarVisibility
|
||||
{
|
||||
get => (ScrollBarVisibility)GetValue(ScrollBarVisibilityProperty);
|
||||
set => SetValue(ScrollBarVisibilityProperty, value);
|
||||
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
|
||||
@@ -96,8 +113,10 @@ namespace MPF.UserControls
|
||||
Tab = false;
|
||||
Enter = false;
|
||||
TextWrapping = TextWrapping.NoWrap;
|
||||
IsReadOnly = false;
|
||||
VerticalContentAlignmentValue = VerticalAlignment.Center;
|
||||
ScrollBarVisibility = ScrollBarVisibility.Auto;
|
||||
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
|
||||
VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
|
||||
|
||||
InitializeComponent();
|
||||
}
|
||||
839
MPF.UI.Core/ViewModels/DiscInformationViewModel.cs
Normal file
839
MPF.UI.Core/ViewModels/DiscInformationViewModel.cs
Normal file
@@ -0,0 +1,839 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.UI.Core.ComboBoxItems;
|
||||
using MPF.UI.Core.Windows;
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace MPF.UI.Core.ViewModels
|
||||
{
|
||||
public class DiscInformationViewModel
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Parent DiscInformationWindow object
|
||||
/// </summary>
|
||||
public DiscInformationWindow Parent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Application-level Options object
|
||||
/// </summary>
|
||||
public Options Options { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SubmissionInfo object to fill and save
|
||||
/// </summary>
|
||||
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 List<Region>
|
||||
{
|
||||
Region.Argentina,
|
||||
Region.Asia,
|
||||
Region.AsiaEurope,
|
||||
Region.AsiaUSA,
|
||||
Region.Australia,
|
||||
Region.AustraliaGermany,
|
||||
Region.AustraliaNewZealand,
|
||||
Region.Austria,
|
||||
Region.AustriaSwitzerland,
|
||||
Region.Belarus,
|
||||
Region.Belgium,
|
||||
Region.BelgiumNetherlands,
|
||||
Region.Brazil,
|
||||
Region.Bulgaria,
|
||||
Region.Canada,
|
||||
Region.China,
|
||||
Region.Croatia,
|
||||
Region.Czechia,
|
||||
Region.Denmark,
|
||||
Region.Estonia,
|
||||
Region.Europe,
|
||||
Region.EuropeAsia,
|
||||
Region.EuropeAustralia,
|
||||
Region.EuropeCanada,
|
||||
Region.EuropeGermany,
|
||||
Region.Export,
|
||||
Region.Finland,
|
||||
Region.France,
|
||||
Region.FranceSpain,
|
||||
Region.Germany,
|
||||
Region.GreaterChina,
|
||||
Region.Greece,
|
||||
Region.Hungary,
|
||||
Region.Iceland,
|
||||
Region.India,
|
||||
Region.Ireland,
|
||||
Region.Israel,
|
||||
Region.Italy,
|
||||
Region.Japan,
|
||||
Region.JapanAsia,
|
||||
Region.JapanEurope,
|
||||
Region.JapanKorea,
|
||||
Region.JapanUSA,
|
||||
Region.SouthKorea,
|
||||
Region.LatinAmerica,
|
||||
Region.Lithuania,
|
||||
Region.Netherlands,
|
||||
Region.NewZealand,
|
||||
Region.Norway,
|
||||
Region.Poland,
|
||||
Region.Portugal,
|
||||
Region.Romania,
|
||||
Region.RussianFederation,
|
||||
Region.Scandinavia,
|
||||
Region.Serbia,
|
||||
Region.Singapore,
|
||||
Region.Slovakia,
|
||||
Region.SouthAfrica,
|
||||
Region.Spain,
|
||||
Region.SpainPortugal,
|
||||
Region.Sweden,
|
||||
Region.Switzerland,
|
||||
Region.Taiwan,
|
||||
Region.Thailand,
|
||||
Region.Turkey,
|
||||
Region.UnitedArabEmirates,
|
||||
Region.UnitedKingdom,
|
||||
Region.UKAustralia,
|
||||
Region.Ukraine,
|
||||
Region.UnitedStatesOfAmerica,
|
||||
Region.USAAsia,
|
||||
Region.USAAustralia,
|
||||
Region.USABrazil,
|
||||
Region.USACanada,
|
||||
Region.USAEurope,
|
||||
Region.USAGermany,
|
||||
Region.USAJapan,
|
||||
Region.USAKorea,
|
||||
Region.World,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// List of available languages
|
||||
/// </summary>
|
||||
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of Redump-supported Languages
|
||||
/// </summary>
|
||||
private static readonly List<Language> RedumpLanguages = new List<Language>
|
||||
{
|
||||
Language.Afrikaans,
|
||||
Language.Albanian,
|
||||
Language.Arabic,
|
||||
Language.Armenian,
|
||||
Language.Basque,
|
||||
Language.Belarusian,
|
||||
Language.Bulgarian,
|
||||
Language.Catalan,
|
||||
Language.Chinese,
|
||||
Language.Croatian,
|
||||
Language.Czech,
|
||||
Language.Danish,
|
||||
Language.Dutch,
|
||||
Language.English,
|
||||
Language.Estonian,
|
||||
Language.Finnish,
|
||||
Language.French,
|
||||
Language.Gaelic,
|
||||
Language.German,
|
||||
Language.Greek,
|
||||
Language.Hebrew,
|
||||
Language.Hindi,
|
||||
Language.Hungarian,
|
||||
Language.Icelandic,
|
||||
Language.Indonesian,
|
||||
Language.Italian,
|
||||
Language.Japanese,
|
||||
Language.Korean,
|
||||
Language.Latin,
|
||||
Language.Latvian,
|
||||
Language.Lithuanian,
|
||||
Language.Macedonian,
|
||||
Language.Norwegian,
|
||||
Language.Polish,
|
||||
Language.Portuguese,
|
||||
Language.Panjabi,
|
||||
Language.Romanian,
|
||||
Language.Russian,
|
||||
Language.Serbian,
|
||||
Language.Slovak,
|
||||
Language.Slovenian,
|
||||
Language.Spanish,
|
||||
Language.Swedish,
|
||||
Language.Tamil,
|
||||
Language.Thai,
|
||||
Language.Turkish,
|
||||
Language.Ukrainian,
|
||||
Language.Vietnamese,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// List of available languages
|
||||
/// </summary>
|
||||
public List<Element<LanguageSelection>> LanguageSelections { get; private set; } = Element<LanguageSelection>.GenerateElements().ToList();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public DiscInformationViewModel(DiscInformationWindow parent, Options options, SubmissionInfo submissionInfo)
|
||||
{
|
||||
Parent = parent;
|
||||
Options = options;
|
||||
SubmissionInfo = submissionInfo.Clone() as SubmissionInfo ?? new SubmissionInfo();
|
||||
|
||||
// Limit lists, if necessary
|
||||
if (this.Options.EnableRedumpCompatibility)
|
||||
{
|
||||
SetRedumpRegions();
|
||||
SetRedumpLanguages();
|
||||
}
|
||||
|
||||
// Add handlers
|
||||
Parent.AcceptButton.Click += OnAcceptClick;
|
||||
Parent.CancelButton.Click += OnCancelClick;
|
||||
Parent.RingCodeGuideButton.Click += OnRingCodeGuideClick;
|
||||
|
||||
// Update UI with new values
|
||||
ManipulateFields();
|
||||
Load();
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Enable tab entry on supported fields
|
||||
/// </summary>
|
||||
private void EnableTabsInInputFields()
|
||||
{
|
||||
// Additional Information
|
||||
Parent.CommentsTextBox.Tab = true;
|
||||
|
||||
// Contents
|
||||
Parent.GeneralContent.Tab = true;
|
||||
Parent.GamesTextBox.Tab = true;
|
||||
Parent.NetYarozeGamesTextBox.Tab = true;
|
||||
Parent.PlayableDemosTextBox.Tab = true;
|
||||
Parent.RollingDemosTextBox.Tab = true;
|
||||
Parent.TechDemosTextBox.Tab = true;
|
||||
Parent.GameFootageTextBox.Tab = true;
|
||||
Parent.VideosTextBox.Tab = true;
|
||||
Parent.PatchesTextBox.Tab = true;
|
||||
Parent.SavegamesTextBox.Tab = true;
|
||||
Parent.ExtrasTextBox.Tab = true;
|
||||
|
||||
// L0
|
||||
Parent.L0MasteringRing.Tab = true;
|
||||
Parent.L0MasteringSID.Tab = true;
|
||||
Parent.L0Toolstamp.Tab = true;
|
||||
Parent.L0MouldSID.Tab = true;
|
||||
Parent.L0AdditionalMould.Tab = true;
|
||||
|
||||
// L1
|
||||
Parent.L1MasteringRing.Tab = true;
|
||||
Parent.L1MasteringSID.Tab = true;
|
||||
Parent.L1Toolstamp.Tab = true;
|
||||
Parent.L1MouldSID.Tab = true;
|
||||
Parent.L1AdditionalMould.Tab = true;
|
||||
|
||||
// L2
|
||||
Parent.L2MasteringRing.Tab = true;
|
||||
Parent.L2MasteringSID.Tab = true;
|
||||
Parent.L2Toolstamp.Tab = true;
|
||||
|
||||
// L3
|
||||
Parent.L3MasteringRing.Tab = true;
|
||||
Parent.L3MasteringSID.Tab = true;
|
||||
Parent.L3Toolstamp.Tab = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide any optional, read-only fields if they don't have a value
|
||||
/// </summary>
|
||||
private void HideReadOnlyFields()
|
||||
{
|
||||
if (SubmissionInfo?.FullyMatchedID == null)
|
||||
Parent.FullyMatchedID.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.FullyMatchedID.Text = SubmissionInfo.FullyMatchedID.ToString();
|
||||
if (SubmissionInfo?.PartiallyMatchedIDs == null)
|
||||
Parent.PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.PartiallyMatchedIDs.Text = string.Join(", ", SubmissionInfo.PartiallyMatchedIDs);
|
||||
if (SubmissionInfo?.CopyProtection?.AntiModchip == null)
|
||||
Parent.AntiModchip.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.AntiModchip.Text = SubmissionInfo.CopyProtection.AntiModchip.LongName();
|
||||
if (SubmissionInfo?.TracksAndWriteOffsets?.OtherWriteOffsets == null)
|
||||
Parent.DiscOffset.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.DiscOffset.Text = SubmissionInfo.TracksAndWriteOffsets.OtherWriteOffsets;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.DMIHash) != true)
|
||||
Parent.DMIHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CommonDiscInfo?.ErrorsCount))
|
||||
Parent.ErrorsCount.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CommonDiscInfo?.EXEDateBuildDate))
|
||||
Parent.EXEDateBuildDate.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.EDC?.EDC == null)
|
||||
Parent.EDC.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.EDC.Text = SubmissionInfo.EDC.EDC.LongName();
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Filename) != true)
|
||||
Parent.Filename.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.Header))
|
||||
Parent.Header.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.InternalName) != true)
|
||||
Parent.InternalName.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.InternalSerialName) != true)
|
||||
Parent.InternalSerialName.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CopyProtection?.LibCrypt == null)
|
||||
Parent.LibCrypt.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.LibCrypt.Text = SubmissionInfo.CopyProtection.LibCrypt.LongName();
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.LibCryptData))
|
||||
Parent.LibCryptData.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.PFIHash) != true)
|
||||
Parent.PFIHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.PIC))
|
||||
Parent.PIC.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.PVD))
|
||||
Parent.PVD.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Multisession) != true)
|
||||
Parent.Multisession.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.RingNonZeroDataStart) != true)
|
||||
Parent.RingNonZeroDataStart.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.SecuROMData))
|
||||
Parent.SecuROMData.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSHash) != true)
|
||||
Parent.SSHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.SecuritySectorRanges))
|
||||
Parent.SecuritySectorRanges.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSVersion) != true)
|
||||
Parent.SSVersion.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.UniversalHash) != true)
|
||||
Parent.UniversalHash.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.VolumeLabel) != true)
|
||||
Parent.VolumeLabel.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.XeMID) != true)
|
||||
Parent.XeMID.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.XMID) != true)
|
||||
Parent.XMID.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manipulate fields based on the current disc
|
||||
/// </summary>
|
||||
private void ManipulateFields()
|
||||
{
|
||||
// Enable tabs in all fields, if required
|
||||
if (this.Options.EnableTabsInInputFields)
|
||||
EnableTabsInInputFields();
|
||||
|
||||
// Hide read-only fields that don't have values set
|
||||
HideReadOnlyFields();
|
||||
|
||||
// Different media types mean different fields available
|
||||
UpdateFromDiscType();
|
||||
|
||||
// Different systems mean different fields available
|
||||
UpdateFromSystemType();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the current contents of the base SubmissionInfo to the UI
|
||||
/// </summary>
|
||||
private void Load()
|
||||
{
|
||||
Parent.CategoryComboBox.SelectedIndex = Categories.FindIndex(r => r == SubmissionInfo.CommonDiscInfo.Category);
|
||||
Parent.RegionComboBox.SelectedIndex = Regions.FindIndex(r => r == SubmissionInfo.CommonDiscInfo.Region);
|
||||
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));
|
||||
|
||||
// TODO: Figure out if this can be automatically mapped instead
|
||||
|
||||
// Comment Fields
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields != null)
|
||||
{
|
||||
// Additional Information
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.AlternativeTitle))
|
||||
Parent.AlternativeTitleTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeTitle];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.AlternativeForeignTitle))
|
||||
Parent.AlternativeForeignTitleTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeForeignTitle];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Genre))
|
||||
Parent.GenreTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Genre];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PostgapType))
|
||||
Parent.PostgapTypeCheckBox.IsChecked = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PostgapType] != null;
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Series))
|
||||
Parent.SeriesTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Series];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VCD))
|
||||
Parent.VCDCheckBox.IsChecked = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VCD] != null;
|
||||
|
||||
// Physical Identifiers
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.BBFCRegistrationNumber))
|
||||
Parent.BBFCRegistrationNumberTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BBFCRegistrationNumber];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DiscHologramID))
|
||||
Parent.DiscHologramIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DiscHologramID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DNASDiscID))
|
||||
Parent.DNASDiscIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DNASDiscID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ISBN))
|
||||
Parent.ISBNTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISBN];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ISSN))
|
||||
Parent.ISSNTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISSN];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PPN))
|
||||
Parent.PPNTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PPN];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VFCCode))
|
||||
Parent.VFCCodeTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VFCCode];
|
||||
|
||||
// Publisher Identifiers
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.AcclaimID))
|
||||
Parent.AcclaimIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AcclaimID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ActivisionID))
|
||||
Parent.ActivisionIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ActivisionID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.BandaiID))
|
||||
Parent.BandaiIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BandaiID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ElectronicArtsID))
|
||||
Parent.ElectronicArtsIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ElectronicArtsID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.FoxInteractiveID))
|
||||
Parent.FoxInteractiveIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.FoxInteractiveID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.GTInteractiveID))
|
||||
Parent.GTInteractiveIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.GTInteractiveID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.JASRACID))
|
||||
Parent.JASRACIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.JASRACID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.KingRecordsID))
|
||||
Parent.KingRecordsIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KingRecordsID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.KoeiID))
|
||||
Parent.KoeiIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KoeiID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.KonamiID))
|
||||
Parent.KonamiIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KonamiID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.LucasArtsID))
|
||||
Parent.LucasArtsIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.LucasArtsID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.MicrosoftID))
|
||||
Parent.MicrosoftIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.MicrosoftID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.NaganoID))
|
||||
Parent.NaganoIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NaganoID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.NamcoID))
|
||||
Parent.NamcoIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NamcoID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.NipponIchiSoftwareID))
|
||||
Parent.NipponIchiSoftwareIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NipponIchiSoftwareID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.OriginID))
|
||||
Parent.OriginIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.OriginID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PonyCanyonID))
|
||||
Parent.PonyCanyonIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PonyCanyonID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SegaID))
|
||||
Parent.SegaIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SegaID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SelenID))
|
||||
Parent.SelenIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SelenID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SierraID))
|
||||
Parent.SierraIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SierraID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.TaitoID))
|
||||
Parent.TaitoIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.TaitoID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.UbisoftID))
|
||||
Parent.UbisoftIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UbisoftID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ValveID))
|
||||
Parent.ValveIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ValveID];
|
||||
|
||||
// Read-Only Information
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DMIHash))
|
||||
Parent.DMIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Filename))
|
||||
Parent.Filename.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalName))
|
||||
Parent.InternalName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalSerialName))
|
||||
Parent.InternalSerialName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Multisession))
|
||||
Parent.Multisession.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PFIHash))
|
||||
Parent.PFIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.RingNonZeroDataStart))
|
||||
Parent.RingNonZeroDataStart.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSHash))
|
||||
Parent.SSHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSVersion))
|
||||
Parent.SSVersion.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.UniversalHash))
|
||||
Parent.UniversalHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VolumeLabel))
|
||||
Parent.VolumeLabel.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.XeMID))
|
||||
Parent.XeMID.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.XMID))
|
||||
Parent.XMID.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID];
|
||||
}
|
||||
|
||||
// Content Fields
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields != null)
|
||||
{
|
||||
// Games
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Games))
|
||||
Parent.GamesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Games];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.NetYarozeGames))
|
||||
Parent.NetYarozeGamesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.NetYarozeGames];
|
||||
|
||||
// Demos
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.PlayableDemos))
|
||||
Parent.PlayableDemosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.PlayableDemos];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.RollingDemos))
|
||||
Parent.RollingDemosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.RollingDemos];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.TechDemos))
|
||||
Parent.TechDemosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.TechDemos];
|
||||
|
||||
// Video
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.GameFootage))
|
||||
Parent.GameFootageTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.GameFootage];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Videos))
|
||||
Parent.VideosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Videos];
|
||||
|
||||
// Miscellaneous
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Patches))
|
||||
Parent.PatchesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Patches];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Savegames))
|
||||
Parent.SavegamesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Savegames];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Extras))
|
||||
Parent.ExtrasTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Extras];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current contents of the UI to the base SubmissionInfo
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
SubmissionInfo.CommonDiscInfo.Category = (Parent.CategoryComboBox.SelectedItem as Element<DiscCategory>)?.Value ?? DiscCategory.Games;
|
||||
SubmissionInfo.CommonDiscInfo.Region = (Parent.RegionComboBox.SelectedItem as Element<Region>)?.Value ?? Region.World;
|
||||
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
|
||||
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
|
||||
SubmissionInfo.CommonDiscInfo.Languages = new Language?[] { null };
|
||||
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
|
||||
|
||||
// TODO: Figure out if this can be automatically mapped instead
|
||||
|
||||
// Initialize the dictionaries, if needed
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields == null)
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields == null)
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields = new Dictionary<SiteCode?, string>();
|
||||
|
||||
#region Comment Fields
|
||||
|
||||
// Additional Information
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeTitle] = Parent.AlternativeTitleTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeForeignTitle] = Parent.AlternativeForeignTitleTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Genre] = Parent.GenreTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PostgapType] = Parent.PostgapTypeCheckBox.IsChecked?.ToString();
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Series] = Parent.SeriesTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VCD] = Parent.VCDCheckBox.IsChecked?.ToString();
|
||||
|
||||
// Physical Identifiers
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BBFCRegistrationNumber] = Parent.BBFCRegistrationNumberTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DiscHologramID] = Parent.DiscHologramIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DNASDiscID] = Parent.DNASDiscIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISBN] = Parent.ISBNTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISSN] = Parent.ISSNTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PPN] = Parent.PPNTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VFCCode] = Parent.VFCCodeTextBox.Text;
|
||||
|
||||
// Publisher Identifiers
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AcclaimID] = Parent.AcclaimIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ActivisionID] = Parent.ActivisionIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BandaiID] = Parent.BandaiIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ElectronicArtsID] = Parent.ElectronicArtsIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.FoxInteractiveID] = Parent.FoxInteractiveIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.GTInteractiveID] = Parent.GTInteractiveIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.JASRACID] = Parent.JASRACIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KingRecordsID] = Parent.KingRecordsIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KoeiID] = Parent.KoeiIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KonamiID] = Parent.KonamiIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.LucasArtsID] = Parent.LucasArtsIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.MicrosoftID] = Parent.MicrosoftIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NaganoID] = Parent.NaganoIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NamcoID] = Parent.NamcoIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NipponIchiSoftwareID] = Parent.NipponIchiSoftwareIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.OriginID] = Parent.OriginIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PonyCanyonID] = Parent.PonyCanyonIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SegaID] = Parent.SegaIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SelenID] = Parent.SelenIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SierraID] = Parent.SierraIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.TaitoID] = Parent.TaitoIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UbisoftID] = Parent.UbisoftIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ValveID] = Parent.ValveIDTextBox.Text;
|
||||
|
||||
// Read-Only Information
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = Parent.DMIHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename] = Parent.Filename.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = Parent.InternalName.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = Parent.InternalSerialName.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession] = Parent.Multisession.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = Parent.PFIHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart] = Parent.RingNonZeroDataStart.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = Parent.SSHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = Parent.SSVersion.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash] = Parent.UniversalHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel] = Parent.VolumeLabel.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = Parent.XeMID.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = Parent.XMID.Text;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content Fields
|
||||
|
||||
// Games
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Games] = Parent.GamesTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.NetYarozeGames] = Parent.NetYarozeGamesTextBox.Text;
|
||||
|
||||
// Demos
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.PlayableDemos] = Parent.PlayableDemosTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.RollingDemos] = Parent.RollingDemosTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.TechDemos] = Parent.TechDemosTextBox.Text;
|
||||
|
||||
// Video
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.GameFootage] = Parent.GameFootageTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Videos] = Parent.VideosTextBox.Text;
|
||||
|
||||
// Miscellaneous
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Patches] = Parent.PatchesTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Savegames] = Parent.SavegamesTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Extras] = Parent.ExtrasTextBox.Text;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulate the list of Languages based on Redump support
|
||||
/// </summary>
|
||||
private void SetRedumpLanguages()
|
||||
{
|
||||
this.Languages = RedumpLanguages.Select(l => new Element<Language>(l)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulate the list of Regions based on Redump support
|
||||
/// </summary>
|
||||
private void SetRedumpRegions()
|
||||
{
|
||||
this.Regions = RedumpRegions.Select(r => new Element<Region>(r)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visible fields and sections based on the media type
|
||||
/// </summary>
|
||||
private void UpdateFromDiscType()
|
||||
{
|
||||
// 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:
|
||||
Parent.L0Info.Header = "Data Side";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Label Side";
|
||||
Parent.L1MasteringRing.Visibility = Visibility.Collapsed;
|
||||
Parent.L1MasteringSID.Visibility = Visibility.Collapsed;
|
||||
Parent.L1Toolstamp.Visibility = Visibility.Collapsed;
|
||||
Parent.L1MouldSID.Label = "Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Additional Mould";
|
||||
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:
|
||||
// Quad-layer discs
|
||||
if (SubmissionInfo?.SizeAndChecksums?.Layerbreak3 != default(long))
|
||||
{
|
||||
Parent.L2Info.Visibility = Visibility.Visible;
|
||||
Parent.L3Info.Visibility = Visibility.Visible;
|
||||
|
||||
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Data Side Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Layer 1";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Label Side Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
|
||||
Parent.L2Info.Header = "Layer 2";
|
||||
Parent.L2MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L2MasteringSID.Label = "Mastering SID";
|
||||
Parent.L2Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
|
||||
Parent.L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
|
||||
Parent.L3MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L3MasteringSID.Label = "Mastering SID";
|
||||
Parent.L3Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
}
|
||||
|
||||
// Triple-layer discs
|
||||
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak2 != default(long))
|
||||
{
|
||||
Parent.L2Info.Visibility = Visibility.Visible;
|
||||
|
||||
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Data Side Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Layer 1";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Label Side Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
|
||||
Parent.L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
|
||||
Parent.L2MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L2MasteringSID.Label = "Mastering SID";
|
||||
Parent.L2Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
}
|
||||
|
||||
// Double-layer discs
|
||||
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
|
||||
{
|
||||
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Data Side Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Label Side Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
}
|
||||
|
||||
// Single-layer discs
|
||||
else
|
||||
{
|
||||
Parent.L0Info.Header = "Data Side";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Label Side";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Additional Mould";
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// All other media we assume to have no rings
|
||||
default:
|
||||
Parent.L0Info.Visibility = Visibility.Collapsed;
|
||||
Parent.L1Info.Visibility = Visibility.Collapsed;
|
||||
Parent.L2Info.Visibility = Visibility.Collapsed;
|
||||
Parent.L3Info.Visibility = Visibility.Collapsed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visible fields and sections based on the system type
|
||||
/// </summary>
|
||||
private void UpdateFromSystemType()
|
||||
{
|
||||
var system = SubmissionInfo?.CommonDiscInfo?.System;
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
Parent.LanguageSelectionGrid.Visibility = Visibility.Visible;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AcceptButton Click event
|
||||
/// </summary>
|
||||
private void OnAcceptClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Save();
|
||||
Parent.DialogResult = true;
|
||||
Parent.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CancelButton Click event
|
||||
/// </summary>
|
||||
private void OnCancelClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Parent.DialogResult = false;
|
||||
Parent.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for RingCodeGuideButton Click event
|
||||
/// </summary>
|
||||
private void OnRingCodeGuideClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var ringCodeGuideWindow = new RingCodeGuideWindow()
|
||||
{
|
||||
Owner = Parent,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
ringCodeGuideWindow.Show();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
405
MPF.UI.Core/Windows/DiscInformationWindow.xaml
Normal file
405
MPF.UI.Core/Windows/DiscInformationWindow.xaml
Normal file
@@ -0,0 +1,405 @@
|
||||
<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: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">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<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>
|
||||
<TextBlock TextAlignment="Center"><Bold>Disc Information</Bold></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}}"
|
||||
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"/>
|
||||
<controls:UserInput Label="Foreign Title (Non-Latin)"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.ForeignTitleNonLatin, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="AlternativeForeignTitleTextBox" Label="Alternative Foreign Title"/>
|
||||
<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}"
|
||||
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}"
|
||||
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="GenreTextBox" Label="Genre"/>
|
||||
<controls:UserInput x:Name="ProtectionTextBox" Label="Protection" ToolTip="CAUTION: Only edit if you know what you are doing!"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.Protection, Mode=TwoWay}" TextHeight="75" TextWrapping="Wrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="0.75*" />
|
||||
<ColumnDefinition Width="1.25*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label/>
|
||||
<CheckBox Grid.Column="1" x:Name="PostgapTypeCheckBox" Content="Postgap type (Form 2)" Margin="0,4"/>
|
||||
</Grid>
|
||||
<controls:UserInput x:Name="SeriesTextBox" Label="Series"/>
|
||||
<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"/>
|
||||
</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."/>
|
||||
<controls:UserInput x:Name="DiscHologramIDTextBox" Label="Disc Hologram ID"/>
|
||||
<controls:UserInput x:Name="DNASDiscIDTextBox" Label="DNAS Disc ID"/>
|
||||
<controls:UserInput x:Name="ISBNTextBox" Label="ISBN"/>
|
||||
<controls:UserInput x:Name="ISSNTextBox" Label="ISSN"/>
|
||||
<controls:UserInput x:Name="PPNTextBox" Label="PPN"/>
|
||||
<controls:UserInput x:Name="VFCCodeTextBox" Label="VFC code"/>
|
||||
</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"/>
|
||||
<controls:UserInput x:Name="ActivisionIDTextBox" Label="Activision ID"/>
|
||||
<controls:UserInput x:Name="BandaiIDTextBox" Label="Bandai ID"/>
|
||||
<controls:UserInput x:Name="ElectronicArtsIDTextBox" Label="Electronic Arts ID"/>
|
||||
<controls:UserInput x:Name="FoxInteractiveIDTextBox" Label="Fox Interactive ID"/>
|
||||
<controls:UserInput x:Name="GTInteractiveIDTextBox" Label="GT Interactive ID"/>
|
||||
<controls:UserInput x:Name="JASRACIDTextBox" Label="JASRAC ID"/>
|
||||
<controls:UserInput x:Name="KingRecordsIDTextBox" Label="King Records ID"/>
|
||||
<controls:UserInput x:Name="KoeiIDTextBox" Label="Koei ID"/>
|
||||
<controls:UserInput x:Name="KonamiIDTextBox" Label="Konami ID"/>
|
||||
<controls:UserInput x:Name="LucasArtsIDTextBox" Label="LucasArts ID"/>
|
||||
<controls:UserInput x:Name="MicrosoftIDTextBox" Label="Microsoft ID"/>
|
||||
<controls:UserInput x:Name="NaganoIDTextBox" Label="Nagano ID"/>
|
||||
<controls:UserInput x:Name="NamcoIDTextBox" Label="Namco ID"/>
|
||||
<controls:UserInput x:Name="NipponIchiSoftwareIDTextBox" Label="Nippon Ichi Software ID"/>
|
||||
<controls:UserInput x:Name="OriginIDTextBox" Label="Origin ID"/>
|
||||
<controls:UserInput x:Name="PonyCanyonIDTextBox" Label="Pony Canyon ID"/>
|
||||
<controls:UserInput x:Name="SegaIDTextBox" Label="Sega ID"/>
|
||||
<controls:UserInput x:Name="SelenIDTextBox" Label="Selen ID"/>
|
||||
<controls:UserInput x:Name="SierraIDTextBox" Label="Sierra ID"/>
|
||||
<controls:UserInput x:Name="TaitoIDTextBox" Label="Taito ID"/>
|
||||
<controls:UserInput x:Name="UbisoftIDTextBox" Label="Ubisoft ID"/>
|
||||
<controls:UserInput x:Name="ValveIDTextBox" Label="Valve ID"/>
|
||||
</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"/>
|
||||
<controls:UserInput x:Name="NetYarozeGamesTextBox" Label="Net Yaroze Games" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
|
||||
<!-- Demos -->
|
||||
<controls:UserInput x:Name="PlayableDemosTextBox" Label="Playable Demos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
<controls:UserInput x:Name="RollingDemosTextBox" Label="Rolling Demos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
<controls:UserInput x:Name="TechDemosTextBox" Label="Tech Demos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
|
||||
<!-- Video -->
|
||||
<controls:UserInput x:Name="GameFootageTextBox" Label="Game Footage" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
<controls:UserInput x:Name="VideosTextBox" Label="Videos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
|
||||
<!-- Miscellaneous -->
|
||||
<controls:UserInput x:Name="PatchesTextBox" Label="Patches" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
<controls:UserInput x:Name="SavegamesTextBox" Label="Savegames" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
<controls:UserInput x:Name="ExtrasTextBox" Label="Extras" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
</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"/>
|
||||
<controls:UserInput x:Name="PartiallyMatchedIDs" Label="Partially Matched ID(s)" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="DiscOffset" Label="Disc Offset" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="DMIHash" Label="DMI Hash" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="EDC" Label="EDC" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="ErrorsCount" Label="Error(s) Count" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.ErrorsCount, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="EXEDateBuildDate" Label="EXE/Build Date" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.EXEDateBuildDate, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="Filename" Label="Filename" IsReadOnly="True"
|
||||
TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="Header" Label="Header" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.Header, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="InternalName" Label="Internal Names" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="Multisession" Label="Multisession" IsReadOnly="True" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="LibCryptData" Label="LibCrypt Data" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.LibCryptData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="PFIHash" Label="PFI Hash" IsReadOnly="True"/>
|
||||
<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="Universal Hash (SHA-1)" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="SecuROMData" Label="SecuROM Data" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.SecuROMData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="SSHash" Label="Security Sector Hash" IsReadOnly="True"/>
|
||||
<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"/>
|
||||
<controls:UserInput x:Name="UniversalHash" Label="Universal Hash (SHA-1)" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="VolumeLabel" Label="Volume Label" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="XeMID" Label="XeMID" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="XMID" Label="XMID" IsReadOnly="True"/>
|
||||
|
||||
<!-- 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>
|
||||
@@ -1,7 +1,8 @@
|
||||
using MPF.GUI.ViewModels;
|
||||
using MPF.Core.Data;
|
||||
using MPF.UI.Core.ViewModels;
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace MPF.Windows
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DiscInformationWindow.xaml
|
||||
@@ -16,10 +17,10 @@ namespace MPF.Windows
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public DiscInformationWindow(SubmissionInfo submissionInfo)
|
||||
public DiscInformationWindow(Options options, SubmissionInfo submissionInfo)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new DiscInformationViewModel(this, submissionInfo);
|
||||
DataContext = new DiscInformationViewModel(this, options, submissionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
157
MPF.UI.Core/Windows/RingCodeGuideWindow.xaml
Normal file
157
MPF.UI.Core/Windows/RingCodeGuideWindow.xaml
Normal file
@@ -0,0 +1,157 @@
|
||||
<windows:WindowBase x:Class="MPF.UI.Core.Windows.RingCodeGuideWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
|
||||
xmlns:windows="clr-namespace:MPF.UI.Core.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Ring Code Guide" Width="500" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<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>
|
||||
<TextBlock TextAlignment="Center"><Bold>Ring Code Guide</Bold></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}}"
|
||||
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 Header="1-Layer Ringcode" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Image Grid.Row="0" Source="../Images/ring-code-guide-1-layer.png"
|
||||
HorizontalAlignment="Center" Stretch="Fill" Width="500" Height="500"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<Label Grid.Row="1">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Red">1. Mastering Ring:</Bold> Sony DADC<tab>A0100368905-0101<tab>13</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="2">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Green">2. Mastering SID:</Bold> IFPI L553</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="3">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Purple">3. Toolstamp/Mastering Code:</Bold> A2</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="4">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="LightBlue">4. Mould SID:</Bold> IFPI 94V1</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="2-Layer Ringcode" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Image Grid.Row="0" Source="../Images/ring-code-guide-2-layer.png"
|
||||
HorizontalAlignment="Center" Stretch="Fill" Width="475" Height="500"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<Label Grid.Row="1">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Red">1. Outer Mastering Ring:</Bold> IM01501A-L1<tab>03 +<tab>+</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="2">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Green">2. Inner Mastering Ring:</Bold> IM01501A-L0<tab>01 +<tab>+</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="3">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Purple">3. Outer Mastering SID:</Bold> IFPI LB48</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="4">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="LightBlue">4. Inner Mastering SID:</Bold> IFPI LB48</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="5">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold>Note:</Bold> See the 1-Layer guide for more details on additional fields</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</windows:WindowBase>
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF.Windows
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for RingCodeGuideWindow.xaml
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MPF.Windows
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
public class WindowBase : Window
|
||||
{
|
||||
31
MPF.sln
31
MPF.sln
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.28803.156
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32407.343
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF", "MPF\MPF.csproj", "{7B1B75EB-8940-466F-BD51-76471A57F9BE}"
|
||||
EndProject
|
||||
@@ -15,6 +15,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
appveyor.yml = appveyor.yml
|
||||
CHANGELIST.md = CHANGELIST.md
|
||||
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
|
||||
.github\ISSUE_TEMPLATE\informational.md = .github\ISSUE_TEMPLATE\informational.md
|
||||
.github\ISSUE_TEMPLATE\issue-report.md = .github\ISSUE_TEMPLATE\issue-report.md
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
@@ -22,12 +25,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RedumpLib", "RedumpLib\Redu
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.CueSheets", "MPF.CueSheets\MPF.CueSheets.csproj", "{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CICMMetadataEditor", "CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj", "{E4271454-6217-4500-BC36-F8856AC7AD6B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Core", "MPF.Core\MPF.Core.csproj", "{70B1265D-FE49-472A-A83D-0B462152D37A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Modules", "MPF.Modules\MPF.Modules.csproj", "{8A4254BD-552F-4238-B8EB-D59AACD768B9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.UI.Core", "MPF.UI.Core\MPF.UI.Core.csproj", "{EA3768DB-694A-4653-82E4-9FF71B8963F3}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MPF", "MPF", "{4160167D-681D-480B-ABC6-06AC869E5769}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -58,10 +63,6 @@ Global
|
||||
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{70B1265D-FE49-472A-A83D-0B462152D37A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{70B1265D-FE49-472A-A83D-0B462152D37A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{70B1265D-FE49-472A-A83D-0B462152D37A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -70,10 +71,24 @@ Global
|
||||
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{7B1B75EB-8940-466F-BD51-76471A57F9BE} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{7CC064D2-38AB-4A05-8519-28660DE4562A} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{51AB0928-13F9-44BF-A407-B6957A43A056} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{8CFDE289-E171-4D49-A40D-5293265C1253} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{70B1265D-FE49-472A-A83D-0B462152D37A} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{8A4254BD-552F-4238-B8EB-D59AACD768B9} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {73C62E6A-6584-4D93-83B5-ECB1FBDB469B}
|
||||
EndGlobalSection
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Windows;
|
||||
using MPF.Core.Data;
|
||||
using MPF.GUI.ViewModels;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.UI.ViewModels;
|
||||
using MPF.Windows;
|
||||
|
||||
namespace MPF
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.UI.Core.ComboBoxItems;
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace MPF
|
||||
|
||||
@@ -13,11 +13,13 @@ namespace MPF
|
||||
// Private lists of known drive speed ranges
|
||||
private static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
private static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
|
||||
|
||||
// Create collections for UI based on known drive speeds
|
||||
public static DoubleCollection SpeedsForCDAsCollection { get; } = GetDoubleCollectionFromIntList(CD);
|
||||
public static DoubleCollection SpeedsForDVDAsCollection { get; } = GetDoubleCollectionFromIntList(DVD);
|
||||
public static DoubleCollection SpeedsForHDDVDAsCollection { get; } = GetDoubleCollectionFromIntList(HDDVD);
|
||||
public static DoubleCollection SpeedsForBDAsCollection { get; } = GetDoubleCollectionFromIntList(BD);
|
||||
private static DoubleCollection GetDoubleCollectionFromIntList(IReadOnlyList<int> list)
|
||||
=> new DoubleCollection(list.Select(i => Convert.ToDouble(i)).ToList());
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0-windows</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win-x86;win-x64</RuntimeIdentifiers>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
@@ -11,9 +11,9 @@
|
||||
<AssemblyName>MPF</AssemblyName>
|
||||
<Description>Frontend for various dumping programs</Description>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.2</Version>
|
||||
<Version>2.5</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
@@ -27,11 +27,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.0" GeneratePathProperty="true">
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.7.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0-rc.1.21451.13" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -43,15 +43,12 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Icon.ico" />
|
||||
<Resource Include="Images\ring-code-guide.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj">
|
||||
<Project>{51ab0928-13f9-44bf-a407-b6957a43a056}</Project>
|
||||
<Name>MPF.Library</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
|
||||
<ProjectReference Include="..\MPF.UI.Core\MPF.UI.Core.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using MPF.Core.Data;
|
||||
|
||||
namespace MPF
|
||||
{
|
||||
public static class OptionsLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Load the current set of options from the application configuration
|
||||
/// </summary>
|
||||
public static Options LoadFromConfig()
|
||||
{
|
||||
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||||
|
||||
var settings = configFile.AppSettings.Settings;
|
||||
var dict = new Dictionary<string, string>();
|
||||
|
||||
foreach (string key in settings.AllKeys)
|
||||
{
|
||||
dict[key] = settings[key]?.Value ?? string.Empty;
|
||||
}
|
||||
|
||||
return new Options(dict);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current set of options to the application configuration
|
||||
/// </summary>
|
||||
public static void SaveToConfig(Options options)
|
||||
{
|
||||
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||||
|
||||
// Loop through all settings in Options and save them, overwriting existing settings
|
||||
foreach (var kvp in options)
|
||||
{
|
||||
configFile.AppSettings.Settings.Remove(kvp.Key);
|
||||
configFile.AppSettings.Settings.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
configFile.Save(ConfigurationSaveMode.Modified);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Windows.Controls;
|
||||
using MPF.GUI.ViewModels;
|
||||
using MPF.UI.ViewModels;
|
||||
|
||||
namespace MPF.UserControls
|
||||
{
|
||||
|
||||
@@ -1,284 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.Windows;
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace MPF.GUI.ViewModels
|
||||
{
|
||||
public class DiscInformationViewModel
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Parent DiscInformationWindow object
|
||||
/// </summary>
|
||||
public DiscInformationWindow Parent { 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 available languages
|
||||
/// </summary>
|
||||
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of available languages
|
||||
/// </summary>
|
||||
public List<Element<LanguageSelection>> LanguageSelections { get; private set; } = Element<LanguageSelection>.GenerateElements().ToList();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public DiscInformationViewModel(DiscInformationWindow parent, SubmissionInfo submissionInfo)
|
||||
{
|
||||
Parent = parent;
|
||||
SubmissionInfo = submissionInfo.Clone() as SubmissionInfo ?? new SubmissionInfo();
|
||||
|
||||
// Add handlers
|
||||
Parent.AcceptButton.Click += OnAcceptClick;
|
||||
Parent.CancelButton.Click += OnCancelClick;
|
||||
Parent.RingCodeGuideButton.Click += OnRingCodeGuideClick;
|
||||
|
||||
// Update UI with new values
|
||||
ManipulateFields();
|
||||
Load();
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Manipulate fields based on the current disc
|
||||
/// </summary>
|
||||
private void ManipulateFields()
|
||||
{
|
||||
// Sony-printed discs have layers in the opposite order
|
||||
var system = SubmissionInfo?.CommonDiscInfo?.System;
|
||||
bool reverseOrder = system.HasReversedRingcodes();
|
||||
|
||||
// Different media types mean different fields available
|
||||
switch (SubmissionInfo?.CommonDiscInfo?.Media)
|
||||
{
|
||||
case DiscType.CD:
|
||||
case DiscType.GDROM:
|
||||
Parent.L0Info.Header = "Data Side";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Label Side";
|
||||
Parent.L1MasteringRing.Visibility = Visibility.Collapsed;
|
||||
Parent.L1MasteringSID.Visibility = Visibility.Collapsed;
|
||||
Parent.L1Toolstamp.Visibility = Visibility.Collapsed;
|
||||
Parent.L1MouldSID.Label = "Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Additional Mould";
|
||||
break;
|
||||
|
||||
case DiscType.DVD5:
|
||||
case DiscType.DVD9:
|
||||
case DiscType.HDDVDSL:
|
||||
case DiscType.BD25:
|
||||
case DiscType.BD50:
|
||||
case DiscType.NintendoGameCubeGameDisc:
|
||||
case DiscType.NintendoWiiOpticalDiscSL:
|
||||
case DiscType.NintendoWiiOpticalDiscDL:
|
||||
case DiscType.NintendoWiiUOpticalDiscSL:
|
||||
// Quad-layer discs
|
||||
if (SubmissionInfo?.SizeAndChecksums?.Layerbreak3 != default(long))
|
||||
{
|
||||
Parent.L2Info.Visibility = Visibility.Visible;
|
||||
Parent.L3Info.Visibility = Visibility.Visible;
|
||||
|
||||
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Data Side Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Layer 1";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Label Side Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
|
||||
Parent.L2Info.Header = "Layer 2";
|
||||
Parent.L2MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L2MasteringSID.Label = "Mastering SID";
|
||||
Parent.L2Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
|
||||
Parent.L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
|
||||
Parent.L3MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L3MasteringSID.Label = "Mastering SID";
|
||||
Parent.L3Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
}
|
||||
|
||||
// Triple-layer discs
|
||||
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak2 != default(long))
|
||||
{
|
||||
Parent.L2Info.Visibility = Visibility.Visible;
|
||||
|
||||
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Data Side Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Layer 1";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Label Side Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
|
||||
Parent.L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
|
||||
Parent.L2MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L2MasteringSID.Label = "Mastering SID";
|
||||
Parent.L2Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
}
|
||||
|
||||
// Double-layer discs
|
||||
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
|
||||
{
|
||||
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Data Side Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Label Side Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
}
|
||||
|
||||
// Single-layer discs
|
||||
else
|
||||
{
|
||||
Parent.L0Info.Header = "Data Side";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Label Side";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Additional Mould";
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// All other media we assume to have no rings
|
||||
default:
|
||||
Parent.L0Info.Visibility = Visibility.Collapsed;
|
||||
Parent.L1Info.Visibility = Visibility.Collapsed;
|
||||
Parent.L2Info.Visibility = Visibility.Collapsed;
|
||||
Parent.L3Info.Visibility = Visibility.Collapsed;
|
||||
break;
|
||||
}
|
||||
|
||||
// Different systems mean different fields available
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
Parent.LanguageSelectionGrid.Visibility = Visibility.Visible;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the current contents of the base SubmissionInfo to the UI
|
||||
/// </summary>
|
||||
private void Load()
|
||||
{
|
||||
Parent.CategoryComboBox.SelectedIndex = Categories.FindIndex(r => r == SubmissionInfo.CommonDiscInfo.Category);
|
||||
Parent.RegionComboBox.SelectedIndex = Regions.FindIndex(r => r == SubmissionInfo.CommonDiscInfo.Region);
|
||||
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>
|
||||
public void Save()
|
||||
{
|
||||
SubmissionInfo.CommonDiscInfo.Category = (Parent.CategoryComboBox.SelectedItem as Element<DiscCategory>)?.Value ?? DiscCategory.Games;
|
||||
SubmissionInfo.CommonDiscInfo.Region = (Parent.RegionComboBox.SelectedItem as Element<Region>)?.Value ?? Region.World;
|
||||
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
|
||||
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
|
||||
SubmissionInfo.CommonDiscInfo.Languages = new Language?[] { null };
|
||||
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AcceptButton Click event
|
||||
/// </summary>
|
||||
private void OnAcceptClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Save();
|
||||
Parent.DialogResult = true;
|
||||
Parent.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CancelButton Click event
|
||||
/// </summary>
|
||||
private void OnCancelClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Parent.DialogResult = false;
|
||||
Parent.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for RingCodeGuideButton Click event
|
||||
/// </summary>
|
||||
private void OnRingCodeGuideClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var ringCodeGuideWindow = new RingCodeGuideWindow()
|
||||
{
|
||||
Owner = Parent,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
ringCodeGuideWindow.Show();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ using System.Windows.Media;
|
||||
using MPF.Core.Data;
|
||||
using MPF.UserControls;
|
||||
|
||||
namespace MPF.GUI.ViewModels
|
||||
namespace MPF.UI.ViewModels
|
||||
{
|
||||
public class LogViewModel
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,14 +2,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using MPF.Core.Data;
|
||||
using MPF.UI.Core.ComboBoxItems;
|
||||
using MPF.Windows;
|
||||
using RedumpLib.Web;
|
||||
using WPFCustomMessageBox;
|
||||
|
||||
namespace MPF.GUI.ViewModels
|
||||
namespace MPF.UI.ViewModels
|
||||
{
|
||||
public class OptionsViewModel
|
||||
{
|
||||
@@ -103,7 +105,7 @@ namespace MPF.GUI.ViewModels
|
||||
/// </summary>
|
||||
private static List<Element<InternalProgram>> PopulateInternalPrograms()
|
||||
{
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.DD };
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.Redumper, InternalProgram.DD };
|
||||
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
|
||||
}
|
||||
|
||||
@@ -186,23 +188,30 @@ namespace MPF.GUI.ViewModels
|
||||
/// <summary>
|
||||
/// Test Redump login credentials
|
||||
/// </summary>
|
||||
private void TestRedumpLogin()
|
||||
#if NET48 || NETSTANDARD2_1
|
||||
private bool? TestRedumpLogin()
|
||||
#else
|
||||
private async Task<bool?> TestRedumpLogin()
|
||||
#endif
|
||||
{
|
||||
using (RedumpWebClient wc = new RedumpWebClient())
|
||||
{
|
||||
bool? loggedIn = wc.Login(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
|
||||
if (loggedIn == true)
|
||||
CustomMessageBox.Show(Parent, "Redump login credentials accepted!", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
else if (loggedIn == false)
|
||||
CustomMessageBox.Show(Parent, "Redump login credentials denied!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
else
|
||||
CustomMessageBox.Show(Parent, "Error validating credentials!", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
#if NET48 || NETSTANDARD2_1
|
||||
(bool? success, string message) = RedumpWebClient.ValidateCredentials(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
|
||||
#else
|
||||
(bool? success, string message) = await RedumpHttpClient.ValidateCredentials(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
|
||||
#endif
|
||||
if (success == true)
|
||||
CustomMessageBox.Show(Parent, message, "Success", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
else if (success == false)
|
||||
CustomMessageBox.Show(Parent, message, "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
else
|
||||
CustomMessageBox.Show(Parent, message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region UI Functionality
|
||||
#region UI Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Create an open folder dialog box
|
||||
@@ -231,9 +240,9 @@ namespace MPF.GUI.ViewModels
|
||||
private System.Windows.Controls.TextBox TextBoxForPathSetting(string name) =>
|
||||
Parent.FindName(name + "TextBox") as System.Windows.Controls.TextBox;
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for generic Click event
|
||||
@@ -256,9 +265,14 @@ namespace MPF.GUI.ViewModels
|
||||
/// <summary>
|
||||
/// Test Redump credentials for validity
|
||||
/// </summary>
|
||||
#if NET48 || NETSTANDARD2_1
|
||||
private void OnRedumpTestClick(object sender, EventArgs e) =>
|
||||
TestRedumpLogin();
|
||||
#else
|
||||
private async void OnRedumpTestClick(object sender, EventArgs e) =>
|
||||
_ = await TestRedumpLogin();
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
<windows:WindowBase x:Class="MPF.Windows.DiscInformationWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UserControls"
|
||||
xmlns:windows="clr-namespace:MPF.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Disc Information" Width="515" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical" Width="500">
|
||||
<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>
|
||||
<TextBlock TextAlignment="Center"><Bold>Disc Information</Bold></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}}"
|
||||
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}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Expander Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Common Disc Information" IsExpanded="True">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput Label="Title" Text="{Binding SubmissionInfo.CommonDiscInfo.Title, Mode=TwoWay}"/>
|
||||
<controls:UserInput Label="Foreign Title (Non-Latin)" Text="{Binding SubmissionInfo.CommonDiscInfo.ForeignTitleNonLatin, 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="1*" />
|
||||
<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}"
|
||||
Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<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}"
|
||||
Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*" />
|
||||
<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="1*" />
|
||||
<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}"/>
|
||||
<controls:UserInput Label="Comments" Text="{Binding SubmissionInfo.CommonDiscInfo.Comments, Mode=TwoWay}" TextHeight="50" Tab="True" Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top" />
|
||||
<controls:UserInput Label="Contents" Text="{Binding SubmissionInfo.CommonDiscInfo.Contents, Mode=TwoWay}" TextHeight="50" Tab="True" Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<Expander Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Version and Editions" IsExpanded="True">
|
||||
<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>
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem x:Name="L0Info" Header="Data/L0 Info" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput x:Name="L0MasteringRing" Label="Data/L0 Mastering Ring" Text="{Binding SubmissionInfo.CommonDiscInfo.Layer0MasteringRing, Mode=TwoWay}" Tab="True"/>
|
||||
<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>
|
||||
</TabItem>
|
||||
|
||||
<TabItem x:Name="L1Info" Header="Label/L1 Info" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput x:Name="L1MasteringRing" Label="Label/L1 Mastering Ring" Text="{Binding SubmissionInfo.CommonDiscInfo.Layer1MasteringRing, Mode=TwoWay}" Tab="True"/>
|
||||
<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>
|
||||
</TabItem>
|
||||
|
||||
<TabItem x:Name="L2Info" Header="L2 Info" Visibility="Collapsed" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput x:Name="L2MasteringRing" Label="L2 Mastering Ring" Text="{Binding SubmissionInfo.CommonDiscInfo.Layer2MasteringRing, Mode=TwoWay}" Tab="True"/>
|
||||
<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>
|
||||
</TabItem>
|
||||
|
||||
<TabItem x:Name="L3Info" Header="L3 Info" Visibility="Collapsed" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput x:Name="L3MasteringRing" Label="L3 Mastering Ring" Text="{Binding SubmissionInfo.CommonDiscInfo.Layer3MasteringRing, Mode=TwoWay}" Tab="True"/>
|
||||
<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>
|
||||
</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" IsDefault="True" Content="Accept"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" 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>
|
||||
@@ -1,15 +1,15 @@
|
||||
<windows:WindowBase x:Class="MPF.Windows.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UserControls"
|
||||
xmlns:viewModels="clr-namespace:MPF.GUI.ViewModels"
|
||||
xmlns:windows="clr-namespace:MPF.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
<coreWindows:WindowBase x:Class="MPF.Windows.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UserControls"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows;assembly=MPF.UI.Core"
|
||||
xmlns:viewModels="clr-namespace:MPF.UI.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
@@ -24,7 +24,7 @@
|
||||
<Grid Margin="0,2,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="25"/>
|
||||
<ColumnDefinition Width="115"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
@@ -53,6 +53,10 @@
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}" />
|
||||
<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}}"
|
||||
@@ -115,7 +119,6 @@
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label x:Name="SystemMediaTypeLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
|
||||
@@ -132,16 +135,13 @@
|
||||
</ComboBox>
|
||||
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="OutputFilenameLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="Output Filename"/>
|
||||
<TextBox x:Name="OutputFilenameTextBox" Grid.Row="1" Grid.Column="1" Height="22" VerticalContentAlignment="Center" />
|
||||
|
||||
<Label x:Name="OutputDirectoryLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Output Directory"/>
|
||||
<TextBox x:Name="OutputDirectoryTextBox" Grid.Row="2" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="OutputDirectoryBrowseButton" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
<Label x:Name="OutputPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="Output Path"/>
|
||||
<TextBox x:Name="OutputPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="OutputPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
Style="{DynamicResource CustomButtonStyle}"/>
|
||||
|
||||
<Label x:Name="DriveLetterLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="Drive Letter"/>
|
||||
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<Label x:Name="DriveLetterLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Drive Letter"/>
|
||||
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="2" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Letter}" />
|
||||
@@ -149,21 +149,23 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Label x:Name="DriveSpeedLabel" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Content="Drive Speed"/>
|
||||
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
<Label x:Name="DriveSpeedLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="Drive Speed"/>
|
||||
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="ParametersLabel" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" Content="Parameters"/>
|
||||
<TextBox x:Name="ParametersTextBox" Grid.Row="5" Grid.Column="1" Height="22" Width="370" HorizontalAlignment="Left" IsEnabled="False" VerticalContentAlignment="Center" />
|
||||
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right" IsChecked="False" />
|
||||
<Label x:Name="ParametersLabel" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Content="Parameters"/>
|
||||
<TextBox x:Name="ParametersTextBox" Grid.Row="4" Grid.Column="1" Height="22" Width="370" HorizontalAlignment="Left" IsEnabled="False" VerticalContentAlignment="Center" />
|
||||
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="4" Grid.Column="1" Height="22" HorizontalAlignment="Right" IsChecked="False" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Controls">
|
||||
<UniformGrid Columns="3" Margin="5,5,5,5">
|
||||
<UniformGrid Columns="4" Margin="5,5,5,5">
|
||||
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True" Content="Start Dumping"
|
||||
IsEnabled="False" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="MediaScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for discs"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="UpdateVolumeLabel" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Update Label"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="CopyProtectScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for protection"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
</UniformGrid>
|
||||
@@ -171,7 +173,7 @@
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
|
||||
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
|
||||
<Label x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Waiting for media..." />
|
||||
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Text="Waiting for media..." />
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
@@ -180,4 +182,4 @@
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</windows:WindowBase>
|
||||
</coreWindows:WindowBase>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using MPF.GUI.ViewModels;
|
||||
using MPF.UI.Core.Windows;
|
||||
using MPF.UI.ViewModels;
|
||||
|
||||
namespace MPF.Windows
|
||||
{
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<windows:WindowBase x:Class="MPF.Windows.OptionsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:MPF"
|
||||
xmlns:windows="clr-namespace:MPF.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Options" Width="515.132" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
<coreWindows:WindowBase x:Class="MPF.Windows.OptionsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:MPF"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows;assembly=MPF.UI.Core"
|
||||
mc:Ignorable="d"
|
||||
Title="Options" Width="515.132" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
@@ -70,12 +70,15 @@
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
|
||||
IsChecked="{Binding Options.EnableDarkMode}"
|
||||
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
|
||||
/>
|
||||
|
||||
/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Check for Updates on Startup"
|
||||
IsChecked="{Binding Options.CheckForUpdatesOnStartup}"
|
||||
ToolTip="Check for updates when the application starts" Margin="0,4"
|
||||
/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Fast Update Label"
|
||||
IsChecked="{Binding Options.FastUpdateLabel}"
|
||||
ToolTip="Bypasses disc checks to quickly update the output path. Use with caution!" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
@@ -92,6 +95,7 @@
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Aaru Path" />
|
||||
@@ -109,13 +113,18 @@
|
||||
<Button x:Name="DDPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Dumping Program" />
|
||||
<ComboBox x:Name="InternalProgramComboBox" Grid.Row="3" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Redumper Path" />
|
||||
<TextBox x:Name="RedumperPathTextBox" Grid.Row="3" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.RedumperPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="RedumperPathButton" Grid.Row="3" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Dumping Program" />
|
||||
<ComboBox x:Name="InternalProgramComboBox" Grid.Row="4" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding InternalPrograms}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Output Path" />
|
||||
<TextBox x:Name="DefaultOutputPathTextBox" Grid.Row="4" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DefaultOutputPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="DefaultOutputPathButton" Grid.Row="4" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
<Label Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Output Path" />
|
||||
<TextBox x:Name="DefaultOutputPathTextBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DefaultOutputPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="DefaultOutputPathButton" Grid.Row="5" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
</Grid>
|
||||
</TabItem>
|
||||
@@ -123,7 +132,7 @@
|
||||
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
|
||||
<UniformGrid Columns="2" Rows="8">
|
||||
<UniformGrid Columns="2" Rows="9">
|
||||
<CheckBox VerticalAlignment="Center" Content="Skip Type Detect"
|
||||
IsChecked="{Binding Options.SkipMediaTypeDetection}"
|
||||
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
|
||||
@@ -162,6 +171,11 @@
|
||||
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Output Protection File"
|
||||
IsChecked="{Binding Options.OutputSeparateProtectionFile}" IsEnabled="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Output protection information to a separate file" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Eject After Dump"
|
||||
IsChecked="{Binding Options.EjectAfterDump}"
|
||||
ToolTip="Eject the disc from the drive after dumping has completed" Margin="0,4"
|
||||
@@ -172,6 +186,16 @@
|
||||
ToolTip="Enable showing the disc information output after dumping" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Tab Input"
|
||||
IsChecked="{Binding Options.EnableTabsInInputFields}"
|
||||
ToolTip="Enable entering tabs in supported input fields instead of tab navigation" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Redump Compatibility"
|
||||
IsChecked="{Binding Options.EnableRedumpCompatibility}"
|
||||
ToolTip="Enable limiting outputs to only those supported by Redump" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Show Eject Reminder"
|
||||
IsChecked="{Binding Options.ShowDiscEjectReminder}"
|
||||
ToolTip="Enable showing the disc eject reminder after information is gathered" Margin="0,4"
|
||||
@@ -179,7 +203,7 @@
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Add Placeholders"
|
||||
IsChecked="{Binding Options.AddPlaceholders}"
|
||||
ToolTip="Enable adding placeholder text in the submissioninfo output for required and optional fields" Margin="0,4"
|
||||
ToolTip="Enable adding placeholder text in the output for required and optional fields" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Output Submission JSON"
|
||||
@@ -188,7 +212,7 @@
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Artifacts"
|
||||
IsChecked="{Binding Options.IncludeArtifacts}" IsEnabled="{Binding Options.OutputSubmissionJSON}"
|
||||
IsChecked="{Binding Options.IncludeArtifacts}" IsEnabled="{Binding Options.OutputSubmissionJSON}"
|
||||
ToolTip="Include log files in serialized JSON data" Margin="0,4"
|
||||
/>
|
||||
|
||||
@@ -211,27 +235,35 @@
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="CD-ROM" />
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="CD" />
|
||||
<Slider x:Name="DumpSpeedCDSlider" Grid.Row="0" Grid.Column="1" Minimum="1" Maximum="72" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static local:Constants.SpeedsForCDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedCD}" />
|
||||
<TextBox x:Name="DumpSpeedCDTextBox" Grid.Row="0" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedCDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="DVD-ROM" />
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="DVD" />
|
||||
<Slider x:Name="DumpSpeedDVDSlider" Grid.Row="1" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static local:Constants.SpeedsForDVDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedDVD}" />
|
||||
<TextBox x:Name="DumpSpeedDVDTextBox" Grid.Row="1" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="BD-ROM" />
|
||||
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="HD-DVD" />
|
||||
<Slider x:Name="DumpSpeedHDDVDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static local:Constants.SpeedsForHDDVDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedHDDVD}" />
|
||||
<TextBox x:Name="DumpSpeedHDDVDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedHDDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="BD" />
|
||||
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="3" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static local:Constants.SpeedsForBDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedBD}" />
|
||||
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="3" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedBDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
@@ -251,11 +283,6 @@
|
||||
ToolTip="Include executable packers in outputted protections" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Force Scanning All Files"
|
||||
IsChecked="{Binding Options.ForceScanningForProtection}"
|
||||
ToolTip="Force scanning all files even if they're not executables" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
|
||||
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
|
||||
ToolTip="Include debug information during protection scans" Margin="0,4"
|
||||
@@ -295,7 +322,7 @@
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="DiscImageCreator" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<UniformGrid Columns="2" Rows="6">
|
||||
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
|
||||
IsChecked="{Binding Options.DICQuietMode}"
|
||||
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
|
||||
@@ -316,10 +343,38 @@
|
||||
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Multi-Sector Read"
|
||||
IsChecked="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Enable the /mr flag for BD drive dumping" Margin="0,4"
|
||||
/>
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
|
||||
<Label Content="Multi-Sector Read Value:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICMultiSectorReadValue}" IsEnabled="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Set the default value for the /mr flag"
|
||||
/>
|
||||
|
||||
<Label Content="CD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on C2 error"
|
||||
ToolTip="Specifies how many rereads are attempted on C2 error [CD only]"
|
||||
/>
|
||||
|
||||
<Label Content="DVD/HD-DVD/BD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICDVDRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error [DVD/HD-DVD/BD only]"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Redumper" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="1">
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.RedumperRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
@@ -380,4 +435,4 @@
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</windows:WindowBase>
|
||||
</coreWindows:WindowBase>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using MPF.GUI.ViewModels;
|
||||
using MPF.UI.Core.Windows;
|
||||
using MPF.UI.ViewModels;
|
||||
|
||||
namespace MPF.Windows
|
||||
{
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
<windows:WindowBase x:Class="MPF.Windows.RingCodeGuideWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:MPF"
|
||||
xmlns:controls="clr-namespace:MPF.UserControls"
|
||||
xmlns:windows="clr-namespace:MPF.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Ring Code Guide" Width="500" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<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>
|
||||
<TextBlock TextAlignment="Center"><Bold>Ring Code Guide</Bold></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>
|
||||
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Image Grid.Row="0" Source="/Images/ring-code-guide.png"
|
||||
HorizontalAlignment="Center" Stretch="Fill" Width="500" Height="500"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<Label Grid.Row="1">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Red">1. Mastering Ring:</Bold> Sony DADC<tab>A0100368905-0101<tab>15</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="2">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Green">2. Mastering SID:</Bold> IFPI L553</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="3">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Purple">3. Toolstamp/Mastering Code:</Bold> A2</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="4">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="LightBlue">4. Mould SID:</Bold> IFPI 94V1</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</windows:WindowBase>
|
||||
@@ -18,7 +18,7 @@ For those who like to test the newest features, download the latest AppVeyor WIP
|
||||
Even though this is written in C#, this program can only be used on Windows systems due to one of the base programs, DiscImageCreator, being Windows-only. There is some preliminary support for Linux underway, and we will try to integrate with that when the time comes.
|
||||
|
||||
- Windows 7 (newest version of Windows recommended) or Mono-compatible Linux environment (MPF.Check only)
|
||||
- .NET Framework 4.8 or .NET Core 3.1 Runtimes (.NET Core 3.1 is mostly functional due to a dependency issues but may be unstable in some situations)
|
||||
- .NET Framework 4.8 or .NET 6.0 Runtimes (.NET 6.0 is mostly functional due to a dependency issues but may be unstable in some situations)
|
||||
- 1 GB of free RAM
|
||||
- As much hard drive space as the amount of discs you will be dumping (20+ GB recommended)
|
||||
|
||||
@@ -37,8 +37,8 @@ A list of all changes in each stable release and current WIP builds can now be f
|
||||
MPF uses some external libraries to assist with additional information gathering after the dumping process.
|
||||
|
||||
- **BurnOutSharp** - Protection scanning - [GitHub](https://github.com/mnadareski/BurnOutSharp)
|
||||
- **WPFCustomMessageBox.thabse** - Custom message boxes in UI - [GitHub](https://github.com/thabse/WPFCustomMessageBox)
|
||||
- **UnshieldSharp** - Protection scanning - [GitHub](https://github.com/mnadareski/UnshieldSharp)
|
||||
- **WPFCustomMessageBox.thabse** - Custom message boxes in UI - [GitHub](https://github.com/thabse/WPFCustomMessageBox)
|
||||
|
||||
## Contributors
|
||||
|
||||
|
||||
26
RedumpLib/Attributes/LanguageAttribute.cs
Normal file
26
RedumpLib/Attributes/LanguageAttribute.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
namespace RedumpLib.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Attribute specifc to Language values
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Some languages have multiple proper names. Should all be supported?
|
||||
/// </remarks>
|
||||
public class LanguageAttribute : HumanReadableAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// ISO 639-1 Code
|
||||
/// </summary>
|
||||
public string TwoLetterCode { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// ISO 639-2 Code (Standard or Bibliographic)
|
||||
/// </summary>
|
||||
public string ThreeLetterCode { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// ISO 639-2 Code (Terminology)
|
||||
/// </summary>
|
||||
public string ThreeLetterCodeAlt { get; set; } = null;
|
||||
}
|
||||
}
|
||||
@@ -4,101 +4,6 @@ namespace RedumpLib.Data
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
#region Comment Field Tags
|
||||
|
||||
// TODO: Make this into a proper enum with LongName and ShortName
|
||||
|
||||
public const string AcclaimIDCommentField = "[T:ACC]";
|
||||
public const string AcclaimIDCommentString = "<b>Acclaim ID</b>:";
|
||||
public const string ActivisionIDCommentField = "[T:ACT]";
|
||||
public const string ActivisionIDCommentString = "<b>Activision ID</b>:";
|
||||
public const string AlternativeTitleCommentField = "[T:ALT]";
|
||||
public const string AlternativeTitleCommentString = "<b>Alternative Title</b>:";
|
||||
public const string AlternativeForeignTitleCommentField = "[T:ALTF]";
|
||||
public const string AlternativeForeignTitleCommentString = "<b>Alternative Foreign Title</b>:";
|
||||
public const string BandaiIDCommentField = "[T:BID]";
|
||||
public const string BandaiIDCommentString = "<b>Bandai ID</b>:";
|
||||
public const string BBFCRegistrationNumberCommentField = "[T:BBFC]";
|
||||
public const string BBFCRegistrationNumberCommentString = "<b>BBFC Reg. No.</b>:";
|
||||
public const string DNASDiscIDCommentField = "[T:DNAS]";
|
||||
public const string DNASDiscIDCommentString = "<b>DNAS Disc ID</b>:";
|
||||
public const string ElectronicArtsIDCommentField = "[T:EAID]";
|
||||
public const string ElectronicArtsIDCommentString = "<b>Electronic Arts ID</b>:";
|
||||
public const string ExtrasCommentField = "[T:X]";
|
||||
public const string ExtrasCommentString = "<b>Extras</b>:";
|
||||
public const string FoxInteractiveIDCommentField = "[T:FIID]";
|
||||
public const string FoxInteractiveIDCommentString = "<b>Fox Interactive ID</b>:";
|
||||
public const string GameFootageCommentField = "[T:GF]";
|
||||
public const string GameFootageCommentString = "<b>Game Footage</b>:";
|
||||
public const string GenreCommentField = "[T:G]";
|
||||
public const string GenreCommentString = "<b>Genre</b>:";
|
||||
public const string GTInteractiveIDCommentField = "[T:GTID]";
|
||||
public const string GTInteractiveIDCommentString = "<b>GT Interactive ID</b>:";
|
||||
public const string InternalSerialNameCommentField = "[T:ISN]";
|
||||
public const string InternalSerialNameCommentString = "<b>Internal Serial</b>:";
|
||||
public const string ISBNCommentField = "[T:ISBN]";
|
||||
public const string ISBNCommentString = "<b>ISBN</b>:";
|
||||
public const string ISSNCommentField = "[T:ISSN]";
|
||||
public const string ISSNCommentString = "<b>ISSN</b>:";
|
||||
public const string JASRACIDCommentField = "[T:JID]";
|
||||
public const string JASRACIDCommentString = "<b>JASRAC ID</b>:";
|
||||
public const string KingRecordsIDCommentField = "[T:KIRZ]";
|
||||
public const string KingRecordsIDCommentString = "<b>King Records ID</b>:";
|
||||
public const string KoeiIDCommentField = "[T:KOEI]";
|
||||
public const string KoeiIDCommentString = "<b>Koei ID</b>:";
|
||||
public const string KonamiIDCommentField = "[T:KID]";
|
||||
public const string KonamiIDCommentString = "<b>Konami ID</b>:";
|
||||
public const string LucasArtsIDCommentField = "[T:LAID]";
|
||||
public const string LucasArtsIDCommentString = "<b>Lucas Arts ID</b>:";
|
||||
public const string NaganoIDCommentField = "[T:NGID]";
|
||||
public const string NaganoIDCommentString = "<b>Nagano ID</b>:";
|
||||
public const string NamcoIDCommentField = "[T:NID]";
|
||||
public const string NamcoIDCommentString = "<b>Namco ID</b>:";
|
||||
public const string NetYarozeGamesCommentField = "[T:NYG]";
|
||||
public const string NetYarozeGamesCommentSring = "Net Yaroze Games</b>:";
|
||||
public const string NipponIchiSoftwareIDCommentField = "[T:NPS]";
|
||||
public const string NipponIchiSoftwareIDCommentString = "<b>Nippon Ichi Software ID</b>:";
|
||||
public const string OriginIDCommentField = "[T:OID]";
|
||||
public const string OriginIDCommentString = "<b>Origin ID</b>:";
|
||||
public const string PatchesCommentField = "[T:P]";
|
||||
public const string PatchesCommentString = "<b>Patches</b>:";
|
||||
public const string PlayableDemosCommentField = "[T:PD]";
|
||||
public const string PlayableDemosCommentString = "<b>Playable Demos</b>:";
|
||||
public const string PonyCanyonIDCommentField = "[T:PCID]";
|
||||
public const string PonyCanyonIDCommentString = "<b>Pony Canyon ID</b>:";
|
||||
public const string PostgapTypeCommentField = "[T:PT2]";
|
||||
public const string PostgapTypeCommentString = "<b>Postgap type</b>: Form 2";
|
||||
public const string PPNCommentField = "[T:PPN]";
|
||||
public const string PPNCommentString = "<b>PPN</b>:";
|
||||
public const string RollingDemosCommentField = "[T:RD]";
|
||||
public const string RollingDemosCommentString = "<b>Rolling Demos</b>:";
|
||||
public const string SavegamesCommentField = "[T:SG]";
|
||||
public const string SavegamesCommentString = "<b>Savegames</b>:";
|
||||
public const string SegaIDCommentField = "[T:SID]";
|
||||
public const string SegaIDCommentString = "<b>Sega ID</b>:";
|
||||
public const string SelenIDCommentField = "[T:SNID]";
|
||||
public const string SelenIDCommentString = "<b>Selen ID</b>:";
|
||||
public const string SeriesCommentField = "[T:S]";
|
||||
public const string SeriesCommentString = "<b>Series</b>:";
|
||||
public const string TaitoIDCommentField = "[T:TID]";
|
||||
public const string TaitoIDCommentString = "<b>Taito ID</b>:";
|
||||
public const string TechDemosCommentField = "[T:TD]";
|
||||
public const string TechDemosCommentString = "<b>Tech Demos</b>:";
|
||||
public const string UbisoftIDCommentField = "[T:UID]";
|
||||
public const string UbisoftIDCommentString = "<b>Ubisoft ID</b>:";
|
||||
public const string ValveIDCommentField = "[T:VID]";
|
||||
public const string ValveIDCommentString = "<b>Valve ID</b>:";
|
||||
public const string VFCCodeCommentField = "[T:VFC]";
|
||||
public const string VGCCodeCommentString = "<b>VFC code</b>:";
|
||||
public const string VideosCommentField = "[T:V]";
|
||||
public const string VideosCommentString = "<b>Videos</b>:";
|
||||
public const string VolumeLabelCommentField = "[T:VOL]";
|
||||
public const string VolumeLabelCommentString = "<b>Volume Label</b>:";
|
||||
public const string VCDCommentField = "[T:VCD]";
|
||||
public const string VCDCommentString = "<b>V-CD</b>";
|
||||
|
||||
#endregion
|
||||
|
||||
// TODO: Add RegexOptions.Compiled
|
||||
#region Regular Expressions
|
||||
|
||||
@@ -272,12 +177,12 @@ namespace RedumpLib.Data
|
||||
public const string DiscPageUrl = @"http://redump.org/disc/{0}/";
|
||||
|
||||
/// <summary>
|
||||
/// Redump last modified search URL
|
||||
/// Redump last modified search URL template
|
||||
/// </summary>
|
||||
public const string LastModifiedUrl = @"http://redump.org/discs/sort/modified/dir/desc?page={0}";
|
||||
|
||||
/// <summary>
|
||||
/// Redump login page URL
|
||||
/// Redump login page URL template
|
||||
/// </summary>
|
||||
public const string LoginUrl = "http://forum.redump.org/login/";
|
||||
|
||||
@@ -326,6 +231,11 @@ namespace RedumpLib.Data
|
||||
/// </summary>
|
||||
public const string UserDumpsUrl = @"http://redump.org/discs/dumper/{0}/?page={1}";
|
||||
|
||||
/// <summary>
|
||||
/// Redump last modified user dumps URL template
|
||||
/// </summary>
|
||||
public const string UserDumpsLastModifiedUrl = @"http://redump.org/discs/sort/modified/dir/desc/dumper/{0}?page={1}";
|
||||
|
||||
/// <summary>
|
||||
/// Redump WIP disc page URL template
|
||||
/// </summary>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedumpLib.Attributes;
|
||||
|
||||
namespace RedumpLib.Data
|
||||
@@ -10,7 +12,7 @@ namespace RedumpLib.Data
|
||||
{
|
||||
#region Cross-Enumeration
|
||||
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// Get a list of valid MediaTypes for a given RedumpSystem
|
||||
/// </summary>
|
||||
/// <param name="system">RedumpSystem value to check</param>
|
||||
@@ -671,6 +673,11 @@ namespace RedumpLib.Data
|
||||
types.Add(MediaType.CDROM);
|
||||
break;
|
||||
|
||||
// UNKNOWN
|
||||
case RedumpSystem.SonyElectronicBook:
|
||||
types.Add(MediaType.CDROM);
|
||||
break;
|
||||
|
||||
// https://en.wikipedia.org/wiki/Super_Audio_CD
|
||||
case RedumpSystem.SuperAudioCD:
|
||||
types.Add(MediaType.CDROM);
|
||||
@@ -745,7 +752,11 @@ namespace RedumpLib.Data
|
||||
switch (discType)
|
||||
{
|
||||
case DiscType.BD25:
|
||||
//case DiscType.BD33:
|
||||
case DiscType.BD50:
|
||||
//case DiscType.BD66:
|
||||
case DiscType.BD100:
|
||||
case DiscType.BD128:
|
||||
return MediaType.BluRay;
|
||||
case DiscType.CD:
|
||||
return MediaType.CDROM;
|
||||
@@ -755,6 +766,7 @@ namespace RedumpLib.Data
|
||||
case DiscType.GDROM:
|
||||
return MediaType.GDROM;
|
||||
case DiscType.HDDVDSL:
|
||||
case DiscType.HDDVDDL:
|
||||
return MediaType.HDDVD;
|
||||
// case DiscType.MILCD: // TODO: Support this?
|
||||
// return MediaType.MILCD;
|
||||
@@ -845,9 +857,21 @@ namespace RedumpLib.Data
|
||||
case "bd25":
|
||||
case "bd-25":
|
||||
return DiscType.BD25;
|
||||
//case "bd33":
|
||||
//case "bd-33":
|
||||
// return DiscType.BD33;
|
||||
case "bd50":
|
||||
case "bd-50":
|
||||
return DiscType.BD50;
|
||||
//case "bd66":
|
||||
//case "bd-66":
|
||||
// return DiscType.BD66;
|
||||
case "bd100":
|
||||
case "bd-100":
|
||||
return DiscType.BD100;
|
||||
case "bd128":
|
||||
case "bd-128":
|
||||
return DiscType.BD128;
|
||||
case "cd":
|
||||
case "cdrom":
|
||||
case "cd-rom":
|
||||
@@ -866,6 +890,9 @@ namespace RedumpLib.Data
|
||||
case "hddvdsl":
|
||||
case "hd-dvd sl":
|
||||
return DiscType.HDDVDSL;
|
||||
case "hddvddl":
|
||||
case "hd-dvd dl":
|
||||
return DiscType.HDDVDDL;
|
||||
case "milcd":
|
||||
case "mil-cd":
|
||||
return DiscType.MILCD;
|
||||
@@ -909,7 +936,23 @@ namespace RedumpLib.Data
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
/// <returns></returns>
|
||||
public static string ShortName(this Language? language) => AttributeHelper<Language?>.GetAttribute(language)?.ShortName;
|
||||
public static string ShortName(this Language? language)
|
||||
{
|
||||
// Some languages need to use the alternate code instead
|
||||
switch (language)
|
||||
{
|
||||
case Language.Albanian:
|
||||
case Language.Armenian:
|
||||
case Language.Icelandic:
|
||||
case Language.Macedonian:
|
||||
case Language.Romanian:
|
||||
case Language.Slovak:
|
||||
return language.ThreeLetterCodeAlt();
|
||||
|
||||
default:
|
||||
return language.ThreeLetterCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Language enum value for a given string
|
||||
@@ -918,103 +961,56 @@ namespace RedumpLib.Data
|
||||
/// <returns>Language represented by the string, if possible</returns>
|
||||
public static Language? ToLanguage(string lang)
|
||||
{
|
||||
switch (lang)
|
||||
{
|
||||
case "afr":
|
||||
return Language.Afrikaans;
|
||||
case "sqi":
|
||||
return Language.Albanian;
|
||||
case "ara":
|
||||
return Language.Arabic;
|
||||
case "baq":
|
||||
return Language.Basque;
|
||||
case "bul":
|
||||
return Language.Bulgarian;
|
||||
case "cat":
|
||||
return Language.Catalan;
|
||||
case "chi":
|
||||
return Language.Chinese;
|
||||
case "hrv":
|
||||
return Language.Croatian;
|
||||
case "cze":
|
||||
return Language.Czech;
|
||||
case "dan":
|
||||
return Language.Danish;
|
||||
case "dut":
|
||||
return Language.Dutch;
|
||||
case "eng":
|
||||
return Language.English;
|
||||
case "est":
|
||||
return Language.Estonian;
|
||||
case "fin":
|
||||
return Language.Finnish;
|
||||
case "fre":
|
||||
return Language.French;
|
||||
case "gla":
|
||||
return Language.Gaelic;
|
||||
case "ger":
|
||||
return Language.German;
|
||||
case "gre":
|
||||
return Language.Greek;
|
||||
case "heb":
|
||||
return Language.Hebrew;
|
||||
case "hin":
|
||||
return Language.Hindi;
|
||||
case "hun":
|
||||
return Language.Hungarian;
|
||||
case "ind":
|
||||
return Language.Indonesian;
|
||||
case "isl":
|
||||
return Language.Icelandic;
|
||||
case "ita":
|
||||
return Language.Italian;
|
||||
case "jap":
|
||||
return Language.Japanese;
|
||||
case "kor":
|
||||
return Language.Korean;
|
||||
case "lat":
|
||||
return Language.Latin;
|
||||
case "lav":
|
||||
return Language.Latvian;
|
||||
case "lit":
|
||||
return Language.Lithuanian;
|
||||
case "mkd":
|
||||
return Language.Macedonian;
|
||||
case "nor":
|
||||
return Language.Norwegian;
|
||||
case "pol":
|
||||
return Language.Polish;
|
||||
case "por":
|
||||
return Language.Portuguese;
|
||||
case "pan":
|
||||
return Language.Punjabi;
|
||||
case "ron":
|
||||
return Language.Romanian;
|
||||
case "rus":
|
||||
return Language.Russian;
|
||||
case "srp":
|
||||
return Language.Serbian;
|
||||
case "slk":
|
||||
return Language.Slovak;
|
||||
case "slv":
|
||||
return Language.Slovenian;
|
||||
case "spa":
|
||||
return Language.Spanish;
|
||||
case "swe":
|
||||
return Language.Swedish;
|
||||
case "tam":
|
||||
return Language.Tamil;
|
||||
case "tha":
|
||||
return Language.Thai;
|
||||
case "tur":
|
||||
return Language.Turkish;
|
||||
case "ukr":
|
||||
return Language.Ukrainian;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
var languages = Enum.GetValues(typeof(Language)).Cast<Language?>().ToList();
|
||||
|
||||
// Check ISO 639-1 codes
|
||||
Dictionary<string, Language?> languageMapping = languages
|
||||
.Where(l => l.TwoLetterCode() != null)
|
||||
.ToDictionary(l => l.TwoLetterCode(), l => l);
|
||||
|
||||
if (languageMapping.ContainsKey(lang))
|
||||
return languageMapping[lang];
|
||||
|
||||
// Check standard ISO 639-2 codes
|
||||
languageMapping = languages
|
||||
.Where(l => l.ThreeLetterCode() != null)
|
||||
.ToDictionary(l => l.ThreeLetterCode(), l => l);
|
||||
|
||||
if (languageMapping.ContainsKey(lang))
|
||||
return languageMapping[lang];
|
||||
|
||||
// Check alternate ISO 639-2 codes
|
||||
languageMapping = languages
|
||||
.Where(l => l.ThreeLetterCodeAlt() != null)
|
||||
.ToDictionary(l => l.ThreeLetterCodeAlt(), l => l);
|
||||
|
||||
if (languageMapping.ContainsKey(lang))
|
||||
return languageMapping[lang];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the ISO 639-2 code for each known language
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
/// <returns></returns>
|
||||
public static string ThreeLetterCode(this Language? language) => ((LanguageAttribute)AttributeHelper<Language?>.GetAttribute(language))?.ThreeLetterCode;
|
||||
|
||||
/// <summary>
|
||||
/// Get the ISO 639-2 alternate code for each known language
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
/// <returns></returns>
|
||||
public static string ThreeLetterCodeAlt(this Language? language) => ((LanguageAttribute)AttributeHelper<Language?>.GetAttribute(language))?.ThreeLetterCodeAlt;
|
||||
|
||||
/// <summary>
|
||||
/// Get the ISO 639-1 code for each known language
|
||||
/// </summary>
|
||||
/// <param name="language"></param>
|
||||
/// <returns></returns>
|
||||
public static string TwoLetterCode(this Language? language) => ((LanguageAttribute)AttributeHelper<Language?>.GetAttribute(language))?.TwoLetterCode;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Language Selection
|
||||
@@ -1030,6 +1026,24 @@ namespace RedumpLib.Data
|
||||
|
||||
#region Media Type
|
||||
|
||||
/// <summary>
|
||||
/// List all media types with their short usable names
|
||||
/// </summary>
|
||||
public static List<string> ListMediaTypes()
|
||||
{
|
||||
var mediaTypes = new List<string>();
|
||||
|
||||
foreach (var val in Enum.GetValues(typeof(MediaType)))
|
||||
{
|
||||
if (((MediaType)val) == MediaType.NONE)
|
||||
continue;
|
||||
|
||||
mediaTypes.Add($"{((MediaType?)val).ShortName()} - {((MediaType?)val).LongName()}");
|
||||
}
|
||||
|
||||
return mediaTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Redump longnames for each known media type
|
||||
/// </summary>
|
||||
@@ -1069,173 +1083,82 @@ namespace RedumpLib.Data
|
||||
/// <returns>Region represented by the string, if possible</returns>
|
||||
public static Region? ToRegion(string region)
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
case "Ar":
|
||||
return Region.Argentina;
|
||||
case "A":
|
||||
return Region.Asia;
|
||||
case "A,E":
|
||||
return Region.AsiaEurope;
|
||||
case "A,U":
|
||||
return Region.AsiaUSA;
|
||||
case "Au":
|
||||
return Region.Australia;
|
||||
case "Au,G":
|
||||
return Region.AustraliaGermany;
|
||||
case "Au,Nz":
|
||||
return Region.AustraliaNewZealand;
|
||||
case "At":
|
||||
return Region.Austria;
|
||||
case "At,Ch":
|
||||
return Region.AustriaSwitzerland;
|
||||
case "Be":
|
||||
return Region.Belgium;
|
||||
case "Be,N":
|
||||
return Region.BelgiumNetherlands;
|
||||
case "B":
|
||||
return Region.Brazil;
|
||||
case "Bg":
|
||||
return Region.Bulgaria;
|
||||
case "Ca":
|
||||
return Region.Canada;
|
||||
case "C":
|
||||
return Region.China;
|
||||
case "Hr":
|
||||
return Region.Croatia;
|
||||
case "Cz":
|
||||
return Region.Czech;
|
||||
case "Dk":
|
||||
return Region.Denmark;
|
||||
case "Ee":
|
||||
return Region.Estonia;
|
||||
case "E":
|
||||
return Region.Europe;
|
||||
case "E,A":
|
||||
return Region.EuropeAsia;
|
||||
case "E,Au":
|
||||
return Region.EuropeAustralia;
|
||||
case "E,Ca":
|
||||
return Region.EuropeCanada;
|
||||
case "E,G":
|
||||
return Region.EuropeGermany;
|
||||
case "Ex":
|
||||
return Region.Export;
|
||||
case "Fi":
|
||||
return Region.Finland;
|
||||
case "F":
|
||||
return Region.France;
|
||||
case "F,S":
|
||||
return Region.FranceSpain;
|
||||
case "G":
|
||||
return Region.Germany;
|
||||
case "GC":
|
||||
return Region.GreaterChina;
|
||||
case "Gr":
|
||||
return Region.Greece;
|
||||
case "H":
|
||||
return Region.Hungary;
|
||||
case "Is":
|
||||
return Region.Iceland;
|
||||
case "In":
|
||||
return Region.India;
|
||||
case "Ie":
|
||||
return Region.Ireland;
|
||||
case "Il":
|
||||
return Region.Israel;
|
||||
case "I":
|
||||
return Region.Italy;
|
||||
case "J":
|
||||
return Region.Japan;
|
||||
case "J,A":
|
||||
return Region.JapanAsia;
|
||||
case "J,E":
|
||||
return Region.JapanEurope;
|
||||
case "J,K":
|
||||
return Region.JapanKorea;
|
||||
case "J,U":
|
||||
return Region.JapanUSA;
|
||||
case "K":
|
||||
return Region.Korea;
|
||||
case "LAm":
|
||||
return Region.LatinAmerica;
|
||||
case "Lt":
|
||||
return Region.Lithuania;
|
||||
case "N":
|
||||
return Region.Netherlands;
|
||||
case "Nz":
|
||||
return Region.NewZealand;
|
||||
case "No":
|
||||
return Region.Norway;
|
||||
case "P":
|
||||
return Region.Poland;
|
||||
case "Pt":
|
||||
return Region.Portugal;
|
||||
case "Ro":
|
||||
return Region.Romania;
|
||||
case "R":
|
||||
return Region.Russia;
|
||||
case "Sca":
|
||||
return Region.Scandinavia;
|
||||
case "Rs":
|
||||
return Region.Serbia;
|
||||
case "Sg":
|
||||
return Region.Singapore;
|
||||
case "Sk":
|
||||
return Region.Slovakia;
|
||||
case "Za":
|
||||
return Region.SouthAfrica;
|
||||
case "S":
|
||||
return Region.Spain;
|
||||
case "S,Pt":
|
||||
return Region.SpainPortugal;
|
||||
case "Sw":
|
||||
return Region.Sweden;
|
||||
case "Ch":
|
||||
return Region.Switzerland;
|
||||
case "Tw":
|
||||
return Region.Taiwan;
|
||||
case "Th":
|
||||
return Region.Thailand;
|
||||
case "Tr":
|
||||
return Region.Turkey;
|
||||
case "Ae":
|
||||
return Region.UnitedArabEmirates;
|
||||
case "Uk":
|
||||
return Region.UK;
|
||||
case "Uk,Au":
|
||||
return Region.UKAustralia;
|
||||
case "Ue":
|
||||
return Region.Ukraine;
|
||||
case "U":
|
||||
return Region.USA;
|
||||
case "U,A":
|
||||
return Region.USAAsia;
|
||||
case "U,Au":
|
||||
return Region.USAAustralia;
|
||||
case "U,B":
|
||||
return Region.USABrazil;
|
||||
case "U,Ca":
|
||||
return Region.USACanada;
|
||||
case "U,E":
|
||||
return Region.USAEurope;
|
||||
case "U,G":
|
||||
return Region.USAGermany;
|
||||
case "U,J":
|
||||
return Region.USAJapan;
|
||||
case "U,K":
|
||||
return Region.USAKorea;
|
||||
case "W":
|
||||
return Region.World;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
region = region.ToLowerInvariant();
|
||||
var regions = Enum.GetValues(typeof(Region)).Cast<Region?>().ToList();
|
||||
|
||||
// Check ISO 3166-1 alpha-2 codes
|
||||
Dictionary<string, Region?> regionMapping = regions
|
||||
.Where(r => r.ShortName() != null)
|
||||
.ToDictionary(r => r.ShortName().ToLowerInvariant(), r => r);
|
||||
|
||||
if (regionMapping.ContainsKey(region))
|
||||
return regionMapping[region];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Site Code
|
||||
|
||||
/// <summary>
|
||||
/// Get the HTML version for each known site code
|
||||
/// </summary>
|
||||
/// <param name="siteCode"></param>
|
||||
/// <returns></returns>
|
||||
public static string LongName(this SiteCode? siteCode) => AttributeHelper<SiteCode?>.GetAttribute(siteCode)?.LongName;
|
||||
|
||||
/// <summary>
|
||||
/// Get the short tag for each known site code
|
||||
/// </summary>
|
||||
/// <param name="siteCode"></param>
|
||||
/// <returns></returns>
|
||||
public static string ShortName(this SiteCode? siteCode) => AttributeHelper<SiteCode?>.GetAttribute(siteCode)?.ShortName;
|
||||
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a system is a marker value
|
||||
/// </summary>
|
||||
/// <param name="system">RedumpSystem value to check</param>
|
||||
/// <returns>True if the system is a marker value, false otherwise</returns>
|
||||
public static bool IsMarker(this RedumpSystem? system)
|
||||
{
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.MarkerArcadeEnd:
|
||||
case RedumpSystem.MarkerComputerEnd:
|
||||
case RedumpSystem.MarkerDiscBasedConsoleEnd:
|
||||
// case RedumpSystem.MarkerOtherConsoleEnd:
|
||||
case RedumpSystem.MarkerOtherEnd:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all systems with their short usable names
|
||||
/// </summary>
|
||||
public static List<string> ListSystems()
|
||||
{
|
||||
var systems = new List<string>();
|
||||
|
||||
var knownSystems = Enum.GetValues(typeof(RedumpSystem))
|
||||
.OfType<RedumpSystem?>()
|
||||
.Where(s => s != null && !s.IsMarker() && s.GetCategory() != SystemCategory.NONE)
|
||||
.OrderBy(s => s.LongName() ?? string.Empty);
|
||||
|
||||
foreach (var val in knownSystems)
|
||||
{
|
||||
systems.Add($"{val.ShortName()} - {val.LongName()}");
|
||||
}
|
||||
|
||||
return systems;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Redump longnames for each known system
|
||||
/// </summary>
|
||||
@@ -1313,12 +1236,14 @@ namespace RedumpLib.Data
|
||||
|
||||
case "xboxbios":
|
||||
case "xbox bios":
|
||||
case "xbox-bios":
|
||||
case "microsoftxboxbios":
|
||||
case "microsoftxbox bios":
|
||||
case "microsoft xbox bios":
|
||||
return RedumpSystem.MicrosoftXboxBIOS;
|
||||
case "gcbios":
|
||||
case "gc bios":
|
||||
case "gc-bios":
|
||||
case "gamecubebios":
|
||||
case "ngcbios":
|
||||
case "ngc bios":
|
||||
@@ -1329,6 +1254,7 @@ namespace RedumpLib.Data
|
||||
case "ps1 bios":
|
||||
case "psxbios":
|
||||
case "psx bios":
|
||||
case "psx-bios":
|
||||
case "playstationbios":
|
||||
case "playstation bios":
|
||||
case "sonyps1bios":
|
||||
@@ -1343,6 +1269,7 @@ namespace RedumpLib.Data
|
||||
return RedumpSystem.SonyPlayStationBIOS;
|
||||
case "ps2bios":
|
||||
case "ps2 bios":
|
||||
case "ps2-bios":
|
||||
case "playstation2bios":
|
||||
case "playstation2 bios":
|
||||
case "playstation 2 bios":
|
||||
@@ -1358,6 +1285,7 @@ namespace RedumpLib.Data
|
||||
|
||||
#region Consoles
|
||||
|
||||
case "ajcd":
|
||||
case "jaguar":
|
||||
case "jagcd":
|
||||
case "jaguarcd":
|
||||
@@ -1367,6 +1295,7 @@ namespace RedumpLib.Data
|
||||
case "atarijaguarcd":
|
||||
case "atari jaguar cd":
|
||||
return RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem;
|
||||
case "qis":
|
||||
case "playdia":
|
||||
case "playdiaqis":
|
||||
case "playdiaquickinteractivesystem":
|
||||
@@ -1410,20 +1339,24 @@ namespace RedumpLib.Data
|
||||
case "fujitsufmtownsmarty":
|
||||
case "fujitsu fm towns marty":
|
||||
return RedumpSystem.FujitsuFMTownsMarty;
|
||||
case "hvn":
|
||||
case "videonow":
|
||||
case "hasbrovideonow":
|
||||
case "hasbro videonow":
|
||||
return RedumpSystem.HasbroVideoNow;
|
||||
case "hvnc":
|
||||
case "videonowcolor":
|
||||
case "videonow color":
|
||||
case "hasbrovideonowcolor":
|
||||
case "hasbro videonow color":
|
||||
return RedumpSystem.HasbroVideoNowColor;
|
||||
case "hvnjr":
|
||||
case "videonowjr":
|
||||
case "videonow jr":
|
||||
case "hasbrovideonowjr":
|
||||
case "hasbro videonow jr":
|
||||
return RedumpSystem.HasbroVideoNowJr;
|
||||
case "xvnxp":
|
||||
case "videonowxp":
|
||||
case "videonow xp":
|
||||
case "hasbrovideonowxp":
|
||||
@@ -1442,6 +1375,7 @@ namespace RedumpLib.Data
|
||||
case "mattel fisherprice ixl":
|
||||
case "mattel fisher-price ixl":
|
||||
return RedumpSystem.MattelFisherPriceiXL;
|
||||
case "hs":
|
||||
case "hyperscan":
|
||||
case "mattelhyperscan":
|
||||
case "mattel hyperscan":
|
||||
@@ -1466,6 +1400,7 @@ namespace RedumpLib.Data
|
||||
case "microsoft xbox one":
|
||||
return RedumpSystem.MicrosoftXboxOne;
|
||||
case "xbs":
|
||||
case "xboxsx":
|
||||
case "xbseries":
|
||||
case "xbseriess":
|
||||
case "xbseriesx":
|
||||
@@ -1483,6 +1418,7 @@ namespace RedumpLib.Data
|
||||
case "microsoft xbox series x":
|
||||
case "microsoft xbox series x and s":
|
||||
return RedumpSystem.MicrosoftXboxSeriesXS;
|
||||
case "pce":
|
||||
case "pcecd":
|
||||
case "pce-cd":
|
||||
case "tgcd":
|
||||
@@ -1578,6 +1514,7 @@ namespace RedumpLib.Data
|
||||
case "segadreamcast":
|
||||
case "sega dreamcast":
|
||||
return RedumpSystem.SegaDreamcast;
|
||||
case "ss":
|
||||
case "saturn":
|
||||
case "segasaturn":
|
||||
case "sega saturn":
|
||||
@@ -1687,6 +1624,7 @@ namespace RedumpLib.Data
|
||||
#region Computers
|
||||
|
||||
case "acorn":
|
||||
case "archcd":
|
||||
case "archimedes":
|
||||
case "acornarchimedes":
|
||||
case "acorn archimedes":
|
||||
@@ -1699,6 +1637,7 @@ namespace RedumpLib.Data
|
||||
case "apple mac":
|
||||
case "apple macintosh":
|
||||
return RedumpSystem.AppleMacintosh;
|
||||
case "acd":
|
||||
case "amiga":
|
||||
case "commodoreamiga":
|
||||
case "commodore amiga":
|
||||
@@ -1729,6 +1668,7 @@ namespace RedumpLib.Data
|
||||
case "nec pc-98":
|
||||
return RedumpSystem.NECPC98series;
|
||||
case "x68k":
|
||||
case "x68kcd":
|
||||
case "x68000":
|
||||
case "sharpx68k":
|
||||
case "sharp x68k":
|
||||
@@ -1800,6 +1740,7 @@ namespace RedumpLib.Data
|
||||
case "ice pc":
|
||||
case "ice pc-based hardware":
|
||||
return RedumpSystem.ICEPCHardware;
|
||||
case "ite":
|
||||
case "iteagle":
|
||||
case "eagle":
|
||||
case "incredible technologies eagle":
|
||||
@@ -1807,6 +1748,7 @@ namespace RedumpLib.Data
|
||||
case "itpc":
|
||||
case "incredible technologies pc-based systems":
|
||||
return RedumpSystem.IncredibleTechnologiesVarious;
|
||||
case "kea":
|
||||
case "eamusement":
|
||||
case "e-amusement":
|
||||
case "konamieamusement":
|
||||
@@ -1814,10 +1756,12 @@ namespace RedumpLib.Data
|
||||
case "konamie-amusement":
|
||||
case "konami e-amusement":
|
||||
return RedumpSystem.KonamieAmusement;
|
||||
case "kfb":
|
||||
case "firebeat":
|
||||
case "konamifirebeat":
|
||||
case "konami firebeat":
|
||||
return RedumpSystem.KonamiFireBeat;
|
||||
case "ksgv":
|
||||
case "gvsystem":
|
||||
case "gv system":
|
||||
case "konamigvsystem":
|
||||
@@ -1827,6 +1771,7 @@ namespace RedumpLib.Data
|
||||
case "konamisystemgv":
|
||||
case "konami system gv":
|
||||
return RedumpSystem.KonamiSystemGV;
|
||||
case "km2":
|
||||
case "konamim2":
|
||||
case "konami m2":
|
||||
return RedumpSystem.KonamiM2;
|
||||
@@ -1839,11 +1784,13 @@ namespace RedumpLib.Data
|
||||
case "konamipython2":
|
||||
case "konami python 2":
|
||||
return RedumpSystem.KonamiPython2;
|
||||
case "ks573":
|
||||
case "system573":
|
||||
case "system 573":
|
||||
case "konamisystem573":
|
||||
case "konami system 573":
|
||||
return RedumpSystem.KonamiSystem573;
|
||||
case "kt":
|
||||
case "twinkle":
|
||||
case "konamitwinkle":
|
||||
case "konami twinkle":
|
||||
@@ -1876,6 +1823,7 @@ namespace RedumpLib.Data
|
||||
case "meritindustriesmegatouchxl":
|
||||
case "merit industries megatouch xl":
|
||||
return RedumpSystem.MeritIndustriesMegaTouchXL;
|
||||
case "ns246":
|
||||
case "system246":
|
||||
case "system 246":
|
||||
case "namcosystem246":
|
||||
@@ -1908,6 +1856,7 @@ namespace RedumpLib.Data
|
||||
case "nintendo triforce":
|
||||
case "namco / sega / nintendo triforce":
|
||||
return RedumpSystem.NamcoSegaNintendoTriforce;
|
||||
case "ns12":
|
||||
case "system12":
|
||||
case "system 12":
|
||||
case "namcosystem12":
|
||||
@@ -1939,6 +1888,7 @@ namespace RedumpLib.Data
|
||||
case "nichibutsu xrs":
|
||||
case "nichibutsu x-rate system":
|
||||
return RedumpSystem.NichibutsuXRateSystem;
|
||||
case "m2":
|
||||
case "panasonicm2":
|
||||
case "panasonic m2":
|
||||
return RedumpSystem.PanasonicM2;
|
||||
@@ -1982,10 +1932,12 @@ namespace RedumpLib.Data
|
||||
case "seganu":
|
||||
case "sega nu":
|
||||
return RedumpSystem.SegaNu;
|
||||
case "sre":
|
||||
case "ringedge":
|
||||
case "segaringedge":
|
||||
case "sega ringedge":
|
||||
return RedumpSystem.SegaRingEdge;
|
||||
case "sre2":
|
||||
case "ringedge2":
|
||||
case "ringedge 2":
|
||||
case "segaringedge2":
|
||||
@@ -2028,6 +1980,7 @@ namespace RedumpLib.Data
|
||||
case "audio":
|
||||
case "audiocd":
|
||||
case "audio cd":
|
||||
case "audio-cd":
|
||||
return RedumpSystem.AudioCD;
|
||||
case "bdvideo":
|
||||
case "bd-video":
|
||||
@@ -2045,6 +1998,7 @@ namespace RedumpLib.Data
|
||||
return RedumpSystem.DVDVideo;
|
||||
case "enhancedcd":
|
||||
case "enhanced cd":
|
||||
case "enhanced-cd":
|
||||
case "enhancedcdrom":
|
||||
case "enhanced cdrom":
|
||||
case "enhanced cd-rom":
|
||||
@@ -2055,6 +2009,7 @@ namespace RedumpLib.Data
|
||||
case "hddvd-video":
|
||||
case "hd-dvd-video":
|
||||
return RedumpSystem.HDDVDVideo;
|
||||
case "navi21":
|
||||
case "naviken":
|
||||
case "naviken21":
|
||||
case "naviken 2.1":
|
||||
@@ -2069,7 +2024,9 @@ namespace RedumpLib.Data
|
||||
case "photo":
|
||||
case "photocd":
|
||||
case "photo cd":
|
||||
case "photo-cd":
|
||||
return RedumpSystem.PhotoCD;
|
||||
case "psxgs":
|
||||
case "gameshark":
|
||||
case "psgameshark":
|
||||
case "ps gameshark":
|
||||
@@ -2085,6 +2042,7 @@ namespace RedumpLib.Data
|
||||
case "rainbowdisc":
|
||||
case "rainbow disc":
|
||||
return RedumpSystem.RainbowDisc;
|
||||
case "sp21":
|
||||
case "pl21":
|
||||
case "prologue21":
|
||||
case "prologue 21":
|
||||
@@ -2092,6 +2050,10 @@ namespace RedumpLib.Data
|
||||
case "sega prologue21":
|
||||
case "sega prologue 21":
|
||||
return RedumpSystem.SegaPrologue21MultimediaKaraokeSystem;
|
||||
case "electronicbook":
|
||||
case "sonyelectronicbook":
|
||||
case "sony electronic book":
|
||||
return RedumpSystem.SonyElectronicBook;
|
||||
case "sacd":
|
||||
case "superaudiocd":
|
||||
case "super audio cd":
|
||||
@@ -2100,6 +2062,7 @@ namespace RedumpLib.Data
|
||||
case "taoiktv":
|
||||
case "tao iktv":
|
||||
return RedumpSystem.TaoiKTV;
|
||||
case "ksite":
|
||||
case "kisssite":
|
||||
case "kiss-site":
|
||||
case "tomykisssite":
|
||||
@@ -2119,7 +2082,7 @@ namespace RedumpLib.Data
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region System Category
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,13 +12,19 @@ namespace RedumpLib.Data
|
||||
/// Version of the current schema
|
||||
/// </summary>
|
||||
[JsonProperty(PropertyName = "schema_version", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public int SchemaVersion { get; set; } = 1;
|
||||
public int SchemaVersion { get; set; } = 2;
|
||||
|
||||
/// <summary>
|
||||
/// List of matched Redump IDs
|
||||
/// Fully matched Redump ID
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public List<int> MatchedIDs { get; set; }
|
||||
public int? FullyMatchedID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of partially matched Redump IDs
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public List<int> PartiallyMatchedIDs { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// DateTime of when the disc was added
|
||||
@@ -59,15 +65,19 @@ namespace RedumpLib.Data
|
||||
[JsonProperty(PropertyName = "size_and_checksums", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public SizeAndChecksumsSection SizeAndChecksums { get; set; } = new SizeAndChecksumsSection();
|
||||
|
||||
[JsonProperty(PropertyName = "dumping_info", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public DumpingInfoSection DumpingInfo { get; set; } = new DumpingInfoSection();
|
||||
|
||||
[JsonProperty(PropertyName = "artifacts", DefaultValueHandling = DefaultValueHandling.Ignore)]
|
||||
public Dictionary<string, string> Artifacts { get; set; } = new Dictionary<string, string>();
|
||||
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return new SubmissionInfo
|
||||
{
|
||||
SchemaVersion = this.SchemaVersion,
|
||||
MatchedIDs = this.MatchedIDs,
|
||||
FullyMatchedID = this.FullyMatchedID,
|
||||
PartiallyMatchedIDs = this.PartiallyMatchedIDs,
|
||||
Added = this.Added,
|
||||
LastModified = this.LastModified,
|
||||
CommonDiscInfo = this.CommonDiscInfo?.Clone() as CommonDiscInfoSection,
|
||||
@@ -79,7 +89,8 @@ namespace RedumpLib.Data
|
||||
DumpersAndStatus = this.DumpersAndStatus?.Clone() as DumpersAndStatusSection,
|
||||
TracksAndWriteOffsets = this.TracksAndWriteOffsets?.Clone() as TracksAndWriteOffsetsSection,
|
||||
SizeAndChecksums = this.SizeAndChecksums?.Clone() as SizeAndChecksumsSection,
|
||||
Artifacts = this.Artifacts.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
|
||||
DumpingInfo = this.DumpingInfo?.Clone() as DumpingInfoSection,
|
||||
Artifacts = this.Artifacts?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -148,7 +159,7 @@ namespace RedumpLib.Data
|
||||
[JsonProperty(PropertyName = "d_ring_0_mo1_sid", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Layer0MouldSID { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "dr_ring_0_mo1", NullValueHandling = NullValueHandling.Ignore)]
|
||||
[JsonProperty(PropertyName = "d_ring_0_mo1", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Layer0AdditionalMould { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "d_ring_0_ma2", Required = Required.AllowNull)]
|
||||
@@ -163,7 +174,7 @@ namespace RedumpLib.Data
|
||||
[JsonProperty(PropertyName = "d_ring_0_mo2_sid", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Layer1MouldSID { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "dr_ring_0_mo2", NullValueHandling = NullValueHandling.Ignore)]
|
||||
[JsonProperty(PropertyName = "d_ring_0_mo2", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Layer1AdditionalMould { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "d_ring_0_ma3", Required = Required.AllowNull)]
|
||||
@@ -211,9 +222,15 @@ namespace RedumpLib.Data
|
||||
[JsonProperty(PropertyName = "d_comments", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Comments { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Dictionary<SiteCode?, string> CommentsSpecialFields { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "d_contents", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Contents { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Dictionary<SiteCode?, string> ContentsSpecialFields { get; set; }
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return new CommonDiscInfoSection
|
||||
@@ -254,7 +271,9 @@ namespace RedumpLib.Data
|
||||
EXEDateBuildDate = this.EXEDateBuildDate,
|
||||
ErrorsCount = this.ErrorsCount,
|
||||
Comments = this.Comments,
|
||||
CommentsSpecialFields = this.CommentsSpecialFields?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
|
||||
Contents = this.Contents,
|
||||
ContentsSpecialFields = this.ContentsSpecialFields?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -387,6 +406,9 @@ namespace RedumpLib.Data
|
||||
[JsonProperty(PropertyName = "d_protection", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string Protection { get; set; }
|
||||
|
||||
[JsonIgnore]
|
||||
public Dictionary<string, List<string>> FullProtections { get; set; }
|
||||
|
||||
[JsonProperty(PropertyName = "d_securom", NullValueHandling = NullValueHandling.Ignore)]
|
||||
public string SecuROMData { get; set; }
|
||||
|
||||
@@ -398,6 +420,7 @@ namespace RedumpLib.Data
|
||||
LibCrypt = this.LibCrypt,
|
||||
LibCryptData = this.LibCryptData,
|
||||
Protection = this.Protection,
|
||||
FullProtections = this.FullProtections?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value),
|
||||
SecuROMData = this.SecuROMData,
|
||||
};
|
||||
}
|
||||
@@ -497,4 +520,42 @@ namespace RedumpLib.Data
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumping info section for moderation
|
||||
/// </summary>
|
||||
public class DumpingInfoSection : ICloneable
|
||||
{
|
||||
// Name not defined by Redump
|
||||
[JsonProperty(PropertyName = "d_dumping_program", Required = Required.AllowNull)]
|
||||
public string DumpingProgram { get; set; }
|
||||
|
||||
// Name not defined by Redump
|
||||
[JsonProperty(PropertyName = "d_drive_manufacturer", Required = Required.AllowNull)]
|
||||
public string Manufacturer { get; set; }
|
||||
|
||||
// Name not defined by Redump
|
||||
[JsonProperty(PropertyName = "d_drive_model", Required = Required.AllowNull)]
|
||||
public string Model { get; set; }
|
||||
|
||||
// Name not defined by Redump
|
||||
[JsonProperty(PropertyName = "d_drive_firmware", Required = Required.AllowNull)]
|
||||
public string Firmware { get; set; }
|
||||
|
||||
// Name not defined by Redump
|
||||
[JsonProperty(PropertyName = "d_reported_disc_type", Required = Required.AllowNull)]
|
||||
public string ReportedDiscType { get; set; }
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
return new DumpingInfoSection
|
||||
{
|
||||
DumpingProgram = this.DumpingProgram,
|
||||
Manufacturer = this.Manufacturer,
|
||||
Model = this.Model,
|
||||
Firmware = this.Firmware,
|
||||
ReportedDiscType = this.ReportedDiscType,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win-x86;win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
808
RedumpLib/Web/RedumpHttpClient.cs
Normal file
808
RedumpLib/Web/RedumpHttpClient.cs
Normal file
@@ -0,0 +1,808 @@
|
||||
#if NET6_0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace RedumpLib.Web
|
||||
{
|
||||
public class RedumpHttpClient : HttpClient
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Determines if user is logged into Redump
|
||||
/// </summary>
|
||||
public bool LoggedIn { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the user is a staff member
|
||||
/// </summary>
|
||||
public bool IsStaff { get; private set; } = false;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public RedumpHttpClient()
|
||||
: base(new HttpClientHandler { UseCookies = true })
|
||||
{
|
||||
}
|
||||
|
||||
#region Credentials
|
||||
|
||||
/// <summary>
|
||||
/// Validate supplied credentials
|
||||
/// </summary>
|
||||
public async static Task<(bool?, string)> ValidateCredentials(string username, string password)
|
||||
{
|
||||
// If options are invalid or we're missing something key, just return
|
||||
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||
return (false, null);
|
||||
|
||||
// Try logging in with the supplied credentials otherwise
|
||||
using RedumpHttpClient httpClient = new();
|
||||
|
||||
bool? loggedIn = await httpClient.Login(username, password);
|
||||
if (loggedIn == true)
|
||||
return (true, "Redump username and password accepted!");
|
||||
else if (loggedIn == false)
|
||||
return (false, "Redump username and password denied!");
|
||||
else
|
||||
return (null, "An error occurred validating your credentials!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Login to Redump, if possible
|
||||
/// </summary>
|
||||
/// <param name="username">Redump username</param>
|
||||
/// <param name="password">Redump password</param>
|
||||
/// <returns>True if the user could be logged in, false otherwise, null on error</returns>
|
||||
public async Task<bool?> Login(string username, string password)
|
||||
{
|
||||
// Credentials verification
|
||||
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
Console.WriteLine("Credentials entered, will attempt Redump login...");
|
||||
}
|
||||
else if (!string.IsNullOrWhiteSpace(username) && string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
Console.WriteLine("Only a username was specified, will not attempt Redump login...");
|
||||
return false;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(username))
|
||||
{
|
||||
Console.WriteLine("No credentials entered, will not attempt Redump login...");
|
||||
return false;
|
||||
}
|
||||
|
||||
// HTTP encode the password
|
||||
password = WebUtility.UrlEncode(password);
|
||||
|
||||
// Attempt to login up to 3 times
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get the current token from the login page
|
||||
var loginPage = await GetStringAsync(Constants.LoginUrl);
|
||||
string token = Constants.TokenRegex.Match(loginPage).Groups[1].Value;
|
||||
|
||||
// Construct the login request
|
||||
var postContent = new StringContent($"form_sent=1&redirect_url=&csrf_token={token}&req_username={username}&req_password={password}&save_pass=0", Encoding.UTF8);
|
||||
postContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/x-www-form-urlencoded");
|
||||
|
||||
// Send the login request and get the result
|
||||
var response = await PostAsync(Constants.LoginUrl, postContent);
|
||||
string responseContent = await response?.Content?.ReadAsStringAsync();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(responseContent))
|
||||
{
|
||||
Console.WriteLine($"An error occurred while trying to log in on attempt {i}: No response");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (responseContent.Contains("Incorrect username and/or password."))
|
||||
{
|
||||
Console.WriteLine("Invalid credentials entered, continuing without logging in...");
|
||||
return false;
|
||||
}
|
||||
|
||||
// The user was able to be logged in
|
||||
Console.WriteLine("Credentials accepted! Logged into Redump...");
|
||||
LoggedIn = true;
|
||||
|
||||
// If the user is a moderator or staff, set accordingly
|
||||
if (responseContent.Contains("http://forum.redump.org/forum/9/staff/"))
|
||||
IsStaff = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception occurred while trying to log in on attempt {i}: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("Could not login to Redump in 3 attempts, continuing without logging in...");
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Single Page Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Process a Redump site page as a list of possible IDs or disc page
|
||||
/// </summary>
|
||||
/// <param name="url">Base URL to download using</param>
|
||||
/// <returns>List of IDs from the page, empty on error</returns>
|
||||
public async Task<List<int>> CheckSingleSitePage(string url)
|
||||
{
|
||||
List<int> ids = new();
|
||||
|
||||
// Try up to 3 times to retrieve the data
|
||||
string dumpsPage = await DownloadString(url, retries: 3);
|
||||
|
||||
// If we have no dumps left
|
||||
if (dumpsPage == null || dumpsPage.Contains("No discs found."))
|
||||
return ids;
|
||||
|
||||
// If we have a single disc page already
|
||||
if (dumpsPage.Contains("<b>Download:</b>"))
|
||||
{
|
||||
var value = Regex.Match(dumpsPage, @"/disc/(\d+)/sfv/").Groups[1].Value;
|
||||
if (int.TryParse(value, out int id))
|
||||
ids.Add(id);
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
// Otherwise, traverse each dump on the page
|
||||
var matches = Constants.DiscRegex.Matches(dumpsPage);
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (int.TryParse(match.Groups[1].Value, out int value))
|
||||
ids.Add(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception has occurred: {ex}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a Redump site page as a list of possible IDs or disc page
|
||||
/// </summary>
|
||||
/// <param name="url">Base URL to download using</param>
|
||||
/// <param name="outDir">Output directory to save data to</param>
|
||||
/// <param name="failOnSingle">True to return on first error, false otherwise</param>
|
||||
/// <returns>True if the page could be downloaded, false otherwise</returns>
|
||||
public async Task<bool> CheckSingleSitePage(string url, string outDir, bool failOnSingle)
|
||||
{
|
||||
// Try up to 3 times to retrieve the data
|
||||
string dumpsPage = await DownloadString(url, retries: 3);
|
||||
|
||||
// If we have no dumps left
|
||||
if (dumpsPage == null || dumpsPage.Contains("No discs found."))
|
||||
return false;
|
||||
|
||||
// If we have a single disc page already
|
||||
if (dumpsPage.Contains("<b>Download:</b>"))
|
||||
{
|
||||
var value = Regex.Match(dumpsPage, @"/disc/(\d+)/sfv/").Groups[1].Value;
|
||||
if (int.TryParse(value, out int id))
|
||||
{
|
||||
bool downloaded = await DownloadSingleSiteID(id, outDir, false);
|
||||
if (!downloaded && failOnSingle)
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Otherwise, traverse each dump on the page
|
||||
var matches = Constants.DiscRegex.Matches(dumpsPage);
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (int.TryParse(match.Groups[1].Value, out int value))
|
||||
{
|
||||
bool downloaded = await DownloadSingleSiteID(value, outDir, false);
|
||||
if (!downloaded && failOnSingle)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception has occurred: {ex}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a Redump WIP page as a list of possible IDs or disc page
|
||||
/// </summary>
|
||||
/// <param name="wc">RedumpWebClient to access the packs</param>
|
||||
/// <returns>List of IDs from the page, empty on error</returns>
|
||||
public async Task<List<int>> CheckSingleWIPPage(string url)
|
||||
{
|
||||
List<int> ids = new();
|
||||
|
||||
// Try up to 3 times to retrieve the data
|
||||
string dumpsPage = await DownloadString(url, retries: 3);
|
||||
|
||||
// If we have no dumps left
|
||||
if (dumpsPage == null || dumpsPage.Contains("No discs found."))
|
||||
return ids;
|
||||
|
||||
// Otherwise, traverse each dump on the page
|
||||
var matches = Constants.NewDiscRegex.Matches(dumpsPage);
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (int.TryParse(match.Groups[2].Value, out int value))
|
||||
ids.Add(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception has occurred: {ex}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a Redump WIP page as a list of possible IDs or disc page
|
||||
/// </summary>
|
||||
/// <param name="wc">RedumpWebClient to access the packs</param>
|
||||
/// <param name="outDir">Output directory to save data to</param>
|
||||
/// <param name="failOnSingle">True to return on first error, false otherwise</param>
|
||||
/// <returns>True if the page could be downloaded, false otherwise</returns>
|
||||
public async Task<bool> CheckSingleWIPPage(string url, string outDir, bool failOnSingle)
|
||||
{
|
||||
// Try up to 3 times to retrieve the data
|
||||
string dumpsPage = await DownloadString(url, retries: 3);
|
||||
|
||||
// If we have no dumps left
|
||||
if (dumpsPage == null || dumpsPage.Contains("No discs found."))
|
||||
return false;
|
||||
|
||||
// Otherwise, traverse each dump on the page
|
||||
var matches = Constants.NewDiscRegex.Matches(dumpsPage);
|
||||
foreach (Match match in matches)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (int.TryParse(match.Groups[2].Value, out int value))
|
||||
{
|
||||
bool downloaded = await DownloadSingleWIPID(value, outDir, false);
|
||||
if (!downloaded && failOnSingle)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception has occurred: {ex}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Download Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Download a single pack
|
||||
/// </summary>
|
||||
/// <param name="url">Base URL to download using</param>
|
||||
/// <param name="system">System to download packs for</param>
|
||||
/// <returns>Byte array containing the downloaded pack, null on error</returns>
|
||||
public async Task<byte[]> DownloadSinglePack(string url, RedumpSystem? system)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await GetByteArrayAsync(string.Format(url, system.ShortName()));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception has occurred: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download a single pack
|
||||
/// </summary>
|
||||
/// <param name="url">Base URL to download using</param>
|
||||
/// <param name="system">System to download packs for</param>
|
||||
/// <param name="outDir">Output directory to save data to</param>
|
||||
/// <param name="subfolder">Named subfolder for the pack, used optionally</param>
|
||||
public async Task<bool> DownloadSinglePack(string url, RedumpSystem? system, string outDir, string subfolder)
|
||||
{
|
||||
try
|
||||
{
|
||||
// If no output directory is defined, use the current directory instead
|
||||
if (string.IsNullOrWhiteSpace(outDir))
|
||||
outDir = Environment.CurrentDirectory;
|
||||
|
||||
string tempfile = Path.Combine(outDir, "tmp" + Guid.NewGuid().ToString());
|
||||
string packUri = string.Format(url, system.ShortName());
|
||||
|
||||
// Make the call to get the pack
|
||||
string remoteFileName = await DownloadFile(packUri, tempfile);
|
||||
MoveOrDelete(tempfile, remoteFileName, outDir, subfolder);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception has occurred: {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download an individual site ID data, if possible
|
||||
/// </summary>
|
||||
/// <param name="id">Redump disc ID to retrieve</param>
|
||||
/// <returns>String containing the page contents if successful, null on error</returns>
|
||||
public async Task<string> DownloadSingleSiteID(int id)
|
||||
{
|
||||
string paddedId = id.ToString().PadLeft(5, '0');
|
||||
Console.WriteLine($"Processing ID: {paddedId}");
|
||||
try
|
||||
{
|
||||
// Try up to 3 times to retrieve the data
|
||||
string discPageUri = string.Format(Constants.DiscPageUrl, +id);
|
||||
string discPage = await DownloadString(discPageUri, retries: 3);
|
||||
|
||||
if (discPage == null || discPage.Contains($"Disc with ID \"{id}\" doesn't exist"))
|
||||
{
|
||||
Console.WriteLine($"ID {paddedId} could not be found!");
|
||||
return null;
|
||||
}
|
||||
|
||||
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
|
||||
return discPage;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception has occurred: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download an individual site ID data, if possible
|
||||
/// </summary>
|
||||
/// <param name="id">Redump disc ID to retrieve</param>
|
||||
/// <param name="outDir">Output directory to save data to</param>
|
||||
/// <param name="rename">True to rename deleted entries, false otherwise</param>
|
||||
/// <returns>True if all data was downloaded, false otherwise</returns>
|
||||
public async Task<bool> DownloadSingleSiteID(int id, string outDir, bool rename)
|
||||
{
|
||||
// If no output directory is defined, use the current directory instead
|
||||
if (string.IsNullOrWhiteSpace(outDir))
|
||||
outDir = Environment.CurrentDirectory;
|
||||
|
||||
string paddedId = id.ToString().PadLeft(5, '0');
|
||||
string paddedIdDir = Path.Combine(outDir, paddedId);
|
||||
Console.WriteLine($"Processing ID: {paddedId}");
|
||||
try
|
||||
{
|
||||
// Try up to 3 times to retrieve the data
|
||||
string discPageUri = string.Format(Constants.DiscPageUrl, +id);
|
||||
string discPage = await DownloadString(discPageUri, retries: 3);
|
||||
|
||||
if (discPage == null || discPage.Contains($"Disc with ID \"{id}\" doesn't exist"))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (rename)
|
||||
{
|
||||
if (Directory.Exists(paddedIdDir) && rename)
|
||||
Directory.Move(paddedIdDir, paddedIdDir + "-deleted");
|
||||
else
|
||||
Directory.CreateDirectory(paddedIdDir + "-deleted");
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
Console.WriteLine($"ID {paddedId} could not be found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the page has been updated since the last time it was downloaded, if possible
|
||||
if (File.Exists(Path.Combine(paddedIdDir, "disc.html")))
|
||||
{
|
||||
// Read in the cached file
|
||||
var oldDiscPage = File.ReadAllText(Path.Combine(paddedIdDir, "disc.html"));
|
||||
|
||||
// Check for the last modified date in both pages
|
||||
var oldResult = Constants.LastModifiedRegex.Match(oldDiscPage);
|
||||
var newResult = Constants.LastModifiedRegex.Match(discPage);
|
||||
|
||||
// If both pages contain the same modified date, skip it
|
||||
if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value)
|
||||
{
|
||||
Console.WriteLine($"ID {paddedId} has not been changed since last download");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If neither page contains a modified date, skip it
|
||||
else if (!oldResult.Success && !newResult.Success)
|
||||
{
|
||||
Console.WriteLine($"ID {paddedId} has not been changed since last download");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create ID subdirectory
|
||||
Directory.CreateDirectory(paddedIdDir);
|
||||
|
||||
// View Edit History
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/changes/\""))
|
||||
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.ChangesExt, Path.Combine(paddedIdDir, "changes.html"));
|
||||
|
||||
// CUE
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/cue/\""))
|
||||
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.CueExt, Path.Combine(paddedIdDir, paddedId + ".cue"));
|
||||
|
||||
// Edit disc
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/edit/\""))
|
||||
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.EditExt, Path.Combine(paddedIdDir, "edit.html"));
|
||||
|
||||
// GDI
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/gdi/\""))
|
||||
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.GdiExt, Path.Combine(paddedIdDir, paddedId + ".gdi"));
|
||||
|
||||
// KEYS
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/key/\""))
|
||||
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.KeyExt, Path.Combine(paddedIdDir, paddedId + ".key"));
|
||||
|
||||
// LSD
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/lsd/\""))
|
||||
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.LsdExt, Path.Combine(paddedIdDir, paddedId + ".lsd"));
|
||||
|
||||
// MD5
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/md5/\""))
|
||||
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.Md5Ext, Path.Combine(paddedIdDir, paddedId + ".md5"));
|
||||
|
||||
// Review WIP entry
|
||||
if (Constants.NewDiscRegex.IsMatch(discPage))
|
||||
{
|
||||
var match = Constants.NewDiscRegex.Match(discPage);
|
||||
_ = await DownloadFile(string.Format(Constants.WipDiscPageUrl, match.Groups[2].Value), Path.Combine(paddedIdDir, "newdisc.html"));
|
||||
}
|
||||
|
||||
// SBI
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/sbi/\""))
|
||||
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.SbiExt, Path.Combine(paddedIdDir, paddedId + ".sbi"));
|
||||
|
||||
// SFV
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/sfv/\""))
|
||||
await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.SfvExt, Path.Combine(paddedIdDir, paddedId + ".sfv"));
|
||||
|
||||
// SHA1
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/sha1/\""))
|
||||
_ = await DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.Sha1Ext, Path.Combine(paddedIdDir, paddedId + ".sha1"));
|
||||
|
||||
// HTML (Last in case of errors)
|
||||
using (var discStreamWriter = File.CreateText(Path.Combine(paddedIdDir, "disc.html")))
|
||||
{
|
||||
discStreamWriter.Write(discPage);
|
||||
}
|
||||
|
||||
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception has occurred: {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download an individual WIP ID data, if possible
|
||||
/// </summary>
|
||||
/// <param name="id">Redump WIP disc ID to retrieve</param>
|
||||
/// <returns>String containing the page contents if successful, null on error</returns>
|
||||
public async Task<string> DownloadSingleWIPID(int id)
|
||||
{
|
||||
string paddedId = id.ToString().PadLeft(5, '0');
|
||||
Console.WriteLine($"Processing ID: {paddedId}");
|
||||
try
|
||||
{
|
||||
// Try up to 3 times to retrieve the data
|
||||
string discPageUri = string.Format(Constants.WipDiscPageUrl, +id);
|
||||
string discPage = await DownloadString(discPageUri, retries: 3);
|
||||
|
||||
if (discPage == null || discPage.Contains($"WIP disc with ID \"{id}\" doesn't exist"))
|
||||
{
|
||||
Console.WriteLine($"ID {paddedId} could not be found!");
|
||||
return null;
|
||||
}
|
||||
|
||||
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
|
||||
return discPage;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception has occurred: {ex}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download an individual WIP ID data, if possible
|
||||
/// </summary>
|
||||
/// <param name="id">Redump WIP disc ID to retrieve</param>
|
||||
/// <param name="outDir">Output directory to save data to</param>
|
||||
/// <param name="rename">True to rename deleted entries, false otherwise</param>
|
||||
/// <returns>True if all data was downloaded, false otherwise</returns>
|
||||
public async Task<bool> DownloadSingleWIPID(int id, string outDir, bool rename)
|
||||
{
|
||||
// If no output directory is defined, use the current directory instead
|
||||
if (string.IsNullOrWhiteSpace(outDir))
|
||||
outDir = Environment.CurrentDirectory;
|
||||
|
||||
string paddedId = id.ToString().PadLeft(5, '0');
|
||||
string paddedIdDir = Path.Combine(outDir, paddedId);
|
||||
Console.WriteLine($"Processing ID: {paddedId}");
|
||||
try
|
||||
{
|
||||
// Try up to 3 times to retrieve the data
|
||||
string discPageUri = string.Format(Constants.WipDiscPageUrl, +id);
|
||||
string discPage = await DownloadString(discPageUri, retries: 3);
|
||||
|
||||
if (discPage == null || discPage.Contains($"WIP disc with ID \"{id}\" doesn't exist"))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (rename)
|
||||
{
|
||||
if (Directory.Exists(paddedIdDir) && rename)
|
||||
Directory.Move(paddedIdDir, paddedIdDir + "-deleted");
|
||||
else
|
||||
Directory.CreateDirectory(paddedIdDir + "-deleted");
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
Console.WriteLine($"ID {paddedId} could not be found!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the page has been updated since the last time it was downloaded, if possible
|
||||
if (File.Exists(Path.Combine(paddedIdDir, "disc.html")))
|
||||
{
|
||||
// Read in the cached file
|
||||
var oldDiscPage = File.ReadAllText(Path.Combine(paddedIdDir, "disc.html"));
|
||||
|
||||
// Check for the full match ID in both pages
|
||||
var oldResult = Constants.FullMatchRegex.Match(oldDiscPage);
|
||||
var newResult = Constants.FullMatchRegex.Match(discPage);
|
||||
|
||||
// If both pages contain the same ID, skip it
|
||||
if (oldResult.Success && newResult.Success && oldResult.Groups[1].Value == newResult.Groups[1].Value)
|
||||
{
|
||||
Console.WriteLine($"ID {paddedId} has not been changed since last download");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If neither page contains an ID, skip it
|
||||
else if (!oldResult.Success && !newResult.Success)
|
||||
{
|
||||
Console.WriteLine($"ID {paddedId} has not been changed since last download");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create ID subdirectory
|
||||
Directory.CreateDirectory(paddedIdDir);
|
||||
|
||||
// HTML
|
||||
using (var discStreamWriter = File.CreateText(Path.Combine(paddedIdDir, "disc.html")))
|
||||
{
|
||||
discStreamWriter.Write(discPage);
|
||||
}
|
||||
|
||||
Console.WriteLine($"ID {paddedId} has been successfully downloaded");
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An exception has occurred: {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Download a set of packs
|
||||
/// </summary>
|
||||
/// <param name="url">Base URL to download using</param>
|
||||
/// <param name="system">Systems to download packs for</param>
|
||||
/// <param name="title">Name of the pack that is downloading</param>
|
||||
public async Task<Dictionary<RedumpSystem, byte[]>> DownloadPacks(string url, RedumpSystem?[] systems, string title)
|
||||
{
|
||||
var packsDictionary = new Dictionary<RedumpSystem, byte[]>();
|
||||
|
||||
Console.WriteLine($"Downloading {title}");
|
||||
foreach (var system in systems)
|
||||
{
|
||||
// If the system is invalid, we can't do anything
|
||||
if (system == null || !system.IsAvailable())
|
||||
continue;
|
||||
|
||||
// If we didn't have credentials
|
||||
if (!LoggedIn && system.IsBanned())
|
||||
continue;
|
||||
|
||||
// If the system is unknown, we can't do anything
|
||||
string longName = system.LongName();
|
||||
if (string.IsNullOrWhiteSpace(longName))
|
||||
continue;
|
||||
|
||||
Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName.Length - 1)}");
|
||||
byte[] pack = await DownloadSinglePack(url, system);
|
||||
if (pack != null)
|
||||
packsDictionary.Add(system.Value, pack);
|
||||
}
|
||||
|
||||
Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}");
|
||||
Console.WriteLine();
|
||||
|
||||
return packsDictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download a set of packs
|
||||
/// </summary>
|
||||
/// <param name="url">Base URL to download using</param>
|
||||
/// <param name="systems">Systems to download packs for</param>
|
||||
/// <param name="title">Name of the pack that is downloading</param>
|
||||
/// <param name="outDir">Output directory to save data to</param>
|
||||
/// <param name="subfolder">Named subfolder for the pack, used optionally</param>
|
||||
public async Task<bool> DownloadPacks(string url, RedumpSystem?[] systems, string title, string outDir, string subfolder)
|
||||
{
|
||||
Console.WriteLine($"Downloading {title}");
|
||||
foreach (var system in systems)
|
||||
{
|
||||
// If the system is invalid, we can't do anything
|
||||
if (system == null || !system.IsAvailable())
|
||||
continue;
|
||||
|
||||
// If we didn't have credentials
|
||||
if (!LoggedIn && system.IsBanned())
|
||||
continue;
|
||||
|
||||
// If the system is unknown, we can't do anything
|
||||
string longName = system.LongName();
|
||||
if (string.IsNullOrWhiteSpace(longName))
|
||||
continue;
|
||||
|
||||
Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName.Length - 1)}");
|
||||
await DownloadSinglePack(url, system, outDir, subfolder);
|
||||
}
|
||||
|
||||
Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}");
|
||||
Console.WriteLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download from a URI to a local file
|
||||
/// </summary>
|
||||
/// <param name="uri">Remote URI to retrieve</param>
|
||||
/// <param name="fileName">Filename to write to</param>
|
||||
/// <returns>The remote filename from the URI, null on error</returns>
|
||||
private async Task<string> DownloadFile(string uri, string fileName)
|
||||
{
|
||||
// Make the call to get the file
|
||||
var response = await GetAsync(uri);
|
||||
if (response?.Content?.Headers == null || !response.IsSuccessStatusCode)
|
||||
{
|
||||
Console.WriteLine($"Could not download {uri}");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Copy the data to a local temp file
|
||||
using (var responseStream = await response.Content.ReadAsStreamAsync())
|
||||
using (var tempFileStream = File.OpenWrite(fileName))
|
||||
{
|
||||
responseStream.CopyTo(tempFileStream);
|
||||
}
|
||||
|
||||
return response.Content.Headers.ContentDisposition?.FileName?.Replace("\"", "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download from a URI to a string
|
||||
/// </summary>
|
||||
/// <param name="uri">Remote URI to retrieve</param>
|
||||
/// <param name="retries">Number of times to retry on error</param>
|
||||
/// <returns>String from the URI, null on error</returns>
|
||||
private async Task<string> DownloadString(string uri, int retries = 3)
|
||||
{
|
||||
// Only retry a positive number of times
|
||||
if (retries <= 0)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < retries; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await GetStringAsync(uri);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Move a tempfile to a new name unless it aleady exists, in which case, delete the tempfile
|
||||
/// </summary>
|
||||
/// <param name="tempfile">Path to existing temporary file</param>
|
||||
/// <param name="newfile">Path to new output file</param>
|
||||
/// <param name="outDir">Output directory to save data to</param>
|
||||
/// <param name="subfolder">Optional subfolder to append to the path</param>
|
||||
private static void MoveOrDelete(string tempfile, string newfile, string outDir, string subfolder)
|
||||
{
|
||||
// If we don't have a file to move to, just delete the temp file
|
||||
if (string.IsNullOrWhiteSpace(newfile))
|
||||
{
|
||||
File.Delete(tempfile);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a subfolder, create it and update the newfile name
|
||||
if (!string.IsNullOrWhiteSpace(subfolder))
|
||||
{
|
||||
if (!Directory.Exists(Path.Combine(outDir, subfolder)))
|
||||
Directory.CreateDirectory(Path.Combine(outDir, subfolder));
|
||||
|
||||
newfile = Path.Combine(subfolder, newfile);
|
||||
}
|
||||
|
||||
// If the file already exists, don't overwrite it
|
||||
if (File.Exists(Path.Combine(outDir, newfile)))
|
||||
File.Delete(tempfile);
|
||||
else
|
||||
File.Move(tempfile, Path.Combine(outDir, newfile));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
#if NET48 || NETSTANDARD2_1
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
@@ -30,31 +32,60 @@ namespace RedumpLib.Web
|
||||
public string GetLastFilename()
|
||||
{
|
||||
// If the response headers are null or empty
|
||||
if (ResponseHeaders == null || ResponseHeaders.Count != 0)
|
||||
if (ResponseHeaders == null || ResponseHeaders.Count == 0)
|
||||
return null;
|
||||
|
||||
|
||||
// If we don't have the response header we care about
|
||||
string headerValue = ResponseHeaders.Get("Content-Disposition");
|
||||
if (string.IsNullOrWhiteSpace(headerValue))
|
||||
return null;
|
||||
|
||||
// Extract the filename from the value
|
||||
#if NETSTANDARD2_1
|
||||
return headerValue[(headerValue.IndexOf("filename=") + 9)..].Replace("\"", "");
|
||||
#else
|
||||
return headerValue.Substring(headerValue.IndexOf("filename=") + 9).Replace("\"", "");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override WebRequest GetWebRequest(Uri address)
|
||||
{
|
||||
WebRequest request = base.GetWebRequest(address);
|
||||
HttpWebRequest webRequest = request as HttpWebRequest;
|
||||
if (webRequest != null)
|
||||
{
|
||||
if (request is HttpWebRequest webRequest)
|
||||
webRequest.CookieContainer = m_container;
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate supplied credentials
|
||||
/// </summary>
|
||||
public static (bool?, string) ValidateCredentials(string username, string password)
|
||||
{
|
||||
// If options are invalid or we're missing something key, just return
|
||||
if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
|
||||
return (false, null);
|
||||
|
||||
// Try logging in with the supplied credentials otherwise
|
||||
#if NETSTANDARD2_1
|
||||
using RedumpWebClient wc = new RedumpWebClient();
|
||||
#else
|
||||
using (RedumpWebClient wc = new RedumpWebClient())
|
||||
{
|
||||
#endif
|
||||
bool? loggedIn = wc.Login(username, password);
|
||||
if (loggedIn == true)
|
||||
return (true, "Redump username and password accepted!");
|
||||
else if (loggedIn == false)
|
||||
return (false, "Redump username and password denied!");
|
||||
else
|
||||
return (null, "An error occurred validating your credentials!");
|
||||
#if NET48
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Login to Redump, if possible
|
||||
/// </summary>
|
||||
@@ -502,7 +533,7 @@ namespace RedumpLib.Web
|
||||
// View Edit History
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/changes/\""))
|
||||
DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.ChangesExt, Path.Combine(paddedIdDir, "changes.html"));
|
||||
|
||||
|
||||
// CUE
|
||||
if (discPage.Contains($"<a href=\"/disc/{id}/cue/\""))
|
||||
DownloadFile(string.Format(Constants.DiscPageUrl, +id) + Constants.CueExt, Path.Combine(paddedIdDir, paddedId + ".cue"));
|
||||
@@ -719,7 +750,7 @@ namespace RedumpLib.Web
|
||||
// If we didn't have credentials
|
||||
if (!LoggedIn && system.IsBanned())
|
||||
continue;
|
||||
|
||||
|
||||
// If the system is unknown, we can't do anything
|
||||
string longName = system.LongName();
|
||||
if (string.IsNullOrWhiteSpace(longName))
|
||||
@@ -765,7 +796,7 @@ namespace RedumpLib.Web
|
||||
|
||||
Console.Write($"\r{longName}{new string(' ', Console.BufferWidth - longName.Length - 1)}");
|
||||
DownloadSinglePack(url, system, outDir, subfolder);
|
||||
}
|
||||
}
|
||||
|
||||
Console.Write($"\rComplete!{new string(' ', Console.BufferWidth - 10)}");
|
||||
Console.WriteLine();
|
||||
@@ -778,7 +809,7 @@ namespace RedumpLib.Web
|
||||
/// <param name="newfile">Path to new output file</param>
|
||||
/// <param name="outDir">Output directory to save data to</param>
|
||||
/// <param name="subfolder">Optional subfolder to append to the path</param>
|
||||
private void MoveOrDelete(string tempfile, string newfile, string outDir, string subfolder)
|
||||
private static void MoveOrDelete(string tempfile, string newfile, string outDir, string subfolder)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(newfile))
|
||||
{
|
||||
@@ -802,3 +833,5 @@ namespace RedumpLib.Web
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
154
appveyor.yml
154
appveyor.yml
@@ -1,76 +1,110 @@
|
||||
# version format
|
||||
version: 2.2-{build}
|
||||
version: 2.4-{build}
|
||||
|
||||
# pull request template
|
||||
pull_requests:
|
||||
do_not_increment_build_number: true
|
||||
|
||||
# vm template
|
||||
image: Visual Studio 2019
|
||||
image: Visual Studio 2022
|
||||
|
||||
# environment variables
|
||||
environment:
|
||||
EnableNuGetPackageRestore: true
|
||||
|
||||
# msbuild configuration
|
||||
platform:
|
||||
- Any CPU
|
||||
configuration:
|
||||
- Debug
|
||||
|
||||
# install dependencies
|
||||
install:
|
||||
- ps: appveyor DownloadFile https://dist.nuget.org/win-x86-commandline/latest/nuget.exe
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
- git submodule update --init --recursive
|
||||
|
||||
# pre-build script
|
||||
before_build:
|
||||
- nuget restore
|
||||
|
||||
# build step
|
||||
build:
|
||||
verbosity: minimal
|
||||
project: MPF.sln
|
||||
build_script:
|
||||
- dotnet restore
|
||||
|
||||
# .NET Framework 4.8
|
||||
- msbuild MPF\MPF.csproj -target:Publish -property:TargetFramework=net48 -property:RuntimeIdentifiers=win7-x64
|
||||
- msbuild MPF.Check\MPF.Check.csproj -target:Publish -property:TargetFramework=net48 -property:RuntimeIdentifiers=win7-x64
|
||||
|
||||
# .NET 6.0
|
||||
- dotnet publish MPF\MPF.csproj --framework net6.0-windows --runtime win-x86 --self-contained true
|
||||
- dotnet publish MPF\MPF.csproj --framework net6.0-windows --runtime win-x64 --self-contained true
|
||||
#- dotnet publish MPF\MPF.csproj --framework net6.0-windows --runtime linux-x64 --self-contained true
|
||||
#- dotnet publish MPF\MPF.csproj --framework net6.0-windows --runtime osx-x64 --self-contained true
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj --framework net6.0 --runtime win-x86 --self-contained true -p:PublishSingleFile=true
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj --framework net6.0 --runtime win-x64 --self-contained true -p:PublishSingleFile=true
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj --framework net6.0 --runtime linux-x64 --self-contained true -p:PublishSingleFile=true
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj --framework net6.0 --runtime osx-x64 --self-contained true -p:PublishSingleFile=true
|
||||
|
||||
# post-build step
|
||||
after_build:
|
||||
- ps: appveyor DownloadFile https://github.com/aaru-dps/Aaru/releases/download/v5.3.0/aaru-5.3.0_windows_x64.zip
|
||||
|
||||
# Aaru
|
||||
- ps: appveyor DownloadFile https://github.com/aaru-dps/Aaru/releases/download/v5.3.2/aaru-5.3.2_windows_x64.zip
|
||||
- 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Debug\net48\publish\Programs\Aaru *
|
||||
- 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\Aaru *
|
||||
- 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Debug\net6.0-windows\win-x86\publish\Programs\Aaru *
|
||||
#- 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Debug\net6.0-windows\linux-x64\publish\Programs\Aaru *
|
||||
#- 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Debug\net6.0-windows\osx-x64\publish\Programs\Aaru *
|
||||
|
||||
# dd for Windows
|
||||
- ps: appveyor DownloadFile http://www.chrysocome.net/downloads/8ab730cd2a29e76ddd89be1f99357942/dd-0.6beta3.zip
|
||||
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/7453967/DiscImageCreator_20211101.zip
|
||||
- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net48\publish\Programs\DD *
|
||||
- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\DD *
|
||||
- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net6.0-windows\win-x86\publish\Programs\DD *
|
||||
#- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net6.0-windows\linux-x64\publish\Programs\DD *
|
||||
#- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net6.0-windows\osx-x64\publish\Programs\DD *
|
||||
|
||||
# DiscImageCreator
|
||||
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/10931241/DiscImageCreator_20230309.zip
|
||||
- 7z e DiscImageCreator_20230309.zip -oMPF\bin\Debug\net48\publish\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20230309.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\Creator Release_ANSI\*
|
||||
- 7z e DiscImageCreator_20230309.zip -oMPF\bin\DebugFix \net6.0-windows\win-x86\publish\Programs\Creator Release_ANSI\*
|
||||
#- 7z e DiscImageCreator_20230309.zip -oMPF\bin\Debug\net6.0-windows\linux-x64\publish\Programs\Creator Release_ANSI\*
|
||||
#- 7z e DiscImageCreator_20230309.zip -oMPF\bin\Debug\net6.0-windows\osx-x64\publish\Programs\Creator Release_ANSI\*
|
||||
|
||||
# Redumper
|
||||
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_106/redumper-2023.02.19_build106-win64.zip
|
||||
- 7z e redumper-2023.02.19_build106-win64.zip -oMPF\bin\Debug\net48\publish\Programs\Redumper redumper-2023.02.19_build106-win64\bin\*
|
||||
- 7z e redumper-2023.02.19_build106-win64.zip -oMPF\bin\Debug\net6.0-windows\win-x64\publish\Programs\Redumper redumper-2023.02.19_build106-win64\bin\*
|
||||
- 7z e redumper-2023.02.19_build106-win64.zip -oMPF\bin\Debug\net6.0-windows\win-x86\publish\Programs\Redumper redumper-2023.02.19_build106-win64\bin\*
|
||||
#- 7z e redumper-2023.02.19_build106-win64.zip -oMPF\bin\Debug\net6.0-windows\linux-x64\publish\Programs\Redumper redumper-2023.02.19_build106-win64\bin\*
|
||||
#- 7z e redumper-2023.02.19_build106-win64.zip -oMPF\bin\Debug\net6.0-windows\osx-x64\publish\Programs\Redumper redumper-2023.02.19_build106-win64\bin\*
|
||||
|
||||
# Subdump
|
||||
- ps: appveyor DownloadFile https://archive.org/download/subdump_fua_0x28/subdump_fua_0x28.zip
|
||||
- 7z e subdump_fua_0x28.zip -oMPF\bin\Debug\net48\publish *
|
||||
- mkdir MPF\bin\Debug\net48\publish\Programs\Subdump
|
||||
- mv MPF\bin\Debug\net48\publish\subdump_fua_0x28.exe MPF\bin\Debug\net48\publish\Programs\Subdump\subdump.exe
|
||||
#- 7z e subdump_fua_0x28.zip -oMPF\bin\Debug\net6.0 *
|
||||
#- mkdir MPF\bin\Debug\net6.0-windows\Programs\Subdump
|
||||
#- mv MPF\bin\Debug\net6.0-windows\subdump_fua_0x28.exe MPF\bin\Debug\net6.0-windows\Programs\Subdump\subdump.exe
|
||||
|
||||
- 7z x aaru-5.3.0_windows_x64.zip -oMPF\bin\Debug\net48\Programs\Aaru *
|
||||
#- 7z x aaru-5.3.0_windows_x64.zip -oMPF\bin\Debug\netcoreapp3.1\Programs\Aaru *
|
||||
#- 7z x aaru-5.3.0_windows_x64.zip -oMPF\bin\Debug\net5.0\Programs\Aaru *
|
||||
# MPF
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net48\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_net48.zip *
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net6.0-windows\win-x86\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_net6.0_win-x86.zip *
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net6.0-windows\win-x64\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_net6.0_win-x64.zip *
|
||||
#- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net6.0-windows\linux-x64\publish\
|
||||
#- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_net6.0_linux-x64.zip *
|
||||
#- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net6.0-windows\osx-x64\publish\
|
||||
#- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_net6.0_osx-x64.zip *
|
||||
|
||||
- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net48\Programs\DD *
|
||||
#- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\netcoreapp3.1\Programs\DD *
|
||||
#- 7z e dd-0.6beta3.zip -oMPF\bin\Debug\net5.0\Programs\DD *
|
||||
|
||||
- 7z e DiscImageCreator_20211101.zip -oMPF\bin\Debug\net48\Programs\Creator Release_ANSI\*
|
||||
#- 7z e DiscImageCreator_20211101.zip -oMPF\bin\Debug\netcoreapp3.1\Programs\Creator Release_ANSI\*
|
||||
#- 7z e DiscImageCreator_20211101.zip -oMPF\bin\Debug\net5.0\Programs\Creator Release_ANSI\*
|
||||
|
||||
- 7z e subdump_fua_0x28.zip -oMPF\bin\Debug\net48 *
|
||||
- mkdir MPF\bin\Debug\net48\Programs\Subdump
|
||||
- mv MPF\bin\Debug\net48\subdump_fua_0x28.exe MPF\bin\Debug\net48\Programs\Subdump\subdump.exe
|
||||
#- 7z e subdump_fua_0x28.zip -oMPF\bin\Debug\netcoreapp3.1 *
|
||||
#- mkdir MPF\bin\Debug\netcoreapp3.1\Programs\Subdump
|
||||
#- mv MPF\bin\Debug\netcoreapp3.1\subdump_fua_0x28.exe MPF\bin\Debug\netcoreapp3.1\Programs\Subdump\subdump.exe
|
||||
#- 7z e subdump_fua_0x28.zip -oMPF\bin\Debug\net5.0 *
|
||||
#- mkdir MPF\bin\Debug\net5.0\Programs\Subdump
|
||||
#- mv MPF\bin\Debug\net5.0\subdump_fua_0x28.exe MPF\bin\Debug\net5.0\Programs\Subdump\subdump.exe
|
||||
|
||||
- cd MPF\bin\Debug
|
||||
- 7z a MPF_net48.zip net48\*
|
||||
#- 7z a MPF_netcoreapp3.1.zip netcoreapp3.1\*
|
||||
#- 7z a MPF_net5.0.zip net5.0\*
|
||||
|
||||
- cd ..\..\..\MPF.Check\bin\Debug
|
||||
- 7z a MPF-Check_net48.zip net48\*
|
||||
#- 7z a MPF-Check_netcoreapp3.1.zip netcoreapp3.1\*
|
||||
#- 7z a MPF-Check_net5.0.zip net5.0\*
|
||||
# MPF.Check
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net48\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_net48.zip *
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\win-x86\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_net6.0_win-x86.zip *
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\win-x64\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_net6.0_win-x64.zip *
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\linux-x64\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_net6.0_linux-x64.zip *
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\osx-x64\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_net6.0_osx-x64.zip *
|
||||
|
||||
# success/failure tracking
|
||||
on_success:
|
||||
@@ -82,16 +116,24 @@ on_failure:
|
||||
|
||||
# artifact linking
|
||||
artifacts:
|
||||
- path: MPF\bin\Debug\MPF_net48.zip
|
||||
name: MPF (WPF UI) (.NET Framework 4.8)
|
||||
#- path: MPF\bin\Debug\MPF_netcoreapp3.1.zip
|
||||
# name: MPF (WPF UI) (.NET Core 3.1)
|
||||
#- path: MPF\bin\Debug\MPF_net5.0.zip
|
||||
# name: MPF (WPF UI) (.NET 5.0)
|
||||
- path: MPF_net48.zip
|
||||
name: MPF (.NET Framework 4.8)
|
||||
- path: MPF_net6.0_win-x86.zip
|
||||
name: MPF (.NET 6.0, Windows x86)
|
||||
- path: MPF_net6.0_win-x64.zip
|
||||
name: MPF (.NET 6.0, Windows x64)
|
||||
#- path: MPF_net6.0_linux-x64.zip
|
||||
# name: MPF (.NET 6.0, Linux x64)
|
||||
#- path: MPF_net6.0_osx-x64.zip
|
||||
# name: MPF (.NET 6.0, OSX x64)
|
||||
|
||||
- path: MPF.Check\bin\Debug\MPF-Check_net48.zip
|
||||
- path: MPF.Check_net48.zip
|
||||
name: MPF Check (.NET Framework 4.8)
|
||||
#- path: MPF.Check\bin\Debug\MPF-Check_netcoreapp3.1.zip
|
||||
# name: MPF Check (.NET Core 3.1)
|
||||
#- path: MPF.Check\bin\Debug\MPF-Check_net5.0.zip
|
||||
# name: MPF Check (.NET 5.0)
|
||||
- path: MPF.Check_net6.0_win-x86.zip
|
||||
name: MPF.Check (.NET 6.0, Windows x86)
|
||||
- path: MPF.Check_net6.0_win-x64.zip
|
||||
name: MPF.Check (.NET 6.0, Windows x64)
|
||||
- path: MPF.Check_net6.0_linux-x64.zip
|
||||
name: MPF.Check (.NET 6.0, Linux x64)
|
||||
- path: MPF.Check_net6.0_osx-x64.zip
|
||||
name: MPF.Check (.NET 6.0, OSX x64)
|
||||
Reference in New Issue
Block a user