mirror of
https://github.com/SabreTools/MPF.git
synced 2026-02-04 05:35:52 +00:00
Compare commits
441 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33b4be8b24 | ||
|
|
71a4edc8ba | ||
|
|
ceb305eb54 | ||
|
|
0b0d13dcf3 | ||
|
|
9f02368622 | ||
|
|
152010ee14 | ||
|
|
c6d5f0aea5 | ||
|
|
8c2ad64bb8 | ||
|
|
fa54d694b6 | ||
|
|
dc35b08624 | ||
|
|
4429515ba2 | ||
|
|
fdbc7b34e5 | ||
|
|
a1ab442cf0 | ||
|
|
9ed5c297f6 | ||
|
|
4ce9b214b0 | ||
|
|
7dcdadda75 | ||
|
|
f87a4d9fe2 | ||
|
|
e4e5d173f0 | ||
|
|
115b242d59 | ||
|
|
706bf8a431 | ||
|
|
87ecf1aa99 | ||
|
|
b5cf274333 | ||
|
|
4f4b6879b6 | ||
|
|
3b19463913 | ||
|
|
37386cd182 | ||
|
|
e04bdad16c | ||
|
|
e37f12705d | ||
|
|
5c8dc2c23a | ||
|
|
e9121f3b03 | ||
|
|
d68175db4e | ||
|
|
9d8181b0e2 | ||
|
|
6d657f268a | ||
|
|
3b3c5f823d | ||
|
|
09fc313492 | ||
|
|
316d0f6e54 | ||
|
|
a0033238bd | ||
|
|
5b1c6a7f46 | ||
|
|
8c0dff6552 | ||
|
|
43b230c84a | ||
|
|
f1b657011d | ||
|
|
e4d8ac8e1c | ||
|
|
08f44173dd | ||
|
|
54765c71fd | ||
|
|
01f8b49214 | ||
|
|
e8b0b3efaa | ||
|
|
f637938858 | ||
|
|
ae326c1d2f | ||
|
|
a4a1e6bf0a | ||
|
|
ecee44966e | ||
|
|
83437977ba | ||
|
|
8fcac1d425 | ||
|
|
705b5f1049 | ||
|
|
367d0c104b | ||
|
|
5d4bed2d9e | ||
|
|
63756192d8 | ||
|
|
b68ec78184 | ||
|
|
c55d5183fb | ||
|
|
f82e925944 | ||
|
|
c19f9ea173 | ||
|
|
76b2dd79ab | ||
|
|
c96ff23ad1 | ||
|
|
cd68b55b93 | ||
|
|
cad14d96f7 | ||
|
|
daaf9f1932 | ||
|
|
cb7502b450 | ||
|
|
ece142bbf1 | ||
|
|
611fee4605 | ||
|
|
791e2d0272 | ||
|
|
81742a4676 | ||
|
|
1ff3f2210c | ||
|
|
be9e4b91d5 | ||
|
|
854dcc5f95 | ||
|
|
29b71db33a | ||
|
|
2ee64b222a | ||
|
|
afda54f97b | ||
|
|
ad37c573b6 | ||
|
|
b6cb3104ae | ||
|
|
af9b0dc214 | ||
|
|
b5440032de | ||
|
|
a8e783235c | ||
|
|
fc97fe99e3 | ||
|
|
1c73b1133f | ||
|
|
908eccaafa | ||
|
|
e114d126c5 | ||
|
|
c07b4e4a28 | ||
|
|
06491a6611 | ||
|
|
b9a35850ad | ||
|
|
17a0c5d083 | ||
|
|
1b9523c799 | ||
|
|
ae80ecefc8 | ||
|
|
50ae32e3db | ||
|
|
2d43873398 | ||
|
|
a3df433fef | ||
|
|
35e71d8527 | ||
|
|
c5d07e4be1 | ||
|
|
0df5093b45 | ||
|
|
05d920d095 | ||
|
|
d2f7ac9843 | ||
|
|
857a302aad | ||
|
|
46de589791 | ||
|
|
f14961061c | ||
|
|
453fcf5cb1 | ||
|
|
24cdb27cdb | ||
|
|
24235c5896 | ||
|
|
c226f8cf58 | ||
|
|
b8f6c9f65f | ||
|
|
bb42e2db10 | ||
|
|
db544fd4b7 | ||
|
|
370c6bde5a | ||
|
|
d7965ee37f | ||
|
|
b2c6e07ed1 | ||
|
|
19cef20ceb | ||
|
|
058a1aeeaa | ||
|
|
d185077925 | ||
|
|
e3948ba91b | ||
|
|
4a30f94007 | ||
|
|
1ae27bf9e3 | ||
|
|
bc88103471 | ||
|
|
9ec6fbfe52 | ||
|
|
ebdb8de6b3 | ||
|
|
b8b56f6308 | ||
|
|
0726f5a402 | ||
|
|
90da5d1a7e | ||
|
|
bd3c518fa0 | ||
|
|
44272f60bf | ||
|
|
640ce8d854 | ||
|
|
74732058f2 | ||
|
|
22e480ccd1 | ||
|
|
32a0cb0394 | ||
|
|
88551bc2ed | ||
|
|
039af56f6a | ||
|
|
7a428e2add | ||
|
|
c8dd85eb72 | ||
|
|
7c7a19c5a0 | ||
|
|
9bedd26b24 | ||
|
|
09afdf52fb | ||
|
|
c69afe69dd | ||
|
|
ab18c7920a | ||
|
|
5af1841d13 | ||
|
|
8c324e3b8b | ||
|
|
d99099d587 | ||
|
|
fa7b46a516 | ||
|
|
f7c746b536 | ||
|
|
b6e109133f | ||
|
|
0d694c1bde | ||
|
|
47f45fa46f | ||
|
|
481f4b41d1 | ||
|
|
359ad87faa | ||
|
|
ba47cb7da2 | ||
|
|
8927c49963 | ||
|
|
21f9668093 | ||
|
|
371571d13f | ||
|
|
b231f82c4c | ||
|
|
1741326253 | ||
|
|
1878ef5ad6 | ||
|
|
ae42f5edd7 | ||
|
|
4362ed71e0 | ||
|
|
f02904ea49 | ||
|
|
abf4eb9b7c | ||
|
|
1f942977cc | ||
|
|
7edadd4739 | ||
|
|
cb09816c63 | ||
|
|
48f0a826ca | ||
|
|
9c10842924 | ||
|
|
def499769f | ||
|
|
5e1e61a441 | ||
|
|
4896a12ec5 | ||
|
|
b28ad5d3cd | ||
|
|
4cdbbcedf0 | ||
|
|
6ab63ba651 | ||
|
|
d36c5099f3 | ||
|
|
8ba8347a0c | ||
|
|
4c5184eac5 | ||
|
|
9914b94716 | ||
|
|
150e69eca5 | ||
|
|
f09923974b | ||
|
|
d98bc28930 | ||
|
|
ee4a7ab653 | ||
|
|
6b20aee320 | ||
|
|
a5849325f3 | ||
|
|
86d2d83fbe | ||
|
|
308e0d6937 | ||
|
|
3541bca2d0 | ||
|
|
2496099532 | ||
|
|
6001395181 | ||
|
|
fa2192a284 | ||
|
|
f09155936e | ||
|
|
a07fca6a7b | ||
|
|
5fe7a1dac8 | ||
|
|
16ed2f9595 | ||
|
|
9004d2bc7b | ||
|
|
cc98b38290 | ||
|
|
18ef0cddff | ||
|
|
d9ca55d96c | ||
|
|
816c94de58 | ||
|
|
c2ba58148a | ||
|
|
d9533f2448 | ||
|
|
5126d1854c | ||
|
|
6baaf132a7 | ||
|
|
98a30e6558 | ||
|
|
0912b78568 | ||
|
|
d92a1d566d | ||
|
|
ff40d18ed3 | ||
|
|
4d9cd85ba6 | ||
|
|
b6ae390cee | ||
|
|
4692028cfb | ||
|
|
3ada7db916 | ||
|
|
aa4b2f415d | ||
|
|
56e91bf177 | ||
|
|
228c752585 | ||
|
|
6ce7ccfa91 | ||
|
|
c8c98278b6 | ||
|
|
0fc57c58cf | ||
|
|
3dcac28488 | ||
|
|
4cd7073bf6 | ||
|
|
1af21e7aba | ||
|
|
52adcd0b46 | ||
|
|
8a91593e58 | ||
|
|
729f8273fc | ||
|
|
78df6f6583 | ||
|
|
dc8dae4df7 | ||
|
|
53db9dbf81 | ||
|
|
22755a4af9 | ||
|
|
ba28b414ba | ||
|
|
95d10ecb1e | ||
|
|
c5ab2c747a | ||
|
|
476c494f4e | ||
|
|
758c49c1cc | ||
|
|
d80ad3b3cf | ||
|
|
3d06f80703 | ||
|
|
7344460409 | ||
|
|
19f58d9dde | ||
|
|
ad9f39f832 | ||
|
|
d51117b058 | ||
|
|
0d65d5114a | ||
|
|
30fec3c3d0 | ||
|
|
8eb86fde90 | ||
|
|
7616c6b2ba | ||
|
|
e0742cdfc7 | ||
|
|
a19937d630 | ||
|
|
fa92402bc5 | ||
|
|
cdc3da5839 | ||
|
|
3430f8c1db | ||
|
|
12fd55e76c | ||
|
|
0013606d61 | ||
|
|
4d520d7d63 | ||
|
|
7bfe174680 | ||
|
|
4951e7bf42 | ||
|
|
d7d9c468ae | ||
|
|
32faa33ad3 | ||
|
|
8a5475380a | ||
|
|
1dd5542390 | ||
|
|
5156b89eca | ||
|
|
83ea04c880 | ||
|
|
8695d2981e | ||
|
|
e0c299e6f0 | ||
|
|
16ec54f389 | ||
|
|
998bf5d5fa | ||
|
|
07ec821e9a | ||
|
|
56cf8f3574 | ||
|
|
c6ebfcd6d9 | ||
|
|
6ceffce63d | ||
|
|
f43aafc00d | ||
|
|
ab598d8377 | ||
|
|
dbd876a1c1 | ||
|
|
fd102cb56b | ||
|
|
0afb49b657 | ||
|
|
be980fe0c4 | ||
|
|
08b4e8d602 | ||
|
|
e483e1cdc6 | ||
|
|
5c2dce78e2 | ||
|
|
26ea383775 | ||
|
|
64938fd7f1 | ||
|
|
22318ee3c1 | ||
|
|
b2b54a2706 | ||
|
|
40f04e0321 | ||
|
|
a7638b8063 | ||
|
|
2abcad2a0f | ||
|
|
e088b05de4 | ||
|
|
a31d894b79 | ||
|
|
34fae4572d | ||
|
|
1ecf0ad1fa | ||
|
|
7c7509020f | ||
|
|
7c6b118282 | ||
|
|
c154a844e3 | ||
|
|
088f1b8545 | ||
|
|
fc3c636bdd | ||
|
|
14c807c882 | ||
|
|
d0e9c51786 | ||
|
|
b428bc0ba0 | ||
|
|
6dbbb91438 | ||
|
|
2066d36424 | ||
|
|
29552cd39d | ||
|
|
640e7091cc | ||
|
|
af12c18d2e | ||
|
|
f28cf614c3 | ||
|
|
888cb8ec9f | ||
|
|
04035ac524 | ||
|
|
600374eb2d | ||
|
|
6ecb932a82 | ||
|
|
4cbc9ac109 | ||
|
|
5ee0b7345b | ||
|
|
cc55330fad | ||
|
|
6589380fdf | ||
|
|
bd45482bf7 | ||
|
|
e14c8a8f03 | ||
|
|
4ac00e9a1a | ||
|
|
edf983e304 | ||
|
|
01a69ef9b3 | ||
|
|
a368afc14a | ||
|
|
d83fed16f5 | ||
|
|
949df08690 | ||
|
|
ab3abb5b3e | ||
|
|
d16e73a530 | ||
|
|
3f8c55ca47 | ||
|
|
2e9aaa50f9 | ||
|
|
36951dc5da | ||
|
|
b39c8dd738 | ||
|
|
a1155cf9b7 | ||
|
|
1d151d213e | ||
|
|
6dc0c1438a | ||
|
|
8739569db6 | ||
|
|
0dcba9ce71 | ||
|
|
8d37b85e12 | ||
|
|
43cf8e1a45 | ||
|
|
7317553483 | ||
|
|
0b342e265c | ||
|
|
08359dd45f | ||
|
|
a24415cae6 | ||
|
|
59f8161308 | ||
|
|
51f955a14c | ||
|
|
ddebdef00c | ||
|
|
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 |
1
.github/ISSUE_TEMPLATE/feature-request.md
vendored
1
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -10,7 +10,6 @@ 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 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...
|
||||
|
||||
1
.github/ISSUE_TEMPLATE/informational.md
vendored
1
.github/ISSUE_TEMPLATE/informational.md
vendored
@@ -10,7 +10,6 @@ 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 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...
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/issue-report.md
vendored
2
.github/ISSUE_TEMPLATE/issue-report.md
vendored
@@ -10,7 +10,6 @@ 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 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.
|
||||
|
||||
@@ -27,6 +26,7 @@ What runtime version are you using?
|
||||
|
||||
- [ ] .NET Framework 4.8 running on (Operating System)
|
||||
- [ ] .NET 6.0 running on (Operating System)
|
||||
- [ ] .NET 7.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 "Aaru"]
|
||||
path = Aaru
|
||||
url = https://github.com/aaru-dps/Aaru.git
|
||||
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -10,9 +10,9 @@
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/MPF/bin/Debug/netcoreapp3.1/MPF.dll",
|
||||
"program": "${workspaceFolder}/MPF.Check/bin/Debug/net6.0/MPF.Check.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/MPF",
|
||||
"cwd": "${workspaceFolder}/MPF.Check",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"omnisharp.projectLoadTimeout": 480
|
||||
}
|
||||
16
.vscode/tasks.json
vendored
16
.vscode/tasks.json
vendored
@@ -7,21 +7,7 @@
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/MPF/MPF.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/MPF/MPF.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
"${workspaceFolder}/MPF.sln",
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
1
Aaru
1
Aaru
Submodule Aaru deleted from b1ac16c551
451
CHANGELIST.md
451
CHANGELIST.md
@@ -1,4 +1,444 @@
|
||||
### 2.7.3 (2023-10-26)
|
||||
|
||||
- Split InfoTool into 2 classes
|
||||
- Add path variants for PlayStation info
|
||||
- Convert Drive to use paths internally
|
||||
- Create skeleton for first-run
|
||||
- Show options window on first run
|
||||
- Fix drive letter issue in UI
|
||||
- Add first-run Options title, fix saving bug
|
||||
- Fix multiple handler invocation
|
||||
- Create method for applying theme
|
||||
- Fix drive letter check
|
||||
- Simply theme application
|
||||
- Add framework for deleteable files
|
||||
- Add deleteable file lists for Redumper and DIC
|
||||
- Add optional file deletion
|
||||
- Rearrange OptionsWindow to be easier to navigate
|
||||
- Fix up DumpEnvironment a bit
|
||||
- Add filename suffix setting (nw)
|
||||
- Wire through filename suffix
|
||||
- Expose suffix setting
|
||||
- Set UDF CD threshold at 800MB (Deterous)
|
||||
- Update package versions
|
||||
- Attempt to parse out PS5 params.json
|
||||
- Fix CRC32 hashing
|
||||
- Update XUnit packages
|
||||
- Update to BurnOutSharp 2.9.0
|
||||
- Update to MMI 3.0.0-preview.4
|
||||
- Fix two small nullability issues
|
||||
- Use Array.Empty in hasher
|
||||
- Always use relative path internally (Deterous)
|
||||
|
||||
### 2.7.2 (2023-10-17)
|
||||
|
||||
- Fix options loading for Check
|
||||
- Cleanup and gated code
|
||||
- Gate some switch expressions
|
||||
- Suppress some unnecessary messages
|
||||
- Disable dumping button when Redumper selected with unsupported media type (Deterous)
|
||||
- Improve check for which program supports which media (Deterous)
|
||||
- Remove code for getting Universal Hash from DIC logs (Deterous)
|
||||
- Fix Redumper retry count not showing
|
||||
- Enable parameters checkbox by default
|
||||
- Update Redumper to build 230
|
||||
- Fix GetPVD in dic (fuzz6001)
|
||||
- Get SecuROM data from Redumper
|
||||
- Clean up issue templates
|
||||
- Support Redumper 231 outputs
|
||||
|
||||
### 2.7.1 (2023-10-11)
|
||||
|
||||
- Add pull-all flag for Check
|
||||
- Fix errant version detection issue
|
||||
- Allow user-supplied information in Check
|
||||
- Add BE read flag for Redumper
|
||||
- Add generic drive flag for Redumper
|
||||
- Handle numeric disc titles as issue numbers
|
||||
- Unify handling of enable/disable events
|
||||
- Enable nullability in Check
|
||||
- Remove all but .NET 6 for AppVeyor packaging
|
||||
- Place message boxes at center of main window (Deterous)
|
||||
- Enable nullability in MPF
|
||||
- Enable nullability in MPF.UI.Core
|
||||
- Enable nullability in MPF.Test
|
||||
- Handle some suggested changes
|
||||
- Var-ify many instances
|
||||
- Version-gate some newer syntax
|
||||
- Fix Check always showing Help text
|
||||
- Version-gate some more newer syntax
|
||||
- Enable path browse button by default
|
||||
- Remove unnecessary namespacing
|
||||
|
||||
### 2.7.0 (2023-10-11)
|
||||
|
||||
- Attempt to replace NRT
|
||||
- Try alternate archive naming
|
||||
- Publish, but don't package, release builds
|
||||
- Add note about git being required
|
||||
- Try out using version number in AppVeyor
|
||||
- Job number not build ID
|
||||
- Fix errant space in variable name
|
||||
- Build number not job number
|
||||
- Use System.IO.Hashing for CRC32
|
||||
- Don't reverse the CRC32 output
|
||||
- Add submission info preamble
|
||||
- Fix missing comma
|
||||
- Remove IMAPI2 with no substitution
|
||||
- Remove framework exceptions
|
||||
- Slight reorganization of code
|
||||
- Update README with support information
|
||||
- Remove msbuild-specific AppVeyor items
|
||||
- Be trickier when it comes to type from size
|
||||
- Fix "detected" message
|
||||
- Be smarter about media type based on system
|
||||
- Consolidate into MPF.Core
|
||||
- Fix failing tests
|
||||
- Remove debug symbols in release builds (Deterous)
|
||||
- Allow nullability for modern .NET
|
||||
- Fix failing tests
|
||||
- Remove unnecessary include
|
||||
- Move element items to Core
|
||||
- Move LogLevel enumeration
|
||||
- Split logging code a bit more
|
||||
- Split info window code a bit more
|
||||
- Clarify build instructions in README (Deterous)
|
||||
- Split options window code a bit more
|
||||
- Use binding for more disc info textboxes
|
||||
- Handle Redump password changing better
|
||||
- Refine options window bindings
|
||||
- Refine options window bindings further
|
||||
- Refine info window bindings further
|
||||
- Move decoupled view models
|
||||
- Fix log output
|
||||
- Start migrating MainViewModel
|
||||
- Perform most of MainViewModel changes
|
||||
- Small updtes to MainViewModel
|
||||
- Remove MainWindow from MainViewModel
|
||||
- Use callback for logging, fix Options window
|
||||
- Remove WinForms from MainViewModel
|
||||
- Remove some message boxes from MainViewModel
|
||||
- Remove more Windows from MainViewModel
|
||||
- Fix null reference exception in disc type
|
||||
- Detected type and selected type are different
|
||||
- Remove message boxes from MainViewModel
|
||||
- Move MainViewModel to Core
|
||||
- Remove LogOutputViewModel
|
||||
- Consolidate some constants
|
||||
- Fix media type ordering
|
||||
- Fix dumping button not enabling
|
||||
- Recentralize some Check functionality
|
||||
- Fix media combobox not updating (Deterous)
|
||||
|
||||
### 2.6.6 (2023-10-04)
|
||||
|
||||
- Update Nuget packages
|
||||
- Update to MMI 3.0.0-preview.2
|
||||
- Remove errant character from script
|
||||
- Add placeholders for release builds
|
||||
- Fully sync AppVeyor build with script
|
||||
- Stop compiling Chime finally
|
||||
- Address some warnings and infos
|
||||
- Add setting for pulling comment/contents
|
||||
- Move to config.json
|
||||
- Omit track 0.2 and 00.2 from hash search
|
||||
- Tweak README again
|
||||
- Fix redumper EDC detection output
|
||||
- Fix XGD4 PIC reading
|
||||
- Ensure popups are topmost
|
||||
- Try out more UI functionality
|
||||
- Skip system detection on inactive drives
|
||||
- Fix path tests
|
||||
- Clean up csproj files
|
||||
- Update redumper to build 221
|
||||
- Ensure multisession info is populated
|
||||
- Be clearer with protection outputs
|
||||
|
||||
### 2.6.5 (2023-09-27)
|
||||
|
||||
- Normalize Redumper CSS output
|
||||
- Update redumper to build 219
|
||||
- Add .NET 7 to build scripts
|
||||
- Disable subdump download in AppVeyor
|
||||
- Normalize publish scripts
|
||||
- Update AppVeyor to match scripts
|
||||
- Add release publish scripts
|
||||
- Handle invalid characters when changing program
|
||||
- Fix options not saving on update
|
||||
- Combine build scripts
|
||||
- Force information window to top
|
||||
- Reset debug option to false
|
||||
|
||||
### 2.6.4 (2023-09-25)
|
||||
|
||||
- Add CD Projekt ID field
|
||||
- Fix speed setting in Aaru
|
||||
- Add helper for changing dumping program from UI
|
||||
- Handle extension changing only
|
||||
- Swap order of operations for changing program
|
||||
- Fix dumping path in DD
|
||||
- Fix PlayStation serial code region parsing (Deterous)
|
||||
- Retrofit README
|
||||
- Migrate to Nuget package for Redump
|
||||
- Remove dd for Windows
|
||||
- Migrate to Nuget package for XMID
|
||||
- Migrate to Nuget package for cuesheets
|
||||
- Migrate to Nuget package for PIC
|
||||
- Fix timestamp (Ragowit)
|
||||
- Not building against .NET Standard
|
||||
- Move RedumpSystemComboBoxItem
|
||||
- Move Constants
|
||||
- Move WPFCustomMessageBox
|
||||
- Remove EnableProgressProcessing
|
||||
- Remove EnableLogFormatting
|
||||
- Remove App references from LogViewModel
|
||||
- Remove log formatting code
|
||||
- Move LogOutput
|
||||
- Move to csproj tag for internals
|
||||
- Move OptionsWindow
|
||||
- Begin decoupling App
|
||||
- Fix build
|
||||
- More decoupling App
|
||||
- Make Options non-cloneable
|
||||
- Set parent on MainViewModel init
|
||||
- Make logger a local reference
|
||||
- Move options into MainViewModel
|
||||
- Move MainWindow
|
||||
- Add .NET 7 as a build target (not AppVeyor)
|
||||
- Fix tests that have been broken for a while
|
||||
|
||||
### 2.6.3 (2023-08-15)
|
||||
|
||||
- Update redumper to build 195
|
||||
- Add known .NET 6 limitations to README
|
||||
- Remove `_drive.txt` from required UIC outputs
|
||||
- Make `use` flag required for MPF.Check
|
||||
- Add dumping date to log
|
||||
- Non-zero data start only for audio discs
|
||||
- Update SafeDisc Sanitization (TheRogueArchivist)
|
||||
|
||||
### 2.6.2 (2023-07-25)
|
||||
|
||||
- Ensure custom parameters properly set
|
||||
- Universal hash only for audio discs
|
||||
- Support LibCrypt data from Redumper
|
||||
- Always show extension for Redumper
|
||||
- Normalize old universal hash text
|
||||
- Skip extra tracks during checking
|
||||
- Modify the track count on checking
|
||||
- Attempt to match universal hash
|
||||
- Fix .NET Framework 4.8 build
|
||||
- Fix universal hash URL generation
|
||||
- Add Windows publish script
|
||||
- Add *nix publish script
|
||||
- Add build instructions to README
|
||||
- Clarify build instructions
|
||||
|
||||
### 2.6.1 (2023-07-19)
|
||||
|
||||
- Simplify Redumper error value extraction
|
||||
- Don't pull comment fields that auto-populate
|
||||
- Set best compression levels for log files
|
||||
- Be more explicit about .NET 6 limitation
|
||||
- Set extensions for Redumper in UI
|
||||
- Fix comment field pulling again
|
||||
|
||||
### 2.6 (2023-07-14)
|
||||
|
||||
- Update README
|
||||
- Add warning to login tab
|
||||
- Pull hardware info from redumper log (fuzz6001)
|
||||
- Increase the version of AppVeyor (fuzz6001)
|
||||
- Add Windows 7 note to README
|
||||
- Update redumper to build 113
|
||||
- Split MMI invocation in drive listing
|
||||
- Update README
|
||||
- Add Hasbro iON
|
||||
- Get the write offset from the latest redumper (fuzz6001)
|
||||
- Update redumper to build 115
|
||||
- Update to DIC 20230401
|
||||
- Add DIC `.` notice to README
|
||||
- Resync with Redump
|
||||
- Update redumper to build 118
|
||||
- Clarify non-Redump systems
|
||||
- Add UltraCade
|
||||
- Re-enable BD33 and BD66
|
||||
- Add PIC models for BD (unused)
|
||||
- Handle PIC based on disc type
|
||||
- UMDs always have "2 layers"
|
||||
- Add dumping program selection to main UI
|
||||
- Update to DIC 20230413
|
||||
- Comment out `.` handling for DIC
|
||||
- Ensure dumping program box can enable/disable
|
||||
- Disable special SmartE handling for DIC
|
||||
- Remove path from PS1/PS2 serial
|
||||
- Make TOC file optional for CD/GD
|
||||
- Add datafile models
|
||||
- Add datafile helper method
|
||||
- Start migrating to datafile serialization
|
||||
- Prepare for future DIC changes
|
||||
- Add suppl support to Xbox
|
||||
- Support single digit subs
|
||||
- Fix info tool hash finding
|
||||
- Fix missing size for ISO data
|
||||
- Attempt to more accurately parse layerbreaks
|
||||
- Fix upcoming suppl DAT parsing
|
||||
- Ensure blank lines don't interfere
|
||||
- De-indent ringcode data
|
||||
- Be more specific with runtime identifiers
|
||||
- Fix subdump output path in AV config
|
||||
- Fix subdump mkdir path in AV config
|
||||
- Single file packing for .NET 6 again
|
||||
- Add missing BD disc type identifier string
|
||||
- Truncate PIC data for PS4/PS5
|
||||
- Add internal theme support with class
|
||||
- Add PIC identifier to SubmissionInfo
|
||||
- Fix other media type method
|
||||
- Fix non-zero offset text
|
||||
- Add executable listing for XSX
|
||||
- Update redumper to build 151
|
||||
- Add more safety to DAT generation
|
||||
- Get write offset from redumper 119 (fuzz6001)
|
||||
- Update hardware information pull from redumper (fuzz6001)
|
||||
- Add MCD/SS header support for Redumper
|
||||
- Fix MCD region(s) parsing
|
||||
- Update to DIC 20230606
|
||||
- Update redumper to build 166
|
||||
- Unblock Redumper DVD support
|
||||
- Support Redumper DVD layerbreak
|
||||
- Add placeholder and TODOs for Redumper
|
||||
- Add TODO with notes to Redumper
|
||||
- Fix 2-layer DVD support in Redumper
|
||||
- Fix VSCode build
|
||||
- Fix Redumper DAT/layer parsing
|
||||
- Update Redumper PS1 output parsing
|
||||
- Fix previous commit, clean up helpers
|
||||
- Update redumper to build 173
|
||||
- Initial support for Redumper CSS outputs
|
||||
- Hook up CSS output for testing
|
||||
- Update redumper to build 174
|
||||
- Add support for redumper CD synonyms
|
||||
- Adjust CSS title key parsing
|
||||
- Fix non-reading loop
|
||||
- Normalize Redumper CSS outputs
|
||||
- Normalize multi-instance site tags
|
||||
- Add missing Aaru error log to zip
|
||||
- Check Redumper dat section for completeness
|
||||
- Update redumper to build 176
|
||||
- UMDs are Sony discs
|
||||
- Strip colons from Redumper disc key
|
||||
- Use `HashSet` for disc or book type
|
||||
- Add ordering to disc or book type
|
||||
- Update redumper to build 183
|
||||
- Parse and format Redumper CD multisession data
|
||||
- New layerbreak takes precedence
|
||||
- Reduce pulled information for Xbox and X360
|
||||
- Ensure we found the tags we're skipping
|
||||
- Omit pulling universal hash
|
||||
- Update Nuget packages to newest stable
|
||||
|
||||
### 2.5 (2023-03-12)
|
||||
|
||||
- Add _drive file to zip for UIC
|
||||
- Add Xbox Series X and PS5 to list, fix Acorn
|
||||
- Add Xbox Series X short name to list
|
||||
- Remove windows from test target
|
||||
- Remove .NET 6.0 from tests, add TODO
|
||||
- Add .NET 6.0 to tests, remove msbuild args
|
||||
- Add HD-DVD to speed definitions
|
||||
- Add PS3 internal serial and version parsing (tjanas)
|
||||
- Update Nuget packages to newest stable
|
||||
- Simplify path selection in UI
|
||||
- Fix broken normalization test
|
||||
- Update drive info before dumping
|
||||
- Update redumper strings
|
||||
- Add new parameter and mode validation
|
||||
- Add redumper to the UI
|
||||
- Update redumper to build 81
|
||||
- Fix incorrect naming in Options window
|
||||
- Initial attempt at parsing redumper outputs
|
||||
- Update README
|
||||
- Reenable write offset for all CDs
|
||||
- Add missing redumper output file
|
||||
- Force a filename for redumper
|
||||
- Fix incorrect SetParameters
|
||||
- Fix a couple redumper things
|
||||
- Update AppVeyor version
|
||||
- Output security sectors to info
|
||||
- Remove x86 requirement for build
|
||||
- Fix XGD media type outputs
|
||||
- Fix typo in ToInternalProgram
|
||||
- Fix redumper error count parsing
|
||||
- Update to Aaru v5.3.2 LTS
|
||||
- Update options loader with sane defaults
|
||||
- Fix AppVeyor pathing
|
||||
- Skip during detection
|
||||
- Address some UI concerns
|
||||
- Tweak AppVeyor, show Check 6.0 builds
|
||||
- More strict when custom parameters editing
|
||||
- Use msbuild for .NET Framework 4.8
|
||||
- Update nuget packages
|
||||
- ReadAllText not ReadAllLines
|
||||
- Add drive format (fs) to log
|
||||
- Go back to pre .NET 7 Aaru
|
||||
- Be smarter about old paths
|
||||
- Fix relative paths for DIC
|
||||
- Add nicer failure message
|
||||
- Update to DIC 20230201
|
||||
- Use relative path output for DIC
|
||||
- Remove usage of Aaru
|
||||
- Remove Aaru as submodule
|
||||
- Fix Aaru removal
|
||||
- Enable .NET 6 Windows builds
|
||||
- Update Redumper to build_106
|
||||
- Reformat CICM for old .NET versions
|
||||
- Attempt to handle no drives
|
||||
- Handle no drives better
|
||||
- Handle no drives betterer
|
||||
- Packaging requires `-windows` for framework
|
||||
- Fix other `-windows` places for .NET 6
|
||||
- Fix typo in DICMultiSectorReadValue
|
||||
- Semi-unify drive finding
|
||||
- Introduce cross-platform MMI
|
||||
- Remove System.Management
|
||||
- Add Redumper Universal Hash support
|
||||
- Fix Redumper write offset support
|
||||
- Add Redumper non-zero data start
|
||||
- Use media size for type detection on .NET 6
|
||||
- Trim PIC for PS3
|
||||
- Get the version of redumper (fuzz6001)
|
||||
- Update VSCode config files
|
||||
- Can't publish single file for UI
|
||||
- Handle missing extension gracefully
|
||||
- Fix incorrect option slider display
|
||||
- Handle undetected discs on refresh
|
||||
- Originally intended behaviour of the Update Label button (IcySon55)
|
||||
- Update to BurnOutSharp 2.7.0
|
||||
- Get error count from recent redumper log (fuzz6001)
|
||||
- Minor cosmetic changes
|
||||
- Set saner defaults for dumping speeds
|
||||
- Attempt to support Windows 7
|
||||
- Add `win7-x64` to identifier list
|
||||
- Remove unsupported identifiers
|
||||
- Add identifiers in more places
|
||||
- Move drive finding inside of the try/catch
|
||||
- Update to DIC 20230309
|
||||
- Fix errant forward slashes
|
||||
- Add TOC back as optional file
|
||||
- Fix Redumper path generation
|
||||
- Use and trim quotes for Redumper
|
||||
- Fix incorrect image name setting
|
||||
- Ensure Redumper parameters are set
|
||||
- Ensure min values are taken care of
|
||||
- Update parameters with `=` handling
|
||||
- Ensure drive and speed are set
|
||||
- Handle quotes embedded
|
||||
- Return list of missing files for Redumper
|
||||
- Readd accidentally deleted line
|
||||
- Detect EOF during the search for error counts (fuzz6001)
|
||||
|
||||
### 2.4 (2022-10-26)
|
||||
|
||||
- Update to DIC 20211001
|
||||
- Fix Redump disc title pulling
|
||||
- Add /mr default flag options
|
||||
@@ -178,6 +618,7 @@
|
||||
- Fix offset formatting (fuzz6001)
|
||||
|
||||
### 2.3 (2022-02-05)
|
||||
|
||||
- Start overhauling Redump information pulling, again
|
||||
- Add internal structure for special site codes
|
||||
- Add new tabs for special site information
|
||||
@@ -262,6 +703,7 @@
|
||||
- Adjust paths for DIC just before dumping
|
||||
|
||||
### 2.2 (2021-12-30)
|
||||
|
||||
- Fix Saturn header finding
|
||||
- Add Pocket PC support
|
||||
- Add HD-DVD-Video support
|
||||
@@ -313,6 +755,7 @@
|
||||
- Add safety around volume labels
|
||||
|
||||
### 2.1 (2021-07-22)
|
||||
|
||||
- Enum, no more
|
||||
- Sony works backward
|
||||
- Add experimental dark mode
|
||||
@@ -342,6 +785,7 @@
|
||||
- Update to BurnOutSharp 1.7.0
|
||||
|
||||
### 2.0 (2021-04-23)
|
||||
|
||||
- Rename DICUI to Media Preservation Frontend (MPF)
|
||||
- Add handling for BEh drive _mainInfo.txt changes
|
||||
- Fix multiline regex fields during info pulling
|
||||
@@ -396,6 +840,7 @@
|
||||
- Update to BurnOutSharp 1.6.1
|
||||
|
||||
### 1.18 (2020-11-10)
|
||||
|
||||
- Add more information extraction and generation for Aaru
|
||||
- Remove instances of CD Check from copy protection (again, sorry)
|
||||
- Fix multiline submission info outputs
|
||||
@@ -420,6 +865,7 @@
|
||||
- Added HD-DVD-Video detection
|
||||
|
||||
### 1.17.1 (2020-09-14)
|
||||
|
||||
- Shuffled some shared, internal UI variables
|
||||
- Synced WPF and Avalonia UI internals
|
||||
- Made the disc information window less prone to bugs
|
||||
@@ -427,6 +873,7 @@
|
||||
- Added support for old(?) DIC flags: `/fix` and `/re`
|
||||
|
||||
### 1.17 (2020-09-12)
|
||||
|
||||
- Updated to Aaru version 5.1
|
||||
- Updated to BurnOutSharp version 1.4.0
|
||||
- Updated to DIC version 20200716
|
||||
@@ -586,7 +1033,7 @@
|
||||
- edccchk now run on all CD-Roms
|
||||
- Discs unsupported by Windows are now regonized
|
||||
- Extra \ when accepting default save has been removed.
|
||||
|
||||
|
||||
### 1.02b (2018-05-18)
|
||||
|
||||
- Added missing DLL
|
||||
@@ -599,4 +1046,4 @@
|
||||
|
||||
### 1.01d (2018-05-18)
|
||||
|
||||
-Combine IBM PC-CD options, misc fixes.
|
||||
-Combine IBM PC-CD options, misc fixes.
|
||||
|
||||
@@ -1,26 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>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-2022</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.4</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>2.7.3</VersionPrefix>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -28,22 +20,18 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.3.4" GeneratePathProperty="true">
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.9.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,12 +1,11 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp;
|
||||
using MPF.Core.Converters;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.Library;
|
||||
using RedumpLib.Data;
|
||||
using RedumpLib.Web;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace MPF.Check
|
||||
{
|
||||
@@ -15,28 +14,41 @@ namespace MPF.Check
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Try processing the standalone arguments
|
||||
if (ProcessStandaloneArguments(args))
|
||||
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
|
||||
if (standaloneProcessed != false)
|
||||
{
|
||||
if (standaloneProcessed == null)
|
||||
DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try processing the common arguments
|
||||
(bool success, MediaType mediaType, RedumpSystem? knownSystem) = ProcessCommonArguments(args);
|
||||
(bool success, MediaType mediaType, RedumpSystem? knownSystem, var error) = OptionsLoader.ProcessCommonArguments(args);
|
||||
if (!success)
|
||||
{
|
||||
DisplayHelp(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through and process options
|
||||
(Options options, string path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
|
||||
(var options, var seedInfo, var path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
|
||||
if (options.InternalProgram == InternalProgram.NONE)
|
||||
{
|
||||
DisplayHelp("A program name needs to be provided");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make new Progress objects
|
||||
var resultProgress = new Progress<Result>();
|
||||
resultProgress.ProgressChanged += ProgressUpdated;
|
||||
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
var protectionProgress = new Progress<ProtectionProgress>();
|
||||
protectionProgress.ProgressChanged += ProgressUpdated;
|
||||
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
|
||||
// Validate the supplied credentials
|
||||
#if NET48 || NETSTANDARD2_1
|
||||
#if NET48
|
||||
(bool? _, string message) = RedumpWebClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword);
|
||||
#else
|
||||
(bool? _, string message) = RedumpHttpClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
(bool? _, string? message) = RedumpHttpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
Console.WriteLine(message);
|
||||
@@ -55,11 +67,15 @@ namespace MPF.Check
|
||||
string filepath = Path.GetFullPath(args[i].Trim('"'));
|
||||
|
||||
// Now populate an environment
|
||||
#if NET48
|
||||
Drive drive = null;
|
||||
#else
|
||||
Drive? drive = null;
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
drive = Drive.Create(null, path);
|
||||
|
||||
var env = new DumpEnvironment(options, "", filepath, drive, knownSystem, mediaType, null);
|
||||
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, internalProgram: null, parameters: null);
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
@@ -71,7 +87,11 @@ namespace MPF.Check
|
||||
/// Display help for MPF.Check
|
||||
/// </summary>
|
||||
/// <param name="error">Error string to prefix the help text with</param>
|
||||
#if NET48
|
||||
private static void DisplayHelp(string error = null)
|
||||
#else
|
||||
private static void DisplayHelp(string? error = null)
|
||||
#endif
|
||||
{
|
||||
if (error != null)
|
||||
Console.WriteLine(error);
|
||||
@@ -94,101 +114,5 @@ namespace MPF.Check
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process common arguments for all functionality
|
||||
/// </summary>
|
||||
/// <returns>True if all arguments pass, false otherwise</returns>
|
||||
private static (bool, MediaType, RedumpSystem?) ProcessCommonArguments(string[] args)
|
||||
{
|
||||
// All other use requires at least 3 arguments
|
||||
if (args.Length < 3)
|
||||
{
|
||||
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>
|
||||
/// Process any standalone arguments for the program
|
||||
/// </summary>
|
||||
/// <returns>True if one of the arguments was processed, false otherwise</returns>
|
||||
private static bool ProcessStandaloneArguments(string[] args)
|
||||
{
|
||||
// Help options
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
|
||||
{
|
||||
DisplayHelp();
|
||||
return true;
|
||||
}
|
||||
|
||||
// List options
|
||||
if (args[0] == "-lm" || args[0] == "--listmedia")
|
||||
{
|
||||
Console.WriteLine("Supported Media Types:");
|
||||
foreach (string mediaType in Extensions.ListMediaTypes())
|
||||
{
|
||||
Console.WriteLine(mediaType);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-lp" || args[0] == "--listprograms")
|
||||
{
|
||||
Console.WriteLine("Supported Programs:");
|
||||
foreach (string program in EnumExtensions.ListPrograms())
|
||||
{
|
||||
Console.WriteLine(program);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-ls" || args[0] == "--listsystems")
|
||||
{
|
||||
Console.WriteLine("Supported Systems:");
|
||||
foreach (string system in Extensions.ListSystems())
|
||||
{
|
||||
Console.WriteLine(system);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple process counter to write to console
|
||||
/// </summary>
|
||||
private static void ProgressUpdated(object sender, Result value)
|
||||
{
|
||||
Console.WriteLine(value.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple process counter to write to console
|
||||
/// </summary>
|
||||
private static void ProgressUpdated(object sender, ProtectionProgress value)
|
||||
{
|
||||
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Anything marked as internal can be used by the test methods
|
||||
[assembly: InternalsVisibleTo("MPF.Test")]
|
||||
33
MPF.Core/ConsoleLogger.cs
Normal file
33
MPF.Core/ConsoleLogger.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core.Data;
|
||||
|
||||
namespace MPF.Core
|
||||
{
|
||||
public static class ConsoleLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple process counter to write to console
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public static void ProgressUpdated(object sender, Result value)
|
||||
#else
|
||||
public static void ProgressUpdated(object? sender, Result value)
|
||||
#endif
|
||||
{
|
||||
Console.WriteLine(value.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple process counter to write to console
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public static void ProgressUpdated(object sender, ProtectionProgress value)
|
||||
#else
|
||||
public static void ProgressUpdated(object? sender, ProtectionProgress value)
|
||||
#endif
|
||||
{
|
||||
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,11 +2,8 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#endif
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Converters
|
||||
{
|
||||
@@ -34,97 +31,6 @@ namespace MPF.Core.Converters
|
||||
}
|
||||
}
|
||||
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Convert IMAPI physical media type to a MediaType
|
||||
/// </summary>
|
||||
/// <param name="type">IMAPI_MEDIA_PHYSICAL_TYPE value to check</param>
|
||||
/// <returns>MediaType if possible, null on error</returns>
|
||||
public static MediaType? IMAPIToMediaType(this IMAPI_MEDIA_PHYSICAL_TYPE type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_UNKNOWN:
|
||||
return MediaType.NONE;
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDROM:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDR:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDRW:
|
||||
return MediaType.CDROM;
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDROM:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDRAM:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR_DUALLAYER:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHRW:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR_DUALLAYER:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DISK:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW_DUALLAYER:
|
||||
return MediaType.DVD;
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDROM:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDR:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDRAM:
|
||||
return MediaType.HDDVD;
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDROM:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDR:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDRE:
|
||||
return MediaType.BluRay;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/// <summary>
|
||||
/// Convert Aaru media type to a MediaType
|
||||
/// </summary>
|
||||
/// <param name="type">Aaru.CommonTypes.MediaType value to check</param>
|
||||
/// <returns>MediaType if possible, null on error</returns>
|
||||
public static MediaType? MediaTypeToMediaType(this Aaru.CommonTypes.MediaType? type)
|
||||
{
|
||||
if (type == null)
|
||||
return null;
|
||||
|
||||
return (int)type switch
|
||||
{
|
||||
// Generics
|
||||
>= 10 and <= 39 => MediaType.CDROM,
|
||||
>= 40 and <= 50 => MediaType.DVD,
|
||||
>= 51 and <= 59 => MediaType.HDDVD,
|
||||
>= 60 and <= 69 => MediaType.BluRay,
|
||||
|
||||
// Specialty
|
||||
112 => MediaType.CDROM,
|
||||
113 => MediaType.CDROM,
|
||||
114 => MediaType.DVD,
|
||||
115 => MediaType.DVD,
|
||||
116 => MediaType.BluRay,
|
||||
117 => MediaType.BluRay,
|
||||
118 => MediaType.UMD,
|
||||
120 => MediaType.BluRay,
|
||||
150 => MediaType.CDROM,
|
||||
151 => MediaType.CDROM,
|
||||
152 => MediaType.GDROM,
|
||||
153 => MediaType.GDROM,
|
||||
155 => MediaType.CDROM, // MilCD... is this GD-ROM?
|
||||
171 => MediaType.CDROM,
|
||||
172 => MediaType.CDROM,
|
||||
173 => MediaType.CDROM,
|
||||
174 => MediaType.CDROM,
|
||||
175 => MediaType.CDROM,
|
||||
176 => MediaType.CDROM,
|
||||
177 => MediaType.CDROM,
|
||||
178 => MediaType.DVD,
|
||||
179 => MediaType.CDROM,
|
||||
453 => MediaType.NintendoGameCubeGameDisc,
|
||||
463 => MediaType.NintendoWiiOpticalDisc,
|
||||
464 => MediaType.NintendoWiiUOpticalDisc,
|
||||
>= 770 and <= 799 => MediaType.BluRay,
|
||||
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Convert to Long Name
|
||||
@@ -132,7 +38,11 @@ namespace MPF.Core.Converters
|
||||
/// <summary>
|
||||
/// Long name method cache
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static readonly ConcurrentDictionary<Type, MethodInfo> LongNameMethods = new ConcurrentDictionary<Type, MethodInfo>();
|
||||
#else
|
||||
private static readonly ConcurrentDictionary<Type, MethodInfo?> LongNameMethods = new ConcurrentDictionary<Type, MethodInfo?>();
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get the string representation of a generic enumerable value
|
||||
@@ -143,13 +53,12 @@ namespace MPF.Core.Converters
|
||||
{
|
||||
try
|
||||
{
|
||||
var sourceType = value?.GetType();
|
||||
var sourceType = value.GetType();
|
||||
sourceType = Nullable.GetUnderlyingType(sourceType) ?? sourceType;
|
||||
|
||||
if (!LongNameMethods.TryGetValue(sourceType, out MethodInfo method))
|
||||
if (!LongNameMethods.TryGetValue(sourceType, out var method))
|
||||
{
|
||||
|
||||
method = typeof(RedumpLib.Data.Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
|
||||
method = typeof(Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
|
||||
if (method == null)
|
||||
method = typeof(EnumConverter).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
|
||||
|
||||
@@ -157,7 +66,7 @@ namespace MPF.Core.Converters
|
||||
}
|
||||
|
||||
if (method != null)
|
||||
return method.Invoke(null, new[] { value }) as string;
|
||||
return method.Invoke(null, new[] { value }) as string ?? string.Empty;
|
||||
else
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -181,12 +90,10 @@ namespace MPF.Core.Converters
|
||||
|
||||
case InternalProgram.Aaru:
|
||||
return "Aaru";
|
||||
case InternalProgram.DD:
|
||||
return "dd";
|
||||
case InternalProgram.DiscImageCreator:
|
||||
return "DiscImageCreator";
|
||||
case InternalProgram.Redumper:
|
||||
return "Redumper";
|
||||
return "redumper";
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -209,7 +116,7 @@ namespace MPF.Core.Converters
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Convert From String
|
||||
|
||||
@@ -218,9 +125,13 @@ namespace MPF.Core.Converters
|
||||
/// </summary>
|
||||
/// <param name="internalProgram">String value to convert</param>
|
||||
/// <returns>InternalProgram represented by the string, if possible</returns>
|
||||
#if NET48
|
||||
public static InternalProgram ToInternalProgram(string internalProgram)
|
||||
#else
|
||||
public static InternalProgram ToInternalProgram(string? internalProgram)
|
||||
#endif
|
||||
{
|
||||
switch (internalProgram.ToLowerInvariant())
|
||||
switch (internalProgram?.ToLowerInvariant())
|
||||
{
|
||||
// Dumping support
|
||||
case "aaru":
|
||||
@@ -233,9 +144,7 @@ namespace MPF.Core.Converters
|
||||
case "dicreator":
|
||||
case "discimagecreator":
|
||||
return InternalProgram.DiscImageCreator;
|
||||
case "dd":
|
||||
return InternalProgram.DD;
|
||||
case "rd:":
|
||||
case "rd":
|
||||
case "redumper":
|
||||
return InternalProgram.Redumper;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
@@ -16,11 +16,12 @@ namespace MPF.Core.Data
|
||||
// Byte arrays for signatures
|
||||
public static readonly byte[] SaturnSectorZeroStart = new byte[] { 0x53, 0x45, 0x47, 0x41, 0x20, 0x53, 0x45, 0x47, 0x41, 0x53, 0x41, 0x54, 0x55, 0x52, 0x4E, 0x20 };
|
||||
|
||||
// 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> BD { get; } = CD.Where(s => s <= 16).ToList();
|
||||
private static IReadOnlyList<int> Unknown { get; } = new List<int> { 1 };
|
||||
// Lists of known drive speed ranges
|
||||
public static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
public static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
public static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
public static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
|
||||
public static IReadOnlyList<int> Unknown { get; } = new List<int> { 1 };
|
||||
|
||||
/// <summary>
|
||||
/// Get list of all drive speeds for a given MediaType
|
||||
@@ -35,10 +36,11 @@ namespace MPF.Core.Data
|
||||
case MediaType.GDROM:
|
||||
return CD;
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return DVD;
|
||||
case MediaType.HDDVD:
|
||||
return HDDVD;
|
||||
case MediaType.BluRay:
|
||||
return BD;
|
||||
default:
|
||||
@@ -83,6 +85,7 @@ namespace MPF.Core.Data
|
||||
// Automatic Information
|
||||
|
||||
public const string DumpingProgramField = "Dumping Program";
|
||||
public const string DumpingDateField = "Date";
|
||||
public const string DumpingDriveManufacturer = "Manufacturer";
|
||||
public const string DumpingDriveModel = "Model";
|
||||
public const string DumpingDriveFirmware = "Firmware";
|
||||
|
||||
@@ -2,18 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Management.Infrastructure;
|
||||
using Microsoft.Management.Infrastructure.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Utilities;
|
||||
using RedumpLib.Data;
|
||||
#if NETFRAMEWORK
|
||||
using System.Management;
|
||||
using IMAPI2;
|
||||
#else
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using Aaru.Core.Media.Info;
|
||||
using Aaru.Decoders.SCSI.MMC;
|
||||
using Aaru.Devices;
|
||||
#endif
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
@@ -36,12 +29,20 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Drive partition format
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string DriveFormat { get; private set; } = null;
|
||||
#else
|
||||
public string? DriveFormat { get; private set; } = null;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Windows drive path
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string Name { get; private set; } = null;
|
||||
#else
|
||||
public string? Name { get; private set; } = null;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Represents if Windows has marked the drive as active
|
||||
@@ -57,7 +58,11 @@ namespace MPF.Core.Data
|
||||
/// 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>
|
||||
#if NET48
|
||||
public string VolumeLabel { get; private set; } = null;
|
||||
#else
|
||||
public string? VolumeLabel { get; private set; } = null;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -66,7 +71,11 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Media label as read by Windows, formatted to avoid odd outputs
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string FormattedVolumeLabel
|
||||
#else
|
||||
public string? FormattedVolumeLabel
|
||||
#endif
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -87,9 +96,10 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Windows drive letter
|
||||
/// Read-only access to the drive letter
|
||||
/// </summary>
|
||||
public char Letter => this.Name == null || this.Name.Length == 0 ? '\0' : this.Name[0];
|
||||
/// <remarks>Should only be used in UI applications</remarks>
|
||||
public char? Letter => this.Name?[0] ?? '\0';
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -103,7 +113,11 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
/// <param name="driveType">InternalDriveType value representing the drive type</param>
|
||||
/// <param name="devicePath">Path to the device according to the local machine</param>
|
||||
#if NET48
|
||||
public static Drive Create(InternalDriveType? driveType, string devicePath)
|
||||
#else
|
||||
public static Drive? Create(InternalDriveType? driveType, string devicePath)
|
||||
#endif
|
||||
{
|
||||
// Create a new, empty drive object
|
||||
var drive = new Drive()
|
||||
@@ -117,7 +131,11 @@ namespace MPF.Core.Data
|
||||
|
||||
// Sanitize a Windows-formatted long device path
|
||||
if (devicePath.StartsWith("\\\\.\\"))
|
||||
#if NET48
|
||||
devicePath = devicePath.Substring("\\\\.\\".Length);
|
||||
#else
|
||||
devicePath = devicePath["\\\\.\\".Length..];
|
||||
#endif
|
||||
|
||||
// Create and validate the drive info object
|
||||
var driveInfo = new DriveInfo(devicePath);
|
||||
@@ -134,7 +152,11 @@ namespace MPF.Core.Data
|
||||
/// Populate all fields from a DriveInfo object
|
||||
/// </summary>
|
||||
/// <param name="driveInfo">DriveInfo object to populate from</param>
|
||||
#if NET48
|
||||
private void PopulateFromDriveInfo(DriveInfo driveInfo)
|
||||
#else
|
||||
private void PopulateFromDriveInfo(DriveInfo? driveInfo)
|
||||
#endif
|
||||
{
|
||||
// If we have an invalid DriveInfo, just return
|
||||
if (driveInfo == null || driveInfo == default)
|
||||
@@ -149,6 +171,12 @@ namespace MPF.Core.Data
|
||||
this.TotalSize = driveInfo.TotalSize;
|
||||
this.VolumeLabel = driveInfo.VolumeLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.DriveFormat = string.Empty;
|
||||
this.TotalSize = default;
|
||||
this.VolumeLabel = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
#region Public Functionality
|
||||
@@ -161,16 +189,91 @@ namespace MPF.Core.Data
|
||||
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
|
||||
{
|
||||
var drives = GetDriveList(ignoreFixedDrives);
|
||||
drives = drives?.OrderBy(i => i.Letter)?.ToList();
|
||||
drives = drives.OrderBy(i => i == null ? "\0" : i.Name).ToList();
|
||||
return drives;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current media type from drive letter
|
||||
/// </summary>
|
||||
/// <param name="system"></param>
|
||||
/// <returns></returns>
|
||||
public (MediaType?, string) GetMediaType()
|
||||
=> GetMediaType(this.Name, this.InternalDriveType);
|
||||
#if NET48
|
||||
public (MediaType?, string) GetMediaType(RedumpSystem? system)
|
||||
#else
|
||||
public (MediaType?, string?) GetMediaType(RedumpSystem? system)
|
||||
#endif
|
||||
{
|
||||
// Take care of the non-optical stuff first
|
||||
switch (this.InternalDriveType)
|
||||
{
|
||||
case Data.InternalDriveType.Floppy:
|
||||
return (MediaType.FloppyDisk, null);
|
||||
case Data.InternalDriveType.HardDisk:
|
||||
return (MediaType.HardDisk, null);
|
||||
case Data.InternalDriveType.Removable:
|
||||
return (MediaType.FlashDrive, null);
|
||||
}
|
||||
|
||||
// Some systems should default to certain media types
|
||||
switch (system)
|
||||
{
|
||||
// CD
|
||||
case RedumpSystem.Panasonic3DOInteractiveMultiplayer:
|
||||
case RedumpSystem.PhilipsCDi:
|
||||
case RedumpSystem.SegaDreamcast:
|
||||
case RedumpSystem.SegaSaturn:
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
case RedumpSystem.VideoCD:
|
||||
return (MediaType.CDROM, null);
|
||||
|
||||
// DVD
|
||||
case RedumpSystem.DVDAudio:
|
||||
case RedumpSystem.DVDVideo:
|
||||
case RedumpSystem.MicrosoftXbox:
|
||||
case RedumpSystem.MicrosoftXbox360:
|
||||
return (MediaType.DVD, null);
|
||||
|
||||
// HD-DVD
|
||||
case RedumpSystem.HDDVDVideo:
|
||||
return (MediaType.HDDVD, null);
|
||||
|
||||
// Blu-ray
|
||||
case RedumpSystem.BDVideo:
|
||||
case RedumpSystem.MicrosoftXboxOne:
|
||||
case RedumpSystem.MicrosoftXboxSeriesXS:
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
return (MediaType.BluRay, null);
|
||||
|
||||
// GameCube
|
||||
case RedumpSystem.NintendoGameCube:
|
||||
return (MediaType.NintendoGameCubeGameDisc, null);
|
||||
|
||||
// Wii
|
||||
case RedumpSystem.NintendoWii:
|
||||
return (MediaType.NintendoWiiOpticalDisc, null);
|
||||
|
||||
// WiiU
|
||||
case RedumpSystem.NintendoWiiU:
|
||||
return (MediaType.NintendoWiiUOpticalDisc, null);
|
||||
|
||||
// PSP
|
||||
case RedumpSystem.SonyPlayStationPortable:
|
||||
return (MediaType.UMD, null);
|
||||
}
|
||||
|
||||
// Handle optical media by size and filesystem
|
||||
if (this.TotalSize >= 0 && this.TotalSize <= 800_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
|
||||
return (MediaType.CDROM, null);
|
||||
else if (this.TotalSize > 800_000_000 && this.TotalSize <= 8_540_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
|
||||
return (MediaType.DVD, null);
|
||||
else if (this.TotalSize > 8_540_000_000)
|
||||
return (MediaType.BluRay, null);
|
||||
|
||||
return (null, "Could not determine media type!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current system from drive
|
||||
@@ -179,10 +282,8 @@ namespace MPF.Core.Data
|
||||
/// <returns></returns>
|
||||
public RedumpSystem? GetRedumpSystem(RedumpSystem? defaultValue)
|
||||
{
|
||||
string drivePath = $"{this.Letter}:\\";
|
||||
|
||||
// If we can't read the media in that drive, we can't do anything
|
||||
if (!Directory.Exists(drivePath))
|
||||
if (!Directory.Exists(this.Name))
|
||||
return defaultValue;
|
||||
|
||||
// We're going to assume for floppies, HDDs, and removable drives
|
||||
@@ -201,7 +302,7 @@ namespace MPF.Core.Data
|
||||
// Bandai Playdia Quick Interactive System
|
||||
try
|
||||
{
|
||||
List<string> files = Directory.EnumerateFiles(drivePath, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
List<string> files = Directory.EnumerateFiles(this.Name, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
|
||||
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
|
||||
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
|
||||
@@ -212,16 +313,16 @@ namespace MPF.Core.Data
|
||||
catch { }
|
||||
|
||||
// Mattel Fisher-Price iXL
|
||||
if (File.Exists(Path.Combine(drivePath, "iXL", "iXLUpdater.exe")))
|
||||
if (File.Exists(Path.Combine(this.Name, "iXL", "iXLUpdater.exe")))
|
||||
{
|
||||
return RedumpSystem.MattelFisherPriceiXL;
|
||||
}
|
||||
|
||||
|
||||
// Microsoft Xbox 360
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "$SystemUpdate"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any()
|
||||
if (Directory.Exists(Path.Combine(this.Name, "$SystemUpdate"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
|
||||
&& this.TotalSize <= 500_000_000)
|
||||
{
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
@@ -232,8 +333,8 @@ namespace MPF.Core.Data
|
||||
// Microsoft Xbox One
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "MSXC"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "MSXC")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "MSXC"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "MSXC")).Any())
|
||||
{
|
||||
return RedumpSystem.MicrosoftXboxOne;
|
||||
}
|
||||
@@ -241,35 +342,23 @@ namespace MPF.Core.Data
|
||||
catch { }
|
||||
|
||||
// Sega Dreamcast
|
||||
if (File.Exists(Path.Combine(drivePath, "IP.BIN")))
|
||||
if (File.Exists(Path.Combine(this.Name, "IP.BIN")))
|
||||
{
|
||||
return RedumpSystem.SegaDreamcast;
|
||||
}
|
||||
|
||||
// Sega Mega-CD / Sega-CD
|
||||
if (File.Exists(Path.Combine(drivePath, "_BOOT", "IP.BIN"))
|
||||
|| File.Exists(Path.Combine(drivePath, "_BOOT", "SP.BIN"))
|
||||
|| File.Exists(Path.Combine(drivePath, "_BOOT", "SP_AS.BIN"))
|
||||
|| File.Exists(Path.Combine(drivePath, "FILESYSTEM.BIN")))
|
||||
if (File.Exists(Path.Combine(this.Name, "_BOOT", "IP.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP_AS.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
|
||||
{
|
||||
return RedumpSystem.SegaMegaCDSegaCD;
|
||||
}
|
||||
|
||||
// Sega Saturn
|
||||
try
|
||||
{
|
||||
byte[] sector = ReadSector(0);
|
||||
if (sector != null)
|
||||
{
|
||||
if (sector.StartsWith(Interface.SaturnSectorZeroStart))
|
||||
return RedumpSystem.SegaSaturn;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Sony PlayStation and Sony PlayStation 2
|
||||
string psxExePath = Path.Combine(drivePath, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
|
||||
string psxExePath = Path.Combine(this.Name, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(this.Name, "SYSTEM.CNF");
|
||||
if (File.Exists(systemCnfPath))
|
||||
{
|
||||
// Check for either BOOT or BOOT2
|
||||
@@ -287,9 +376,9 @@ namespace MPF.Core.Data
|
||||
// Sony PlayStation 3
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "PS3_GAME"))
|
||||
|| Directory.Exists(Path.Combine(drivePath, "PS3_UPDATE"))
|
||||
|| File.Exists(Path.Combine(drivePath, "PS3_DISC.SFB")))
|
||||
if (Directory.Exists(Path.Combine(this.Name, "PS3_GAME"))
|
||||
|| Directory.Exists(Path.Combine(this.Name, "PS3_UPDATE"))
|
||||
|| File.Exists(Path.Combine(this.Name, "PS3_DISC.SFB")))
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
}
|
||||
@@ -308,13 +397,13 @@ namespace MPF.Core.Data
|
||||
// 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")))
|
||||
if (File.Exists(Path.Combine(this.Name, "PS4", "UPDATE", "PS4UPDATE.PUP")))
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation4;
|
||||
}
|
||||
|
||||
// V.Tech V.Flash / V.Smile Pro
|
||||
if (File.Exists(Path.Combine(drivePath, "0SYSTEM")))
|
||||
if (File.Exists(Path.Combine(this.Name, "0SYSTEM")))
|
||||
{
|
||||
return RedumpSystem.VTechVFlashVSmilePro;
|
||||
}
|
||||
@@ -324,7 +413,7 @@ namespace MPF.Core.Data
|
||||
#region Computers
|
||||
|
||||
// Sharp X68000
|
||||
if (File.Exists(Path.Combine(drivePath, "COMMAND.X")))
|
||||
if (File.Exists(Path.Combine(this.Name, "COMMAND.X")))
|
||||
{
|
||||
return RedumpSystem.SharpX68000;
|
||||
}
|
||||
@@ -334,7 +423,7 @@ namespace MPF.Core.Data
|
||||
#region Video Formats
|
||||
|
||||
// BD-Video
|
||||
if (Directory.Exists(Path.Combine(drivePath, "BDMV")))
|
||||
if (Directory.Exists(Path.Combine(this.Name, "BDMV")))
|
||||
{
|
||||
// Technically BD-Audio has this as well, but it's hard to split that out right now
|
||||
return RedumpSystem.BDVideo;
|
||||
@@ -343,14 +432,14 @@ namespace MPF.Core.Data
|
||||
// DVD-Audio and DVD-Video
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "AUDIO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "AUDIO_TS")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "AUDIO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
|
||||
{
|
||||
return RedumpSystem.DVDAudio;
|
||||
}
|
||||
|
||||
else if (Directory.Exists(Path.Combine(drivePath, "VIDEO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VIDEO_TS")).Any())
|
||||
else if (Directory.Exists(Path.Combine(this.Name, "VIDEO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
|
||||
{
|
||||
return RedumpSystem.DVDVideo;
|
||||
}
|
||||
@@ -360,8 +449,8 @@ namespace MPF.Core.Data
|
||||
// HD-DVD-Video
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "HVDVD_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "HVDVD_TS")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "HVDVD_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
|
||||
{
|
||||
return RedumpSystem.HDDVDVideo;
|
||||
}
|
||||
@@ -371,15 +460,15 @@ namespace MPF.Core.Data
|
||||
// VCD
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "VCD"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VCD")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "VCD"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VCD")).Any())
|
||||
{
|
||||
return RedumpSystem.VideoCD;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
// Default return
|
||||
return defaultValue;
|
||||
@@ -436,48 +525,6 @@ namespace MPF.Core.Data
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a sector with a specified size from the drive
|
||||
/// </summary>
|
||||
/// <param name="num">Sector number, non-negative</param>
|
||||
/// <param name="size">Size of a sector in bytes</param>
|
||||
/// <returns>Byte array representing the sector, null on error</returns>
|
||||
public byte[] ReadSector(long num, int size = 2048)
|
||||
{
|
||||
// Missing drive leter is not supported
|
||||
if (string.IsNullOrEmpty(this.Name))
|
||||
return null;
|
||||
|
||||
// We don't support negative sectors
|
||||
if (num < 0)
|
||||
return null;
|
||||
|
||||
// Wrap the following in case of device access errors
|
||||
Stream fs = null;
|
||||
try
|
||||
{
|
||||
// Open the drive as a device
|
||||
fs = File.OpenRead($"\\\\?\\{this.Letter}:");
|
||||
|
||||
// Seek to the start of the sector, if possible
|
||||
long start = num * size;
|
||||
fs.Seek(start, SeekOrigin.Begin);
|
||||
|
||||
// Read and return the sector
|
||||
byte[] buffer = new byte[size];
|
||||
fs.Read(buffer, 0, size);
|
||||
return buffer;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
fs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the current drive information based on path
|
||||
/// </summary>
|
||||
@@ -491,8 +538,6 @@ namespace MPF.Core.Data
|
||||
|
||||
#region Helpers
|
||||
|
||||
#if NETFRAMEWORK
|
||||
|
||||
/// <summary>
|
||||
/// Get all current attached Drives
|
||||
/// </summary>
|
||||
@@ -511,27 +556,39 @@ namespace MPF.Core.Data
|
||||
desiredDriveTypes.Add(DriveType.Removable);
|
||||
}
|
||||
|
||||
// Get all supported drive types
|
||||
var drives = DriveInfo.GetDrives()
|
||||
.Where(d => desiredDriveTypes.Contains(d.DriveType))
|
||||
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name))
|
||||
.ToList();
|
||||
// TODO: Reduce reliance on `DriveInfo`
|
||||
// https://github.com/aaru-dps/Aaru/blob/5164a154e2145941472f2ee0aeb2eff3338ecbb3/Aaru.Devices/Windows/ListDevices.cs#L66
|
||||
|
||||
// Get the floppy drives and set the flag from removable
|
||||
// Create an output drive list
|
||||
var drives = new List<Drive>();
|
||||
|
||||
// Get all standard supported drive types
|
||||
try
|
||||
{
|
||||
ManagementObjectSearcher searcher =
|
||||
new ManagementObjectSearcher("root\\CIMV2",
|
||||
"SELECT * FROM Win32_LogicalDisk");
|
||||
drives = DriveInfo.GetDrives()
|
||||
.Where(d => desiredDriveTypes.Contains(d.DriveType))
|
||||
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name) ?? new Drive())
|
||||
.ToList();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return drives;
|
||||
}
|
||||
|
||||
var collection = searcher.Get();
|
||||
foreach (ManagementObject queryObj in collection)
|
||||
// Find and update all floppy drives
|
||||
try
|
||||
{
|
||||
CimSession session = CimSession.Create(null);
|
||||
var collection = session.QueryInstances("root\\CIMV2", "WQL", "SELECT * FROM Win32_LogicalDisk");
|
||||
|
||||
foreach (CimInstance instance in collection)
|
||||
{
|
||||
uint? mediaType = (uint?)queryObj["MediaType"];
|
||||
CimKeyedCollection<CimProperty> properties = instance.CimInstanceProperties;
|
||||
uint? mediaType = properties["MediaType"]?.Value as uint?;
|
||||
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
|
||||
{
|
||||
char devId = queryObj["DeviceID"].ToString()[0];
|
||||
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
|
||||
char devId = (properties["Caption"].Value as string ?? string.Empty)[0];
|
||||
drives.ForEach(d => { if (d?.Name != null && d.Name[0] == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -543,317 +600,6 @@ namespace MPF.Core.Data
|
||||
return drives;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the media type for a device path using the Aaru libraries
|
||||
/// </summary>
|
||||
/// <param name="devicePath">Path to the device</param>
|
||||
/// <param name="internalDriveType">Current internal drive type</param>
|
||||
/// <returns>MediaType, null on error</returns>
|
||||
private static (MediaType?, string) GetMediaType(string devicePath, InternalDriveType? internalDriveType)
|
||||
{
|
||||
char driveLetter = devicePath == null || !devicePath.Any() ? '\0' : devicePath[0];
|
||||
|
||||
// Take care of the non-optical stuff first
|
||||
// TODO: See if any of these can be more granular, like Optical is
|
||||
if (internalDriveType == Data.InternalDriveType.Floppy)
|
||||
return (MediaType.FloppyDisk, null);
|
||||
else if (internalDriveType == Data.InternalDriveType.HardDisk)
|
||||
return (MediaType.HardDisk, null);
|
||||
else if (internalDriveType == Data.InternalDriveType.Removable)
|
||||
return (MediaType.FlashDrive, null);
|
||||
|
||||
// Get the current drive information
|
||||
string deviceId = null;
|
||||
bool loaded = false;
|
||||
try
|
||||
{
|
||||
// Get the device ID first
|
||||
var searcher = new ManagementObjectSearcher(
|
||||
"root\\CIMV2",
|
||||
$"SELECT * FROM Win32_CDROMDrive WHERE Id = '{driveLetter}:\'");
|
||||
|
||||
foreach (ManagementObject queryObj in searcher.Get())
|
||||
{
|
||||
deviceId = (string)queryObj["DeviceID"];
|
||||
loaded = (bool)queryObj["MediaLoaded"];
|
||||
}
|
||||
|
||||
// If we got no valid device, we don't care and just return
|
||||
if (deviceId == null)
|
||||
return (null, "Device could not be found");
|
||||
else if (!loaded)
|
||||
return (null, "Device is not reporting media loaded");
|
||||
|
||||
MsftDiscMaster2 discMaster = new MsftDiscMaster2();
|
||||
deviceId = deviceId.ToLower().Replace('\\', '#').Replace('/', '#');
|
||||
string id = null;
|
||||
foreach (var disc in discMaster)
|
||||
{
|
||||
if (disc.ToString().Contains(deviceId))
|
||||
id = disc.ToString();
|
||||
}
|
||||
|
||||
// If we couldn't find the drive, we don't care and return
|
||||
if (id == null)
|
||||
return (null, "Device ID could not be found");
|
||||
|
||||
// Create the required objects for reading from the drive
|
||||
MsftDiscRecorder2 recorder = new MsftDiscRecorder2();
|
||||
recorder.InitializeDiscRecorder(id);
|
||||
MsftDiscFormat2Data dataWriter = new MsftDiscFormat2Data();
|
||||
|
||||
// If the recorder is not supported, just return
|
||||
if (!dataWriter.IsRecorderSupported(recorder))
|
||||
return (null, "IMAPI2 recorder not supported");
|
||||
|
||||
// Otherwise, set the recorder to get information from
|
||||
dataWriter.Recorder = recorder;
|
||||
|
||||
var media = dataWriter.CurrentPhysicalMediaType;
|
||||
return (media.IMAPIToMediaType(), null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (null, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/// <summary>
|
||||
/// Get all devices attached converted to Drive objects
|
||||
/// </summary>
|
||||
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
|
||||
/// <returns>List of drives, null on error</returns>
|
||||
private static List<Drive> GetDriveList(bool ignoreFixedDrives)
|
||||
{
|
||||
DeviceInfo[] deviceInfos = Device.ListDevices();
|
||||
if (deviceInfos == null)
|
||||
return null;
|
||||
|
||||
var drives = new List<Drive>();
|
||||
foreach (DeviceInfo deviceInfo in deviceInfos)
|
||||
{
|
||||
if (deviceInfo.Path == null)
|
||||
continue;
|
||||
|
||||
if (!deviceInfo.Supported)
|
||||
continue;
|
||||
|
||||
var drive = GetDriveFromDevice(deviceInfo.Path, ignoreFixedDrives);
|
||||
if (drive == null)
|
||||
continue;
|
||||
|
||||
drives.Add(drive);
|
||||
}
|
||||
|
||||
return drives;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a Drive object from a single device
|
||||
/// </summary>
|
||||
/// <param name="devicePath">Path to the device</param>
|
||||
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
|
||||
/// <returns>Drive object for the device, null on error</returns>
|
||||
private static Drive GetDriveFromDevice(string devicePath, bool ignoreFixedDrives)
|
||||
{
|
||||
if (devicePath.Length == 2 &&
|
||||
devicePath[1] == ':' &&
|
||||
devicePath[0] != '/' &&
|
||||
char.IsLetter(devicePath[0]))
|
||||
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
|
||||
|
||||
string windowsLocalDevicePath = devicePath;
|
||||
if (windowsLocalDevicePath.StartsWith("\\\\.\\"))
|
||||
windowsLocalDevicePath = windowsLocalDevicePath.Substring("\\\\.\\".Length);
|
||||
|
||||
var dev = Device.Create(devicePath, out _);
|
||||
if (dev == null || dev.Error)
|
||||
return null;
|
||||
|
||||
var devInfo = new Aaru.Core.Devices.Info.DeviceInfo(dev);
|
||||
if (devInfo.MmcConfiguration != null)
|
||||
{
|
||||
Features.SeparatedFeatures ftr = Features.Separate(devInfo.MmcConfiguration);
|
||||
if (ftr.Descriptors != null && ftr.Descriptors.Any(d => d.Code == 0x0000))
|
||||
{
|
||||
var desc = ftr.Descriptors.First(d => d.Code == 0x0000);
|
||||
bool isOptical = IsOptical(desc.Data);
|
||||
if (isOptical)
|
||||
return Create(Data.InternalDriveType.Optical, windowsLocalDevicePath);
|
||||
else if (!ignoreFixedDrives)
|
||||
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignoreFixedDrives)
|
||||
{
|
||||
switch (dev.Type)
|
||||
{
|
||||
case DeviceType.MMC:
|
||||
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
|
||||
|
||||
case DeviceType.SecureDigital:
|
||||
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
|
||||
}
|
||||
|
||||
if (dev.IsUsb)
|
||||
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
|
||||
|
||||
if (dev.IsFireWire)
|
||||
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
|
||||
|
||||
if (dev.IsPcmcia)
|
||||
return Create(Data.InternalDriveType.Removable, windowsLocalDevicePath);
|
||||
}
|
||||
|
||||
dev.Close();
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the media type for a device path using the Aaru libraries
|
||||
/// </summary>
|
||||
/// <param name="devicePath">Path to the device</param>
|
||||
/// <param name="internalDriveType">Current internal drive type</param>
|
||||
/// <returns>MediaType, null on error</returns>
|
||||
/// <remarks>
|
||||
/// TODO: Get the device type for devices with set media types
|
||||
/// </remarks>
|
||||
private static (MediaType?, string) GetMediaType(string devicePath, InternalDriveType? internalDriveType)
|
||||
{
|
||||
if (devicePath.Length == 2 &&
|
||||
devicePath[1] == ':' &&
|
||||
devicePath[0] != '/' &&
|
||||
char.IsLetter(devicePath[0]))
|
||||
devicePath = "\\\\.\\" + char.ToUpper(devicePath[0]) + ':';
|
||||
|
||||
var dev = Device.Create(devicePath, out _);
|
||||
if (dev == null || dev.Error)
|
||||
return (null, "Device could not be accessed");
|
||||
|
||||
switch (dev.Type)
|
||||
{
|
||||
case DeviceType.ATAPI:
|
||||
case DeviceType.SCSI:
|
||||
ScsiInfo scsiInfo = new ScsiInfo(dev);
|
||||
var mediaType = EnumConverter.MediaTypeToMediaType(scsiInfo?.MediaType);
|
||||
if (mediaType == null)
|
||||
return (mediaType, "Could not determine media type");
|
||||
else
|
||||
return (mediaType, null);
|
||||
}
|
||||
|
||||
return (null, "Device does not support media type finding");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a drive is optical or not
|
||||
/// </summary>
|
||||
/// <param name="featureBytes">Bytes representing the field to check</param>
|
||||
/// <returns>True if the drive is optical, false otherwise</returns>
|
||||
private static bool IsOptical(byte[] featureBytes)
|
||||
{
|
||||
var supportedMediaTypes = OpticalMediaSupport(featureBytes);
|
||||
if (supportedMediaTypes == null || !supportedMediaTypes.Any())
|
||||
return false;
|
||||
|
||||
if (supportedMediaTypes.Contains(MediaType.CDROM))
|
||||
return true;
|
||||
else if (supportedMediaTypes.Contains(MediaType.DVD))
|
||||
return true;
|
||||
else if (supportedMediaTypes.Contains(MediaType.BluRay))
|
||||
return true;
|
||||
else if (supportedMediaTypes.Contains(MediaType.HDDVD))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get supported media types for a drive
|
||||
/// </summary>
|
||||
/// <param name="featureBytes">Bytes representing the field to check</param>
|
||||
/// <returns>List of supported media types, null on error</returns>
|
||||
private static List<MediaType> OpticalMediaSupport(byte[] featureBytes)
|
||||
{
|
||||
Feature_0000? feature = Features.Decode_0000(featureBytes);
|
||||
|
||||
if (!feature.HasValue)
|
||||
return null;
|
||||
|
||||
var supportedMediaTypes = new List<MediaType>();
|
||||
foreach (Profile prof in feature.Value.Profiles)
|
||||
{
|
||||
switch (prof.Number)
|
||||
{
|
||||
// Values we don't care about for Optical
|
||||
case ProfileNumber.Reserved:
|
||||
case ProfileNumber.NonRemovable:
|
||||
case ProfileNumber.Removable:
|
||||
case ProfileNumber.MOErasable:
|
||||
case ProfileNumber.OpticalWORM:
|
||||
case ProfileNumber.ASMO:
|
||||
case ProfileNumber.Unconforming:
|
||||
break;
|
||||
|
||||
// Every supported optical profile
|
||||
case ProfileNumber.CDROM:
|
||||
case ProfileNumber.CDR:
|
||||
case ProfileNumber.CDRW:
|
||||
supportedMediaTypes.Add(MediaType.CDROM);
|
||||
break;
|
||||
|
||||
case ProfileNumber.DVDROM:
|
||||
case ProfileNumber.DVDRSeq:
|
||||
case ProfileNumber.DVDRAM:
|
||||
case ProfileNumber.DVDRWRes:
|
||||
case ProfileNumber.DVDRWSeq:
|
||||
case ProfileNumber.DVDRDLSeq:
|
||||
case ProfileNumber.DVDRDLJump:
|
||||
case ProfileNumber.DVDRWDL:
|
||||
case ProfileNumber.DVDDownload:
|
||||
case ProfileNumber.DVDRWPlus:
|
||||
case ProfileNumber.DVDRPlus:
|
||||
case ProfileNumber.DVDRWDLPlus:
|
||||
case ProfileNumber.DVDRDLPlus:
|
||||
supportedMediaTypes.Add(MediaType.DVD);
|
||||
break;
|
||||
|
||||
// TODO: Add DDCD as media type
|
||||
case ProfileNumber.DDCDROM:
|
||||
case ProfileNumber.DDCDR:
|
||||
case ProfileNumber.DDCDRW:
|
||||
supportedMediaTypes.Add(MediaType.DVD);
|
||||
break;
|
||||
|
||||
case ProfileNumber.BDROM:
|
||||
case ProfileNumber.BDRSeq:
|
||||
case ProfileNumber.BDRRdm:
|
||||
case ProfileNumber.BDRE:
|
||||
supportedMediaTypes.Add(MediaType.BluRay);
|
||||
break;
|
||||
|
||||
case ProfileNumber.HDDVDROM:
|
||||
case ProfileNumber.HDDVDR:
|
||||
case ProfileNumber.HDDVDRAM:
|
||||
case ProfileNumber.HDDVDRW:
|
||||
case ProfileNumber.HDDVDRDL:
|
||||
case ProfileNumber.HDDVDRWDL:
|
||||
case ProfileNumber.HDBURNROM:
|
||||
case ProfileNumber.HDBURNR:
|
||||
case ProfileNumber.HDBURNRW:
|
||||
supportedMediaTypes.Add(MediaType.HDDVD);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return supportedMediaTypes.Distinct().ToList();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
// Dumping support
|
||||
Aaru,
|
||||
DD,
|
||||
DiscImageCreator,
|
||||
Redumper,
|
||||
|
||||
@@ -29,4 +28,15 @@
|
||||
DCDumper,
|
||||
UmdImageCreator,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log level for output
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
USER,
|
||||
VERBOSE,
|
||||
ERROR,
|
||||
SECRET,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,25 +8,37 @@ namespace MPF.Core.Data
|
||||
{
|
||||
public class IniFile : IDictionary<string, string>
|
||||
{
|
||||
#if NET48
|
||||
private Dictionary<string, string> _keyValuePairs = new Dictionary<string, string>();
|
||||
#else
|
||||
private Dictionary<string, string> _keyValuePairs = new();
|
||||
#endif
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET48
|
||||
if (_keyValuePairs == null)
|
||||
_keyValuePairs = new Dictionary<string, string>();
|
||||
#else
|
||||
_keyValuePairs ??= new Dictionary<string, string>();
|
||||
#endif
|
||||
|
||||
key = key.ToLowerInvariant();
|
||||
if (_keyValuePairs.ContainsKey(key))
|
||||
return _keyValuePairs[key];
|
||||
|
||||
return null;
|
||||
return string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
#if NET48
|
||||
if (_keyValuePairs == null)
|
||||
_keyValuePairs = new Dictionary<string, string>();
|
||||
#else
|
||||
_keyValuePairs ??= new Dictionary<string, string>();
|
||||
#endif
|
||||
|
||||
key = key.ToLowerInvariant();
|
||||
_keyValuePairs[key] = value;
|
||||
@@ -99,15 +111,21 @@ namespace MPF.Core.Data
|
||||
// Keys are case-insensitive by default
|
||||
try
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(stream))
|
||||
using (var sr = new StreamReader(stream))
|
||||
{
|
||||
string section = string.Empty;
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string line = sr.ReadLine().Trim();
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
|
||||
// Empty lines are skipped
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
// No-op, we don't process empty lines
|
||||
}
|
||||
|
||||
// Comments start with ';'
|
||||
if (line.StartsWith(";"))
|
||||
else if (line.StartsWith(";"))
|
||||
{
|
||||
// No-op, we don't process comments
|
||||
}
|
||||
@@ -119,7 +137,11 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
// Valid INI lines are in the format key=value
|
||||
#if NET48
|
||||
else if (line.Contains("="))
|
||||
#else
|
||||
else if (line.Contains('='))
|
||||
#endif
|
||||
{
|
||||
// Split the line by '=' for key-value pairs
|
||||
string[] data = line.Split('=');
|
||||
@@ -179,7 +201,7 @@ namespace MPF.Core.Data
|
||||
|
||||
try
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter(stream))
|
||||
using (var sw = new StreamWriter(stream))
|
||||
{
|
||||
// Order the dictionary by keys to link sections together
|
||||
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
|
||||
@@ -245,7 +267,9 @@ namespace MPF.Core.Data
|
||||
|
||||
public bool TryGetValue(string key, out string value)
|
||||
{
|
||||
return ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out value);
|
||||
bool result = ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out var temp);
|
||||
value = temp ?? string.Empty;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<string, string> item)
|
||||
|
||||
@@ -1,51 +1,68 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
public class Options : IDictionary<string, string>, ICloneable
|
||||
public class Options
|
||||
{
|
||||
private readonly Dictionary<string, string> _settings;
|
||||
/// <summary>
|
||||
/// All settings in the form of a dictionary
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Dictionary<string, string> Settings { get; private set; }
|
||||
#else
|
||||
public Dictionary<string, string?> Settings { get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if the program is being run with a clean configuration
|
||||
/// </summary>
|
||||
public bool FirstRun
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "FirstRun", true); }
|
||||
set { Settings["FirstRun"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#region Internal Program
|
||||
|
||||
/// <summary>
|
||||
/// Path to Aaru
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string AaruPath
|
||||
#else
|
||||
public string? AaruPath
|
||||
#endif
|
||||
{
|
||||
get { return GetStringSetting(_settings, "AaruPath", "Programs\\Aaru\\Aaru.exe"); }
|
||||
set { _settings["AaruPath"] = value; }
|
||||
get { return GetStringSetting(Settings, "AaruPath", "Programs\\Aaru\\Aaru.exe"); }
|
||||
set { Settings["AaruPath"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Path to DiscImageCreator
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string DiscImageCreatorPath
|
||||
#else
|
||||
public string? DiscImageCreatorPath
|
||||
#endif
|
||||
{
|
||||
get { return GetStringSetting(_settings, "DiscImageCreatorPath", "Programs\\Creator\\DiscImageCreator.exe"); }
|
||||
set { _settings["DiscImageCreatorPath"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Path to dd for Windows
|
||||
/// </summary>
|
||||
public string DDPath
|
||||
{
|
||||
get { return GetStringSetting(_settings, "DDPath", "Programs\\DD\\dd.exe"); }
|
||||
set { _settings["DDPath"] = value; }
|
||||
get { return GetStringSetting(Settings, "DiscImageCreatorPath", "Programs\\Creator\\DiscImageCreator.exe"); }
|
||||
set { Settings["DiscImageCreatorPath"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Path to Redumper
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string RedumperPath
|
||||
#else
|
||||
public string? RedumperPath
|
||||
#endif
|
||||
{
|
||||
get { return GetStringSetting(_settings, "RedumperPath", "Programs\\RedumperPath\\redumper.exe"); }
|
||||
set { _settings["RedumperPath"] = value; }
|
||||
get { return GetStringSetting(Settings, "RedumperPath", "Programs\\Redumper\\redumper.exe"); }
|
||||
set { Settings["RedumperPath"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -55,13 +72,13 @@ namespace MPF.Core.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
string valueString = GetStringSetting(_settings, "InternalProgram", InternalProgram.DiscImageCreator.ToString());
|
||||
var valueString = GetStringSetting(Settings, "InternalProgram", InternalProgram.DiscImageCreator.ToString());
|
||||
var valueEnum = EnumConverter.ToInternalProgram(valueString);
|
||||
return valueEnum == InternalProgram.NONE ? InternalProgram.DiscImageCreator : valueEnum;
|
||||
}
|
||||
set
|
||||
{
|
||||
_settings["InternalProgram"] = value.ToString();
|
||||
Settings["InternalProgram"] = value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,8 +91,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool EnableDarkMode
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "EnableDarkMode", false); }
|
||||
set { _settings["EnableDarkMode"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "EnableDarkMode", false); }
|
||||
set { Settings["EnableDarkMode"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -83,17 +100,30 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool CheckForUpdatesOnStartup
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "CheckForUpdatesOnStartup", true); }
|
||||
set { _settings["CheckForUpdatesOnStartup"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "CheckForUpdatesOnStartup", true); }
|
||||
set { Settings["CheckForUpdatesOnStartup"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast update label - Skips disc checks and updates path only
|
||||
/// </summary>
|
||||
public bool FastUpdateLabel
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "FastUpdateLabel", false); }
|
||||
set { Settings["FastUpdateLabel"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default output path for dumps
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string DefaultOutputPath
|
||||
#else
|
||||
public string? DefaultOutputPath
|
||||
#endif
|
||||
{
|
||||
get { return GetStringSetting(_settings, "DefaultOutputPath", "ISO"); }
|
||||
set { _settings["DefaultOutputPath"] = value; }
|
||||
get { return GetStringSetting(Settings, "DefaultOutputPath", "ISO"); }
|
||||
set { Settings["DefaultOutputPath"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -103,13 +133,13 @@ namespace MPF.Core.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
string valueString = GetStringSetting(_settings, "DefaultSystem", null);
|
||||
var valueEnum = Extensions.ToRedumpSystem(valueString);
|
||||
var valueString = GetStringSetting(Settings, "DefaultSystem", null);
|
||||
var valueEnum = Extensions.ToRedumpSystem(valueString ?? string.Empty);
|
||||
return valueEnum;
|
||||
}
|
||||
set
|
||||
{
|
||||
_settings["DefaultSystem"] = value.LongName();
|
||||
Settings["DefaultSystem"] = value.LongName();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,8 +149,8 @@ namespace MPF.Core.Data
|
||||
/// <remarks>This is a hidden setting</remarks>
|
||||
public bool ShowDebugViewMenuItem
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "ShowDebugViewMenuItem", false); }
|
||||
set { _settings["ShowDebugViewMenuItem"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "ShowDebugViewMenuItem", false); }
|
||||
set { Settings["ShowDebugViewMenuItem"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -132,8 +162,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedCD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedCD", 72); }
|
||||
set { _settings["PreferredDumpSpeedCD"] = value.ToString(); }
|
||||
get { return GetInt32Setting(Settings, "PreferredDumpSpeedCD", 24); }
|
||||
set { Settings["PreferredDumpSpeedCD"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -141,8 +171,17 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedDVD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedDVD", 24); }
|
||||
set { _settings["PreferredDumpSpeedDVD"] = value.ToString(); }
|
||||
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>
|
||||
@@ -150,8 +189,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedBD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedBD", 16); }
|
||||
set { _settings["PreferredDumpSpeedBD"] = value.ToString(); }
|
||||
get { return GetInt32Setting(Settings, "PreferredDumpSpeedBD", 8); }
|
||||
set { Settings["PreferredDumpSpeedBD"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -163,8 +202,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool AaruEnableDebug
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "AaruEnableDebug", false); }
|
||||
set { _settings["AaruEnableDebug"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "AaruEnableDebug", false); }
|
||||
set { Settings["AaruEnableDebug"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -172,8 +211,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool AaruEnableVerbose
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "AaruEnableVerbose", false); }
|
||||
set { _settings["AaruEnableVerbose"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "AaruEnableVerbose", false); }
|
||||
set { Settings["AaruEnableVerbose"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -181,8 +220,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool AaruForceDumping
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "AaruForceDumping", true); }
|
||||
set { _settings["AaruForceDumping"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "AaruForceDumping", true); }
|
||||
set { Settings["AaruForceDumping"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -190,8 +229,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int AaruRereadCount
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "AaruRereadCount", 5); }
|
||||
set { _settings["AaruRereadCount"] = value.ToString(); }
|
||||
get { return GetInt32Setting(Settings, "AaruRereadCount", 5); }
|
||||
set { Settings["AaruRereadCount"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -199,8 +238,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool AaruStripPersonalData
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "AaruStripPersonalData", false); }
|
||||
set { _settings["AaruStripPersonalData"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "AaruStripPersonalData", false); }
|
||||
set { Settings["AaruStripPersonalData"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -212,8 +251,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool DICMultiSectorRead
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "DICMultiSectorRead", false); }
|
||||
set { _settings["DICMultiSectorRead"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "DICMultiSectorRead", false); }
|
||||
set { Settings["DICMultiSectorRead"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -221,8 +260,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int DICMultiSectorReadValue
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "DICMultiSectorReadValue", 0); }
|
||||
set { _settings["DICMultiSectorRead"] = value.ToString(); }
|
||||
get { return GetInt32Setting(Settings, "DICMultiSectorReadValue", 0); }
|
||||
set { Settings["DICMultiSectorReadValue"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -235,8 +274,8 @@ namespace MPF.Core.Data
|
||||
/// </remarks>
|
||||
public bool DICParanoidMode
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "DICParanoidMode", false); }
|
||||
set { _settings["DICParanoidMode"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "DICParanoidMode", false); }
|
||||
set { Settings["DICParanoidMode"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -244,8 +283,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool DICQuietMode
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "DICQuietMode", false); }
|
||||
set { _settings["DICQuietMode"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "DICQuietMode", false); }
|
||||
set { Settings["DICQuietMode"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -253,8 +292,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int DICRereadCount
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "DICRereadCount", 20); }
|
||||
set { _settings["DICRereadCount"] = value.ToString(); }
|
||||
get { return GetInt32Setting(Settings, "DICRereadCount", 20); }
|
||||
set { Settings["DICRereadCount"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -262,8 +301,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int DICDVDRereadCount
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "DICDVDRereadCount", 10); }
|
||||
set { _settings["DICDVDRereadCount"] = value.ToString(); }
|
||||
get { return GetInt32Setting(Settings, "DICDVDRereadCount", 10); }
|
||||
set { Settings["DICDVDRereadCount"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -271,8 +310,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool DICResetDriveAfterDump
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "DICResetDriveAfterDump", false); }
|
||||
set { _settings["DICResetDriveAfterDump"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "DICResetDriveAfterDump", false); }
|
||||
set { Settings["DICResetDriveAfterDump"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -280,8 +319,57 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool DICUseCMIFlag
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "DICUseCMIFlag", false); }
|
||||
set { _settings["DICUseCMIFlag"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "DICUseCMIFlag", false); }
|
||||
set { Settings["DICUseCMIFlag"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Redumper
|
||||
|
||||
/// <summary>
|
||||
/// Enable debug output while dumping by default
|
||||
/// </summary>
|
||||
public bool RedumperEnableDebug
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "RedumperEnableDebug", false); }
|
||||
set { Settings["RedumperEnableDebug"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable verbose output while dumping by default
|
||||
/// </summary>
|
||||
public bool RedumperEnableVerbose
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "RedumperEnableVerbose", false); }
|
||||
set { Settings["RedumperEnableVerbose"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable BE reading by default with Redumper
|
||||
/// </summary>
|
||||
public bool RedumperUseBEReading
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "RedumperUseBEReading", false); }
|
||||
set { Settings["RedumperUseBEReading"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable generic drive type by default with Redumper
|
||||
/// </summary>
|
||||
public bool RedumperUseGenericDriveType
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "RedumperUseGenericDriveType", false); }
|
||||
set { Settings["RedumperUseGenericDriveType"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default number of rereads
|
||||
/// </summary>
|
||||
public int RedumperRereadCount
|
||||
{
|
||||
get { return GetInt32Setting(Settings, "RedumperRereadCount", 20); }
|
||||
set { Settings["RedumperRereadCount"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -293,8 +381,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool ScanForProtection
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "ScanForProtection", true); }
|
||||
set { _settings["ScanForProtection"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "ScanForProtection", true); }
|
||||
set { Settings["ScanForProtection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -302,8 +390,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool OutputSeparateProtectionFile
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "OutputSeparateProtectionFile", true); }
|
||||
set { _settings["OutputSeparateProtectionFile"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "OutputSeparateProtectionFile", true); }
|
||||
set { Settings["OutputSeparateProtectionFile"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -311,8 +399,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool AddPlaceholders
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "AddPlaceholders", true); }
|
||||
set { _settings["AddPlaceholders"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "AddPlaceholders", true); }
|
||||
set { Settings["AddPlaceholders"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -320,8 +408,17 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool PromptForDiscInformation
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "PromptForDiscInformation", true); }
|
||||
set { _settings["PromptForDiscInformation"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "PromptForDiscInformation", true); }
|
||||
set { Settings["PromptForDiscInformation"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pull all information from Redump if signed in
|
||||
/// </summary>
|
||||
public bool PullAllInformation
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "PullAllInformation", false); }
|
||||
set { Settings["PullAllInformation"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -329,8 +426,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool EnableTabsInInputFields
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "EnableTabsInInputFields", false); }
|
||||
set { _settings["EnableTabsInInputFields"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "EnableTabsInInputFields", false); }
|
||||
set { Settings["EnableTabsInInputFields"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -338,8 +435,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool EnableRedumpCompatibility
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "EnableRedumpCompatibility", true); }
|
||||
set { _settings["EnableRedumpCompatibility"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "EnableRedumpCompatibility", true); }
|
||||
set { Settings["EnableRedumpCompatibility"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -347,8 +444,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool ShowDiscEjectReminder
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "ShowDiscEjectReminder", true); }
|
||||
set { _settings["ShowDiscEjectReminder"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "ShowDiscEjectReminder", true); }
|
||||
set { Settings["ShowDiscEjectReminder"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -356,8 +453,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool EjectAfterDump
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "EjectAfterDump", false); }
|
||||
set { _settings["EjectAfterDump"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "EjectAfterDump", false); }
|
||||
set { Settings["EjectAfterDump"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -365,8 +462,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool IgnoreFixedDrives
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "IgnoreFixedDrives", true); }
|
||||
set { _settings["IgnoreFixedDrives"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "IgnoreFixedDrives", true); }
|
||||
set { Settings["IgnoreFixedDrives"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -374,8 +471,17 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool ToolsInSeparateWindow
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "ToolsInSeparateWindow", true); }
|
||||
set { _settings["ToolsInSeparateWindow"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "ToolsInSeparateWindow", true); }
|
||||
set { Settings["ToolsInSeparateWindow"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the dump filename as a suffix to the auto-generated files
|
||||
/// </summary>
|
||||
public bool AddFilenameSuffix
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "AddFilenameSuffix", false); }
|
||||
set { Settings["AddFilenameSuffix"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -383,8 +489,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool OutputSubmissionJSON
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "OutputSubmissionJSON", false); }
|
||||
set { _settings["OutputSubmissionJSON"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "OutputSubmissionJSON", false); }
|
||||
set { Settings["OutputSubmissionJSON"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -392,8 +498,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool IncludeArtifacts
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "IncludeArtifacts", false); }
|
||||
set { _settings["IncludeArtifacts"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "IncludeArtifacts", false); }
|
||||
set { Settings["IncludeArtifacts"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -401,8 +507,17 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool CompressLogFiles
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "CompressLogFiles", true); }
|
||||
set { _settings["CompressLogFiles"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "CompressLogFiles", true); }
|
||||
set { Settings["CompressLogFiles"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete unnecessary files to reduce space
|
||||
/// </summary>
|
||||
public bool DeleteUnnecessaryFiles
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "DeleteUnnecessaryFiles", false); }
|
||||
set { Settings["DeleteUnnecessaryFiles"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -414,8 +529,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool SkipMediaTypeDetection
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "SkipMediaTypeDetection", false); }
|
||||
set { _settings["SkipMediaTypeDetection"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "SkipMediaTypeDetection", false); }
|
||||
set { Settings["SkipMediaTypeDetection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -423,8 +538,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool SkipSystemDetection
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "SkipSystemDetection", false); }
|
||||
set { _settings["SkipSystemDetection"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "SkipSystemDetection", false); }
|
||||
set { Settings["SkipSystemDetection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -436,8 +551,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool ScanArchivesForProtection
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "ScanArchivesForProtection", true); }
|
||||
set { _settings["ScanArchivesForProtection"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "ScanArchivesForProtection", true); }
|
||||
set { Settings["ScanArchivesForProtection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -445,8 +560,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool ScanPackersForProtection
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "ScanPackersForProtection", false); }
|
||||
set { _settings["ScanPackersForProtection"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "ScanPackersForProtection", false); }
|
||||
set { Settings["ScanPackersForProtection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -454,8 +569,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool IncludeDebugProtectionInformation
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "IncludeDebugProtectionInformation", false); }
|
||||
set { _settings["IncludeDebugProtectionInformation"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "IncludeDebugProtectionInformation", false); }
|
||||
set { Settings["IncludeDebugProtectionInformation"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -467,8 +582,8 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool VerboseLogging
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "VerboseLogging", true); }
|
||||
set { _settings["VerboseLogging"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "VerboseLogging", true); }
|
||||
set { Settings["VerboseLogging"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -476,49 +591,36 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool OpenLogWindowAtStartup
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "OpenLogWindowAtStartup", true); }
|
||||
set { _settings["OpenLogWindowAtStartup"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable fancy formatting of log statements
|
||||
/// Disables EnableProgressProcessing if disabled
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is mainly for outputting redirected console outputs. Not many
|
||||
/// other bits of the logs include any specially handled outputs.
|
||||
/// </remarks>
|
||||
public bool EnableLogFormatting
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "EnableLogFormatting", false); }
|
||||
set { _settings["EnableLogFormatting"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable progress bar updating based on log text
|
||||
/// Disabled if EnableLogFormatting is disabled
|
||||
/// </summary>
|
||||
public bool EnableProgressProcessing
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "EnableProgressProcessing", false); }
|
||||
set { _settings["EnableProgressProcessing"] = value.ToString(); }
|
||||
get { return GetBooleanSetting(Settings, "OpenLogWindowAtStartup", true); }
|
||||
set { Settings["OpenLogWindowAtStartup"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Redump Login Information
|
||||
|
||||
#if NET48
|
||||
public string RedumpUsername
|
||||
#else
|
||||
public string? RedumpUsername
|
||||
#endif
|
||||
{
|
||||
get { return GetStringSetting(_settings, "RedumpUsername", ""); }
|
||||
set { _settings["RedumpUsername"] = value; }
|
||||
get { return GetStringSetting(Settings, "RedumpUsername", ""); }
|
||||
set { Settings["RedumpUsername"] = value; }
|
||||
}
|
||||
|
||||
// TODO: Figure out a way to keep this encrypted in some way, BASE64 to start?
|
||||
#if NET48
|
||||
public string RedumpPassword
|
||||
#else
|
||||
public string? RedumpPassword
|
||||
#endif
|
||||
{
|
||||
get { return GetStringSetting(_settings, "RedumpPassword", ""); }
|
||||
set { _settings["RedumpPassword"] = value; }
|
||||
get
|
||||
{
|
||||
return GetStringSetting(Settings, "RedumpPassword", "");
|
||||
}
|
||||
set { Settings["RedumpPassword"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -532,17 +634,47 @@ namespace MPF.Core.Data
|
||||
/// Constructor taking a dictionary for settings
|
||||
/// </summary>
|
||||
/// <param name="settings"></param>
|
||||
#if NET48
|
||||
public Options(Dictionary<string, string> settings = null)
|
||||
#else
|
||||
public Options(Dictionary<string, string?>? settings = null)
|
||||
#endif
|
||||
{
|
||||
this._settings = settings ?? new Dictionary<string, string>();
|
||||
#if NET48
|
||||
this.Settings = settings ?? new Dictionary<string, string>();
|
||||
#else
|
||||
this.Settings = settings ?? new Dictionary<string, string?>();
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a clone of the object
|
||||
/// Constructor taking an existing Options object
|
||||
/// </summary>
|
||||
public object Clone()
|
||||
/// <param name="source"></param>
|
||||
#if NET48
|
||||
public Options(Options source)
|
||||
#else
|
||||
public Options(Options? source)
|
||||
#endif
|
||||
{
|
||||
return new Options(new Dictionary<string, string>(_settings));
|
||||
#if NET48
|
||||
Settings = new Dictionary<string, string>(source?.Settings ?? new Dictionary<string, string>());
|
||||
#else
|
||||
Settings = new Dictionary<string, string?>(source?.Settings ?? new Dictionary<string, string?>());
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for the internal dictionary
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string this[string key]
|
||||
#else
|
||||
public string? this[string key]
|
||||
#endif
|
||||
{
|
||||
get => this.Settings[key];
|
||||
set => this.Settings[key] = value;
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
@@ -554,11 +686,15 @@ namespace MPF.Core.Data
|
||||
/// <param name="key">Setting key to get a value for</param>
|
||||
/// <param name="defaultValue">Default value to return if no value is found</param>
|
||||
/// <returns>Setting value if possible, default value otherwise</returns>
|
||||
private bool GetBooleanSetting(Dictionary<string, string> settings, string key, bool defaultValue)
|
||||
#if NET48
|
||||
private static bool GetBooleanSetting(Dictionary<string, string> settings, string key, bool defaultValue)
|
||||
#else
|
||||
private static bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
|
||||
#endif
|
||||
{
|
||||
if (settings.ContainsKey(key))
|
||||
{
|
||||
if (Boolean.TryParse(settings[key], out bool value))
|
||||
if (bool.TryParse(settings[key], out bool value))
|
||||
return value;
|
||||
else
|
||||
return defaultValue;
|
||||
@@ -576,11 +712,15 @@ namespace MPF.Core.Data
|
||||
/// <param name="key">Setting key to get a value for</param>
|
||||
/// <param name="defaultValue">Default value to return if no value is found</param>
|
||||
/// <returns>Setting value if possible, default value otherwise</returns>
|
||||
private int GetInt32Setting(Dictionary<string, string> settings, string key, int defaultValue)
|
||||
#if NET48
|
||||
private static int GetInt32Setting(Dictionary<string, string> settings, string key, int defaultValue)
|
||||
#else
|
||||
private static int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
|
||||
#endif
|
||||
{
|
||||
if (settings.ContainsKey(key))
|
||||
{
|
||||
if (Int32.TryParse(settings[key], out int value))
|
||||
if (int.TryParse(settings[key], out int value))
|
||||
return value;
|
||||
else
|
||||
return defaultValue;
|
||||
@@ -598,7 +738,11 @@ namespace MPF.Core.Data
|
||||
/// <param name="key">Setting key to get a value for</param>
|
||||
/// <param name="defaultValue">Default value to return if no value is found</param>
|
||||
/// <returns>Setting value if possible, default value otherwise</returns>
|
||||
private string GetStringSetting(Dictionary<string, string> settings, string key, string defaultValue)
|
||||
#if NET48
|
||||
private static string GetStringSetting(Dictionary<string, string> settings, string key, string defaultValue)
|
||||
#else
|
||||
private static string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
|
||||
#endif
|
||||
{
|
||||
if (settings.ContainsKey(key))
|
||||
return settings[key];
|
||||
@@ -607,45 +751,5 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDictionary implementations
|
||||
|
||||
public ICollection<string> Keys => _settings.Keys;
|
||||
|
||||
public ICollection<string> Values => _settings.Values;
|
||||
|
||||
public int Count => _settings.Count;
|
||||
|
||||
public bool IsReadOnly => ((IDictionary<string, string>)_settings).IsReadOnly;
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get { return (_settings.ContainsKey(key) ? _settings[key] : null); }
|
||||
set { _settings[key] = value; }
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key) => _settings.ContainsKey(key);
|
||||
|
||||
public void Add(string key, string value) => _settings.Add(key, value);
|
||||
|
||||
public bool Remove(string key) => _settings.Remove(key);
|
||||
|
||||
public bool TryGetValue(string key, out string value) => _settings.TryGetValue(key, out value);
|
||||
|
||||
public void Add(KeyValuePair<string, string> item) => _settings.Add(item.Key, item.Value);
|
||||
|
||||
public void Clear() => _settings.Clear();
|
||||
|
||||
public bool Contains(KeyValuePair<string, string> item) => ((IDictionary<string, string>)_settings).Contains(item);
|
||||
|
||||
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) => ((IDictionary<string, string>)_settings).CopyTo(array, arrayIndex);
|
||||
|
||||
public bool Remove(KeyValuePair<string, string> item) => ((IDictionary<string, string>)_settings).Remove(item);
|
||||
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() => _settings.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _settings.GetEnumerator();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace MPF.Core.Data
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
// Only accept new data when not cancelled
|
||||
if (!this.TokenSource.IsCancellationRequested)
|
||||
if (item != null && !this.TokenSource.IsCancellationRequested)
|
||||
this.InternalQueue.Enqueue(item);
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
// Get the next item from the queue
|
||||
if (!this.InternalQueue.TryDequeue(out T nextItem))
|
||||
if (!this.InternalQueue.TryDequeue(out var nextItem))
|
||||
continue;
|
||||
|
||||
// Invoke the lambda, if possible
|
||||
|
||||
@@ -30,7 +30,11 @@
|
||||
/// Create a success result with a custom message
|
||||
/// </summary>
|
||||
/// <param name="message">String to add as a message</param>
|
||||
#if NET48
|
||||
public static Result Success(string message) => new Result(true, message);
|
||||
#else
|
||||
public static Result Success(string? message) => new Result(true, message ?? string.Empty);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Create a default failure result with no message
|
||||
@@ -42,7 +46,11 @@
|
||||
/// Create a failure result with a custom message
|
||||
/// </summary>
|
||||
/// <param name="message">String to add as a message</param>
|
||||
#if NET48
|
||||
public static Result Failure(string message) => new Result(false, message);
|
||||
#else
|
||||
public static Result Failure(string? message) => new Result(false, message ?? string.Empty);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Results can be compared to boolean values based on the success value
|
||||
|
||||
@@ -1,423 +0,0 @@
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information specific to an XGD disc
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// XGD1 XMID Format Information:
|
||||
///
|
||||
/// AABBBCCD
|
||||
/// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details)
|
||||
/// - BBB => Game ID
|
||||
/// - CC => Version number
|
||||
/// - D => Region identifier (see GetRegion for details)
|
||||
///
|
||||
/// XGD2/3 XeMID Format Information:
|
||||
///
|
||||
/// AABCCCDDEFFGHH(IIIIIIII)
|
||||
/// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details)
|
||||
/// - B => Platform identifier; 2 indicates Xbox 360.
|
||||
/// - CCC => Game ID
|
||||
/// - DD => SKU number (unique per SKU of a title)
|
||||
/// - E => Region identifier (see GetRegion for details)
|
||||
/// - FF => Base version; usually starts at 01 (can be 1 or 2 characters)
|
||||
/// - G => Media type identifier (see GetMediaSubtype for details)
|
||||
/// - HH => Disc number stored in [disc number][total discs] format
|
||||
/// - IIIIIIII => 8-hex-digit certification submission identifier; usually on test discs only
|
||||
/// </remarks>
|
||||
public class XgdInfo
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the information in this object is fully instantiated or not
|
||||
/// </summary>
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Raw XMID/XeMID string that all other information is derived from
|
||||
/// </summary>
|
||||
public string XMID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 2-character publisher identifier
|
||||
/// </summary>
|
||||
public string PublisherIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Platform disc is made for, 2 indicates Xbox 360
|
||||
/// </summary>
|
||||
public char? PlatformIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Game ID
|
||||
/// </summary>
|
||||
public string GameID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// For XGD1: Internal version number
|
||||
/// For XGD2/3: Title-specific SKU
|
||||
/// </summary>
|
||||
public string SKU { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Region identifier character
|
||||
/// </summary>
|
||||
public char RegionIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Base version of executables, usually starts at 01
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TODO: Check if this is always 2 characters for XGD2/3
|
||||
/// </remarks>
|
||||
public string BaseVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Media subtype identifier
|
||||
/// </summary>
|
||||
public char MediaSubtypeIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Disc number stored in [disc number][total discs] format
|
||||
/// </summary>
|
||||
public string DiscNumberIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 8-hex-digit certification submission identifier; usually on test discs only
|
||||
/// </summary>
|
||||
public string CertificationSubmissionIdentifier { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Auto-Generated Information
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable name derived from the publisher identifier
|
||||
/// </summary>
|
||||
public string PublisherName => GetPublisher(this.PublisherIdentifier);
|
||||
|
||||
/// <summary>
|
||||
/// Internally represented region
|
||||
/// </summary>
|
||||
public Region? InternalRegion => GetRegion(this.RegionIdentifier);
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable subtype derived from the media identifier
|
||||
/// </summary>
|
||||
public string MediaSubtype => GetMediaSubtype(this.MediaSubtypeIdentifier);
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Populate a set of XGD information from a Master ID (XMID/XeMID) string
|
||||
/// </summary>
|
||||
/// <param name="xmid">XMID/XeMID string representing the DMI information</param>
|
||||
/// <param name="validate">True if value validation should be performed, false otherwise</param>
|
||||
public XgdInfo(string xmid, bool validate = false)
|
||||
{
|
||||
this.Initialized = false;
|
||||
if (string.IsNullOrWhiteSpace(xmid))
|
||||
return;
|
||||
|
||||
this.XMID = xmid.TrimEnd('\0');
|
||||
if (string.IsNullOrWhiteSpace(this.XMID))
|
||||
return;
|
||||
|
||||
// XGD1 information is 8 characters
|
||||
if (this.XMID.Length == 8)
|
||||
this.Initialized = ParseXGD1XMID(this.XMID, validate);
|
||||
|
||||
// XGD2/3 information is semi-variable length
|
||||
else if (this.XMID.Length == 13 || this.XMID.Length == 14 || this.XMID.Length == 21 || this.XMID.Length == 22)
|
||||
this.Initialized = ParseXGD23XeMID(this.XMID, validate);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the human-readable serial string
|
||||
/// </summary>
|
||||
/// <returns>Formatted serial string, null on error</returns>
|
||||
public string GetSerial()
|
||||
{
|
||||
if (!this.Initialized)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// XGD1 doesn't use PlatformIdentifier
|
||||
if (this.PlatformIdentifier == null)
|
||||
return $"{this.PublisherIdentifier}-{this.GameID}";
|
||||
|
||||
// XGD2/3 uses a specific identifier
|
||||
else if (this.PlatformIdentifier == '2')
|
||||
return $"{this.PublisherIdentifier}-{this.PlatformIdentifier}{this.GameID}";
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the human-readable version string
|
||||
/// </summary>
|
||||
/// <returns>Formatted version string, null on error</returns>
|
||||
/// <remarks>This may differ for XGD2/3 in the future</remarks>
|
||||
public string GetVersion()
|
||||
{
|
||||
if (!this.Initialized)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// XGD1 doesn't use PlatformIdentifier
|
||||
if (this.PlatformIdentifier == null)
|
||||
return $"1.{this.SKU}";
|
||||
|
||||
// XGD2/3 uses a specific identifier
|
||||
else if (this.PlatformIdentifier == '2')
|
||||
return $"1.{this.SKU}";
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an XGD1 XMID string
|
||||
/// </summary>
|
||||
/// <param name="xmid">XMID string to attempt to parse</param>
|
||||
/// <param name="validate">True if value validation should be performed, false otherwise</param>
|
||||
/// <returns>True if the XMID could be parsed, false otherwise</returns>
|
||||
private bool ParseXGD1XMID(string xmid, bool validate)
|
||||
{
|
||||
if (xmid == null || xmid.Length != 8)
|
||||
return false;
|
||||
|
||||
this.PublisherIdentifier = xmid.Substring(0, 2);
|
||||
if (validate && string.IsNullOrEmpty(this.PublisherName))
|
||||
return false;
|
||||
|
||||
this.GameID = xmid.Substring(2, 3);
|
||||
this.SKU = xmid.Substring(5, 2);
|
||||
this.RegionIdentifier = xmid[7];
|
||||
if (validate && this.InternalRegion == null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an XGD2/3 XeMID string
|
||||
/// </summary>
|
||||
/// <param name="xemid">XeMID string to attempt to parse</param>
|
||||
/// <param name="validate">True if value validation should be performed, false otherwise</param>
|
||||
/// <returns>True if the XeMID could be parsed, false otherwise</returns>
|
||||
private bool ParseXGD23XeMID(string xemid, bool validate)
|
||||
{
|
||||
if (xemid == null
|
||||
|| (xemid.Length != 13 && xemid.Length != 14
|
||||
&& xemid.Length != 21 && xemid.Length != 22))
|
||||
return false;
|
||||
|
||||
this.PublisherIdentifier = xemid.Substring(0, 2);
|
||||
if (validate && string.IsNullOrEmpty(this.PublisherName))
|
||||
return false;
|
||||
|
||||
this.PlatformIdentifier = xemid[2];
|
||||
if (validate && this.PlatformIdentifier != '2')
|
||||
return false;
|
||||
|
||||
this.GameID = xemid.Substring(3, 3);
|
||||
this.SKU = xemid.Substring(6, 2);
|
||||
this.RegionIdentifier = xemid[8];
|
||||
if (validate && this.InternalRegion == null)
|
||||
return false;
|
||||
|
||||
if (xemid.Length == 13 || xemid.Length == 21)
|
||||
{
|
||||
this.BaseVersion = xemid.Substring(9, 1);
|
||||
this.MediaSubtypeIdentifier = xemid[10];
|
||||
if (validate && string.IsNullOrEmpty(this.MediaSubtype))
|
||||
return false;
|
||||
|
||||
this.DiscNumberIdentifier = xemid.Substring(11, 2);
|
||||
}
|
||||
else if (xemid.Length == 14 || xemid.Length == 22)
|
||||
{
|
||||
this.BaseVersion = xemid.Substring(9, 2);
|
||||
this.MediaSubtypeIdentifier = xemid[11];
|
||||
if (validate && string.IsNullOrEmpty(this.MediaSubtype))
|
||||
return false;
|
||||
|
||||
this.DiscNumberIdentifier = xemid.Substring(12, 2);
|
||||
}
|
||||
|
||||
if (xemid.Length == 21)
|
||||
this.CertificationSubmissionIdentifier = xemid.Substring(13);
|
||||
else if (xemid.Length == 22)
|
||||
this.CertificationSubmissionIdentifier = xemid.Substring(14);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Determine the XGD type based on the XGD2/3 media type identifier character
|
||||
/// </summary>
|
||||
/// <param name="mediaTypeIdentifier">Character denoting the media type</param>
|
||||
/// <returns>Media subtype as a string, if possible</returns>
|
||||
private static string GetMediaSubtype(char mediaTypeIdentifier)
|
||||
{
|
||||
switch (mediaTypeIdentifier)
|
||||
{
|
||||
case 'F': return "XGD3";
|
||||
case 'X': return "XGD2";
|
||||
case 'Z': return "Games on Demand / Marketplace Demo";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the full name of the publisher from the 2-character identifier
|
||||
/// </summary>
|
||||
/// <param name="publisherIdentifier">Case-sensitive 2-character identifier</param>
|
||||
/// <returns>Publisher name, if possible</returns>
|
||||
/// <see cref="https://xboxdevwiki.net/Xbe#Title_ID"/>
|
||||
private static string GetPublisher(string publisherIdentifier)
|
||||
{
|
||||
switch (publisherIdentifier)
|
||||
{
|
||||
case "AC": return "Acclaim Entertainment";
|
||||
case "AH": return "ARUSH Entertainment";
|
||||
case "AQ": return "Aqua System";
|
||||
case "AS": return "ASK";
|
||||
case "AT": return "Atlus";
|
||||
case "AV": return "Activision";
|
||||
case "AY": return "Aspyr Media";
|
||||
case "BA": return "Bandai";
|
||||
case "BL": return "Black Box";
|
||||
case "BM": return "BAM! Entertainment";
|
||||
case "BR": return "Broccoli Co.";
|
||||
case "BS": return "Bethesda Softworks";
|
||||
case "BU": return "Bunkasha Co.";
|
||||
case "BV": return "Buena Vista Games";
|
||||
case "BW": return "BBC Multimedia";
|
||||
case "BZ": return "Blizzard";
|
||||
case "CC": return "Capcom";
|
||||
case "CK": return "Kemco Corporation"; // TODO: Confirm
|
||||
case "CM": return "Codemasters";
|
||||
case "CV": return "Crave Entertainment";
|
||||
case "DC": return "DreamCatcher Interactive";
|
||||
case "DX": return "Davilex";
|
||||
case "EA": return "Electronic Arts (EA)";
|
||||
case "EC": return "Encore inc";
|
||||
case "EL": return "Enlight Software";
|
||||
case "EM": return "Empire Interactive";
|
||||
case "ES": return "Eidos Interactive";
|
||||
case "FI": return "Fox Interactive";
|
||||
case "FS": return "From Software";
|
||||
case "GE": return "Genki Co.";
|
||||
case "GV": return "Groove Games";
|
||||
case "HE": return "Tru Blu (Entertainment division of Home Entertainment Suppliers)";
|
||||
case "HP": return "Hip games";
|
||||
case "HU": return "Hudson Soft";
|
||||
case "HW": return "Highwaystar";
|
||||
case "IA": return "Mad Catz Interactive";
|
||||
case "IF": return "Idea Factory";
|
||||
case "IG": return "Infogrames";
|
||||
case "IL": return "Interlex Corporation";
|
||||
case "IM": return "Imagine Media";
|
||||
case "IO": return "Ignition Entertainment";
|
||||
case "IP": return "Interplay Entertainment";
|
||||
case "IX": return "InXile Entertainment"; // TODO: Confirm
|
||||
case "JA": return "Jaleco";
|
||||
case "JW": return "JoWooD";
|
||||
case "KB": return "Kemco"; // TODO: Confirm
|
||||
case "KI": return "Kids Station Inc."; // TODO: Confirm
|
||||
case "KN": return "Konami";
|
||||
case "KO": return "KOEI";
|
||||
case "KU": return "Kobi and / or GAE (formerly Global A Entertainment)"; // TODO: Confirm
|
||||
case "LA": return "LucasArts";
|
||||
case "LS": return "Black Bean Games (publishing arm of Leader S.p.A.)";
|
||||
case "MD": return "Metro3D";
|
||||
case "ME": return "Medix";
|
||||
case "MI": return "Microïds";
|
||||
case "MJ": return "Majesco Entertainment";
|
||||
case "MM": return "Myelin Media";
|
||||
case "MP": return "MediaQuest"; // TODO: Confirm
|
||||
case "MS": return "Microsoft Game Studios";
|
||||
case "MW": return "Midway Games";
|
||||
case "MX": return "Empire Interactive"; // TODO: Confirm
|
||||
case "NK": return "NewKidCo";
|
||||
case "NL": return "NovaLogic";
|
||||
case "NM": return "Namco";
|
||||
case "OX": return "Oxygen Interactive";
|
||||
case "PC": return "Playlogic Entertainment";
|
||||
case "PL": return "Phantagram Co., Ltd.";
|
||||
case "RA": return "Rage";
|
||||
case "SA": return "Sammy";
|
||||
case "SC": return "SCi Games";
|
||||
case "SE": return "SEGA";
|
||||
case "SN": return "SNK";
|
||||
case "SS": return "Simon & Schuster";
|
||||
case "SU": return "Success Corporation";
|
||||
case "SW": return "Swing! Deutschland";
|
||||
case "TA": return "Takara";
|
||||
case "TC": return "Tecmo";
|
||||
case "TD": return "The 3DO Company (or just 3DO)";
|
||||
case "TK": return "Takuyo";
|
||||
case "TM": return "TDK Mediactive";
|
||||
case "TQ": return "THQ";
|
||||
case "TS": return "Titus Interactive";
|
||||
case "TT": return "Take-Two Interactive Software";
|
||||
case "US": return "Ubisoft";
|
||||
case "VC": return "Victor Interactive Software";
|
||||
case "VN": return "Vivendi Universal (just took Interplays publishing rights)"; // TODO: Confirm
|
||||
case "VU": return "Vivendi Universal Games";
|
||||
case "VV": return "Vivendi Universal Games"; // TODO: Confirm
|
||||
case "WE": return "Wanadoo Edition";
|
||||
case "WR": return "Warner Bros. Interactive Entertainment"; // TODO: Confirm
|
||||
case "XI": return "XPEC Entertainment and Idea Factory";
|
||||
case "XK": return "Xbox kiosk disk?"; // TODO: Confirm
|
||||
case "XL": return "Xbox special bundled or live demo disk?"; // TODO: Confirm
|
||||
case "XM": return "Evolved Games"; // TODO: Confirm
|
||||
case "XP": return "XPEC Entertainment";
|
||||
case "XR": return "Panorama";
|
||||
case "YB": return "YBM Sisa (South-Korea)";
|
||||
case "ZD": return "Zushi Games (formerly Zoo Digital Publishing)";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the region based on the XGD serial character
|
||||
/// </summary>
|
||||
/// <param name="region">Character denoting the region</param>
|
||||
/// <returns>Region, if possible</returns>
|
||||
private static Region? GetRegion(char region)
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
case 'W': return Region.World;
|
||||
case 'A': return Region.UnitedStatesOfAmerica;
|
||||
case 'J': return Region.JapanAsia;
|
||||
case 'E': return Region.Europe;
|
||||
case 'K': return Region.USAJapan;
|
||||
case 'L': return Region.USAEurope;
|
||||
case 'H': return Region.JapanEurope;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using BurnOutSharp;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.Modules;
|
||||
using RedumpLib.Data;
|
||||
using MPF.Core.Modules;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Library
|
||||
namespace MPF.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the state of all settings to be used during dumping
|
||||
@@ -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
|
||||
|
||||
@@ -35,7 +30,11 @@ namespace MPF.Library
|
||||
/// <summary>
|
||||
/// Drive object representing the current drive
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Drive Drive { get; private set; }
|
||||
#else
|
||||
public Drive? Drive { get; private set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected system
|
||||
@@ -47,39 +46,60 @@ namespace MPF.Library
|
||||
/// </summary>
|
||||
public MediaType? Type { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected dumping program
|
||||
/// </summary>
|
||||
public InternalProgram InternalProgram { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Options object representing user-defined options
|
||||
/// </summary>
|
||||
public Options Options { get; private set; }
|
||||
public Data.Options Options { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parameters object representing what to send to the internal program
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public BaseParameters Parameters { get; private set; }
|
||||
#else
|
||||
public BaseParameters? Parameters { get; private set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Generic way of reporting a message
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public EventHandler<string> ReportStatus;
|
||||
#else
|
||||
public EventHandler<string>? ReportStatus;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Queue of items that need to be logged
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private ProcessingQueue<string> outputQueue;
|
||||
#else
|
||||
private ProcessingQueue<string>? outputQueue;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for data returned from a process
|
||||
/// </summary>
|
||||
private void OutputToLog(object proc, string args) => outputQueue.Enqueue(args);
|
||||
#if NET48
|
||||
private void OutputToLog(object proc, string args) => outputQueue?.Enqueue(args);
|
||||
#else
|
||||
private void OutputToLog(object? proc, string args) => outputQueue?.Enqueue(args);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Process the outputs in the queue
|
||||
/// </summary>
|
||||
private void ProcessOutputs(string nextOutput) => ReportStatus.Invoke(this, nextOutput);
|
||||
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, nextOutput);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -87,109 +107,114 @@ namespace MPF.Library
|
||||
/// Constructor for a full DumpEnvironment object from user information
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="outputDirectory"></param>
|
||||
/// <param name="outputFilename"></param>
|
||||
/// <param name="outputPath"></param>
|
||||
/// <param name="drive"></param>
|
||||
/// <param name="system"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="internalProgram"></param>
|
||||
/// <param name="parameters"></param>
|
||||
public DumpEnvironment(Options options,
|
||||
string outputDirectory,
|
||||
string outputFilename,
|
||||
public DumpEnvironment(Data.Options options,
|
||||
string outputPath,
|
||||
#if NET48
|
||||
Drive drive,
|
||||
#else
|
||||
Drive? drive,
|
||||
#endif
|
||||
RedumpSystem? system,
|
||||
MediaType? type,
|
||||
InternalProgram? internalProgram,
|
||||
#if NET48
|
||||
string parameters)
|
||||
#else
|
||||
string? parameters)
|
||||
#endif
|
||||
{
|
||||
// Set options object
|
||||
this.Options = options;
|
||||
Options = options;
|
||||
|
||||
// Output paths
|
||||
(this.OutputDirectory, this.OutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename);
|
||||
OutputPath = InfoTool.NormalizeOutputPaths(outputPath, false);
|
||||
|
||||
// UI information
|
||||
this.Drive = drive;
|
||||
this.System = system ?? options.DefaultSystem;
|
||||
this.Type = type ?? MediaType.NONE;
|
||||
|
||||
Drive = drive;
|
||||
System = system ?? options.DefaultSystem;
|
||||
Type = type ?? MediaType.NONE;
|
||||
InternalProgram = internalProgram ?? options.InternalProgram;
|
||||
|
||||
// Dumping program
|
||||
SetParameters(parameters);
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
// Replace all instances in the output directory
|
||||
this.OutputDirectory = this.OutputDirectory.Replace('.', '_');
|
||||
|
||||
// Currently, only periods in directories matter
|
||||
// Leave the following code commented in case filename handling breaks again
|
||||
|
||||
// Replace all instances in the output filename, except the extension
|
||||
//string tempFilename = Path.GetFileNameWithoutExtension(this.OutputFilename)
|
||||
// .Replace('.', '_');
|
||||
//string tempExtension = Path.GetExtension(this.OutputFilename)?.TrimStart('.');
|
||||
//this.OutputFilename = $"{tempFilename}.{tempExtension}";
|
||||
|
||||
// Assign the path to the filename as well for dumping
|
||||
((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = Path.Combine(this.OutputDirectory, this.OutputFilename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the parameters object based on the internal program and parameters string
|
||||
/// </summary>
|
||||
/// <param name="parameters">String representation of the parameters</param>
|
||||
#if NET48
|
||||
public void SetParameters(string parameters)
|
||||
#else
|
||||
public void SetParameters(string? parameters)
|
||||
#endif
|
||||
{
|
||||
switch (Options.InternalProgram)
|
||||
#if NET48
|
||||
switch (InternalProgram)
|
||||
{
|
||||
// Dumping support
|
||||
case InternalProgram.Aaru:
|
||||
this.Parameters = new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath };
|
||||
break;
|
||||
|
||||
case InternalProgram.DD:
|
||||
this.Parameters = new Modules.DD.Parameters(parameters) { ExecutablePath = Options.DDPath };
|
||||
Parameters = new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath };
|
||||
break;
|
||||
|
||||
case InternalProgram.DiscImageCreator:
|
||||
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
|
||||
break;
|
||||
|
||||
case InternalProgram.Redumper:
|
||||
this.Parameters = new Modules.DD.Parameters(parameters) { ExecutablePath = Options.RedumperPath };
|
||||
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 };
|
||||
Parameters = new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null };
|
||||
break;
|
||||
|
||||
case InternalProgram.DCDumper:
|
||||
this.Parameters = null; // TODO: Create correct parameter type when supported
|
||||
Parameters = null; // TODO: Create correct parameter type when supported
|
||||
break;
|
||||
|
||||
case InternalProgram.UmdImageCreator:
|
||||
this.Parameters = new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null };
|
||||
Parameters = new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null };
|
||||
break;
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
default:
|
||||
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
|
||||
break;
|
||||
}
|
||||
#else
|
||||
Parameters = InternalProgram switch
|
||||
{
|
||||
// Dumping support
|
||||
InternalProgram.Aaru => new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath },
|
||||
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath },
|
||||
InternalProgram.Redumper => new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath },
|
||||
|
||||
// Verification support only
|
||||
InternalProgram.CleanRip => new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null },
|
||||
InternalProgram.DCDumper => null, // TODO: Create correct parameter type when supported
|
||||
InternalProgram.UmdImageCreator => new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null },
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
_ => new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath },
|
||||
};
|
||||
#endif
|
||||
|
||||
// Set system and type
|
||||
this.Parameters.System = this.System;
|
||||
this.Parameters.Type = this.Type;
|
||||
if (Parameters != null)
|
||||
{
|
||||
Parameters.System = System;
|
||||
Parameters.Type = Type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -197,7 +222,11 @@ namespace MPF.Library
|
||||
/// </summary>
|
||||
/// <param name="driveSpeed">Nullable int representing the drive speed</param>
|
||||
/// <returns>String representing the params, null on error</returns>
|
||||
#if NET48
|
||||
public string GetFullParameters(int? driveSpeed)
|
||||
#else
|
||||
public string? GetFullParameters(int? driveSpeed)
|
||||
#endif
|
||||
{
|
||||
// Populate with the correct params for inputs (if we're not on the default option)
|
||||
if (System != null && Type != MediaType.NONE)
|
||||
@@ -207,30 +236,37 @@ namespace MPF.Library
|
||||
return null;
|
||||
|
||||
// Set the proper parameters
|
||||
string filename = OutputDirectory + Path.DirectorySeparatorChar + OutputFilename;
|
||||
switch (Options.InternalProgram)
|
||||
#if NET48
|
||||
switch (InternalProgram)
|
||||
{
|
||||
case InternalProgram.Aaru:
|
||||
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
case InternalProgram.DD:
|
||||
Parameters = new Modules.DD.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Name, 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.Name, OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
case InternalProgram.Redumper:
|
||||
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Name, 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.Name, OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
}
|
||||
#else
|
||||
Parameters = InternalProgram switch
|
||||
{
|
||||
InternalProgram.Aaru => new Modules.Aaru.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
InternalProgram.Redumper => new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
_ => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
};
|
||||
#endif
|
||||
|
||||
// Generate and return the param string
|
||||
return Parameters.GenerateParameters();
|
||||
@@ -246,26 +282,42 @@ namespace MPF.Library
|
||||
/// <summary>
|
||||
/// Cancel an in-progress dumping process
|
||||
/// </summary>
|
||||
public void CancelDumping() => Parameters.KillInternalProgram();
|
||||
public void CancelDumping() => Parameters?.KillInternalProgram();
|
||||
|
||||
/// <summary>
|
||||
/// Eject the disc using DiscImageCreator
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public async Task<string> EjectDisc() =>
|
||||
#else
|
||||
public async Task<string?> EjectDisc() =>
|
||||
#endif
|
||||
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
|
||||
|
||||
/// <summary>
|
||||
/// Reset the current drive using DiscImageCreator
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public async Task<string> ResetDrive() =>
|
||||
#else
|
||||
public async Task<string?> ResetDrive() =>
|
||||
#endif
|
||||
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Reset);
|
||||
|
||||
/// <summary>
|
||||
/// Execute the initial invocation of the dumping programs
|
||||
/// </summary>
|
||||
/// <param name="progress">Optional result progress callback</param>
|
||||
#if NET48
|
||||
public async Task<Result> Run(IProgress<Result> progress = null)
|
||||
#else
|
||||
public async Task<Result> Run(IProgress<Result>? progress = null)
|
||||
#endif
|
||||
{
|
||||
// If we don't have parameters
|
||||
if (Parameters == null)
|
||||
return Result.Failure("Error! Current configuration is not supported!");
|
||||
|
||||
// Check that we have the basics for dumping
|
||||
Result result = IsValidForDump();
|
||||
if (!result)
|
||||
@@ -275,24 +327,24 @@ namespace MPF.Library
|
||||
if (!Options.ToolsInSeparateWindow)
|
||||
{
|
||||
outputQueue = new ProcessingQueue<string>(ProcessOutputs);
|
||||
Parameters.ReportStatus += OutputToLog;
|
||||
if (Parameters.ReportStatus != null)
|
||||
Parameters.ReportStatus += OutputToLog;
|
||||
}
|
||||
|
||||
// Execute internal tool
|
||||
progress?.Report(Result.Success($"Executing {Options.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
|
||||
Directory.CreateDirectory(OutputDirectory);
|
||||
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
|
||||
progress?.Report(Result.Success($"{Options.InternalProgram} has finished!"));
|
||||
progress?.Report(Result.Success($"Executing {InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
|
||||
|
||||
// Execute additional tools
|
||||
progress?.Report(Result.Success("Running any additional tools... see log for output!"));
|
||||
result = await Task.Run(() => ExecuteAdditionalTools());
|
||||
progress?.Report(result);
|
||||
var directoryName = Path.GetDirectoryName(OutputPath);
|
||||
if (!string.IsNullOrWhiteSpace(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
|
||||
progress?.Report(Result.Success($"{InternalProgram} has finished!"));
|
||||
|
||||
// Remove event handler if needed
|
||||
if (!Options.ToolsInSeparateWindow)
|
||||
{
|
||||
outputQueue.Dispose();
|
||||
outputQueue?.Dispose();
|
||||
Parameters.ReportStatus -= OutputToLog;
|
||||
}
|
||||
|
||||
@@ -305,16 +357,29 @@ namespace MPF.Library
|
||||
/// <param name="resultProgress">Optional result progress callback</param>
|
||||
/// <param name="protectionProgress">Optional protection progress callback</param>
|
||||
/// <param name="processUserInfo">Optional user prompt to deal with submission information</param>
|
||||
/// <param name="seedInfo">A seed SubmissionInfo object that contains user data</param>
|
||||
/// <returns>Result instance with the outcome</returns>
|
||||
public async Task<Result> VerifyAndSaveDumpOutput(
|
||||
#if NET48
|
||||
IProgress<Result> resultProgress = null,
|
||||
IProgress<ProtectionProgress> protectionProgress = null,
|
||||
Func<SubmissionInfo, (bool?, SubmissionInfo)> processUserInfo = null)
|
||||
Func<SubmissionInfo, (bool?, SubmissionInfo)> processUserInfo = null,
|
||||
SubmissionInfo seedInfo = null)
|
||||
#else
|
||||
IProgress<Result>? resultProgress = null,
|
||||
IProgress<ProtectionProgress>? protectionProgress = null,
|
||||
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null,
|
||||
SubmissionInfo? seedInfo = null)
|
||||
#endif
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
|
||||
|
||||
// Get the output directory and filename separately
|
||||
var outputDirectory = Path.GetDirectoryName(OutputPath);
|
||||
var outputFilename = Path.GetFileName(OutputPath);
|
||||
|
||||
// Check to make sure that the output had all the correct files
|
||||
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(this.OutputDirectory, this.OutputFilename, this.Parameters, false);
|
||||
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, Parameters, false);
|
||||
if (!foundFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
|
||||
@@ -323,29 +388,36 @@ 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.Drive,
|
||||
this.System,
|
||||
this.Type,
|
||||
this.Options,
|
||||
this.Parameters,
|
||||
var submissionInfo = await SubmissionInfoTool.ExtractOutputInformation(
|
||||
OutputPath,
|
||||
Drive,
|
||||
System,
|
||||
Type,
|
||||
Options,
|
||||
Parameters,
|
||||
resultProgress,
|
||||
protectionProgress);
|
||||
resultProgress?.Report(Result.Success("Extracting information complete!"));
|
||||
|
||||
// Inject seed submission info data, if necessary
|
||||
if (seedInfo != null)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Injecting user-supplied information..."));
|
||||
SubmissionInfoTool.InjectSubmissionInformation(submissionInfo, seedInfo);
|
||||
resultProgress?.Report(Result.Success("Information injection complete!"));
|
||||
}
|
||||
|
||||
// Eject the disc automatically if configured to
|
||||
if (Options.EjectAfterDump == true)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive.Letter}"));
|
||||
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive?.Name}"));
|
||||
await EjectDisc();
|
||||
}
|
||||
|
||||
// Reset the drive automatically if configured to
|
||||
if (Options.InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
|
||||
if (InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Resetting drive {Drive.Letter}"));
|
||||
resultProgress?.Report(Result.Success($"Resetting drive {Drive?.Name}"));
|
||||
await ResetDrive();
|
||||
}
|
||||
|
||||
@@ -370,15 +442,18 @@ namespace MPF.Library
|
||||
|
||||
// Format the information for the text output
|
||||
resultProgress?.Report(Result.Success("Formatting information..."));
|
||||
(List<string> formattedValues, string formatResult) = InfoTool.FormatOutputData(submissionInfo, this.Options);
|
||||
(var formattedValues, var formatResult) = InfoTool.FormatOutputData(submissionInfo, Options);
|
||||
if (formattedValues == null)
|
||||
resultProgress?.Report(Result.Success(formatResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(formatResult));
|
||||
|
||||
// Get the filename suffix for auto-generated files
|
||||
var filenameSuffix = Options.AddFilenameSuffix ? Path.GetFileNameWithoutExtension(outputFilename) : null;
|
||||
|
||||
// Write the text output
|
||||
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
|
||||
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(this.OutputDirectory, formattedValues);
|
||||
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, formattedValues);
|
||||
if (txtSuccess)
|
||||
resultProgress?.Report(Result.Success(txtResult));
|
||||
else
|
||||
@@ -388,7 +463,7 @@ namespace MPF.Library
|
||||
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(this.OutputDirectory, submissionInfo);
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo);
|
||||
if (scanSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
@@ -399,7 +474,7 @@ namespace MPF.Library
|
||||
if (Options.OutputSubmissionJSON)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Writing information to !submissionInfo.json{(Options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
|
||||
bool jsonSuccess = InfoTool.WriteOutputData(this.OutputDirectory, submissionInfo, Options.IncludeArtifacts);
|
||||
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, submissionInfo, Options.IncludeArtifacts);
|
||||
if (jsonSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
@@ -410,13 +485,24 @@ namespace MPF.Library
|
||||
if (Options.CompressLogFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Compressing log files..."));
|
||||
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(this.OutputDirectory, this.OutputFilename, this.Parameters);
|
||||
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, Parameters);
|
||||
if (compressSuccess)
|
||||
resultProgress?.Report(Result.Success(compressResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(compressResult));
|
||||
}
|
||||
|
||||
// Delete unnecessary files, if required
|
||||
if (Options.DeleteUnnecessaryFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Deleting unnecessary files..."));
|
||||
(bool deleteSuccess, string deleteResult) = InfoTool.DeleteUnnecessaryFiles(outputDirectory, outputFilename, Parameters);
|
||||
if (deleteSuccess)
|
||||
resultProgress?.Report(Result.Success(deleteResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(deleteResult));
|
||||
}
|
||||
|
||||
resultProgress?.Report(Result.Success("Submission information process complete!"));
|
||||
return Result.Success();
|
||||
}
|
||||
@@ -427,7 +513,11 @@ namespace MPF.Library
|
||||
/// <returns>True if the configuration is valid, false otherwise</returns>
|
||||
internal bool ParametersValid()
|
||||
{
|
||||
bool parametersValid = Parameters.IsValid();
|
||||
// Missing drive means it can never be valid
|
||||
if (Drive == null)
|
||||
return false;
|
||||
|
||||
bool parametersValid = Parameters?.IsValid() ?? false;
|
||||
bool floppyValid = !(Drive.InternalDriveType == InternalDriveType.Floppy ^ Type == MediaType.FloppyDisk);
|
||||
|
||||
// TODO: HardDisk being in the Removable category is a hack, fix this later
|
||||
@@ -437,18 +527,12 @@ namespace MPF.Library
|
||||
return parametersValid && floppyValid && removableDiskValid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run any additional tools given a DumpEnvironment
|
||||
/// </summary>
|
||||
/// <returns>Result instance with the outcome</returns>
|
||||
private Result ExecuteAdditionalTools() => Result.Success("No external tools needed!");
|
||||
|
||||
/// <summary>
|
||||
/// Run internal program async with an input set of parameters
|
||||
/// </summary>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns>Standard output from commandline window</returns>
|
||||
private async Task<string> ExecuteInternalProgram(BaseParameters parameters)
|
||||
private static async Task<string> ExecuteInternalProgram(BaseParameters parameters)
|
||||
{
|
||||
Process childProcess;
|
||||
string output = await Task.Run(() =>
|
||||
@@ -487,16 +571,15 @@ namespace MPF.Library
|
||||
private Result IsValidForDump()
|
||||
{
|
||||
// Validate that everything is good
|
||||
if (!ParametersValid())
|
||||
if (Parameters == null || !ParametersValid())
|
||||
return Result.Failure("Error! Current configuration is not supported!");
|
||||
|
||||
// Fix the output paths, just in case
|
||||
(OutputDirectory, OutputFilename) = InfoTool.NormalizeOutputPaths(OutputDirectory, OutputFilename);
|
||||
OutputPath = InfoTool.NormalizeOutputPaths(OutputPath, false);
|
||||
|
||||
// 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 (Drive?.Name != null && OutputPath.StartsWith(Drive.Name))
|
||||
return Result.Failure("Error! Cannot output to same drive that is being dumped!");
|
||||
|
||||
// Validate that the required program exists
|
||||
if (!File.Exists(Parameters.ExecutablePath))
|
||||
@@ -504,8 +587,8 @@ 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!");
|
||||
if (Drive?.Name != null && fullExecutablePath.StartsWith(Drive.Name))
|
||||
return Result.Failure("Error! Cannot dump same drive that executable resides on!");
|
||||
|
||||
// Validate that the current configuration is supported
|
||||
return Tools.GetSupportStatus(System, Type);
|
||||
@@ -533,14 +616,18 @@ namespace MPF.Library
|
||||
/// </summary>
|
||||
/// <param name="command">Command string to run</param>
|
||||
/// <returns>The output of the command on success, null on error</returns>
|
||||
#if NET48
|
||||
private async Task<string> RunStandaloneDiscImageCreatorCommand(string command)
|
||||
#else
|
||||
private async Task<string?> RunStandaloneDiscImageCreatorCommand(string command)
|
||||
#endif
|
||||
{
|
||||
// Validate that DiscImageCreator is all set
|
||||
if (!RequiredProgramsExist())
|
||||
return null;
|
||||
|
||||
// Validate we're not trying to eject a non-optical
|
||||
if (Drive.InternalDriveType != InternalDriveType.Optical)
|
||||
if (Drive == null || Drive.InternalDriveType != InternalDriveType.Optical)
|
||||
return null;
|
||||
|
||||
CancelDumping();
|
||||
@@ -548,7 +635,7 @@ namespace MPF.Library
|
||||
var parameters = new Modules.DiscImageCreator.Parameters(string.Empty)
|
||||
{
|
||||
BaseCommand = command,
|
||||
DriveLetter = Drive.Letter.ToString(),
|
||||
DrivePath = Drive.Name,
|
||||
ExecutablePath = Options.DiscImageCreatorPath,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.IO.Hashing;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
@@ -10,7 +11,7 @@ namespace MPF.Core.Hashing
|
||||
[Flags]
|
||||
public enum Hash
|
||||
{
|
||||
CRC = 1 << 0,
|
||||
CRC32 = 1 << 0,
|
||||
MD5 = 1 << 1,
|
||||
SHA1 = 1 << 2,
|
||||
SHA256 = 1 << 3,
|
||||
@@ -18,8 +19,8 @@ namespace MPF.Core.Hashing
|
||||
SHA512 = 1 << 5,
|
||||
|
||||
// Special combinations
|
||||
Standard = CRC | MD5 | SHA1,
|
||||
All = CRC | MD5 | SHA1 | SHA256 | SHA384 | SHA512,
|
||||
Standard = CRC32 | MD5 | SHA1,
|
||||
All = CRC32 | MD5 | SHA1 | SHA256 | SHA384 | SHA512,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +29,12 @@ namespace MPF.Core.Hashing
|
||||
public class Hasher
|
||||
{
|
||||
public Hash HashType { get; private set; }
|
||||
private IDisposable _hasher;
|
||||
|
||||
#if NET48
|
||||
private object _hasher;
|
||||
#else
|
||||
private object? _hasher;
|
||||
#endif
|
||||
|
||||
public Hasher(Hash hashType)
|
||||
{
|
||||
@@ -43,8 +49,8 @@ namespace MPF.Core.Hashing
|
||||
{
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
_hasher = new OptimizedCRC();
|
||||
case Hash.CRC32:
|
||||
_hasher = new Crc32();
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
@@ -71,7 +77,8 @@ namespace MPF.Core.Hashing
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_hasher.Dispose();
|
||||
if (_hasher is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -81,8 +88,9 @@ namespace MPF.Core.Hashing
|
||||
{
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
(_hasher as OptimizedCRC).Update(buffer, 0, size);
|
||||
case Hash.CRC32:
|
||||
var bufferSpan = new ReadOnlySpan<byte>(buffer, 0, size);
|
||||
(_hasher as NonCryptographicHashAlgorithm)?.Append(bufferSpan);
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
@@ -90,7 +98,7 @@ namespace MPF.Core.Hashing
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
(_hasher as HashAlgorithm).TransformBlock(buffer, 0, size, null, 0);
|
||||
(_hasher as HashAlgorithm)?.TransformBlock(buffer, 0, size, null, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -100,11 +108,11 @@ namespace MPF.Core.Hashing
|
||||
/// </summary>
|
||||
public void Terminate()
|
||||
{
|
||||
byte[] emptyBuffer = new byte[0];
|
||||
byte[] emptyBuffer = Array.Empty<byte>();
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
(_hasher as OptimizedCRC).Update(emptyBuffer, 0, 0);
|
||||
case Hash.CRC32:
|
||||
// No finalization is needed
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
@@ -112,7 +120,7 @@ namespace MPF.Core.Hashing
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
(_hasher as HashAlgorithm).TransformFinalBlock(emptyBuffer, 0, 0);
|
||||
(_hasher as HashAlgorithm)?.TransformFinalBlock(emptyBuffer, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -120,19 +128,26 @@ namespace MPF.Core.Hashing
|
||||
/// <summary>
|
||||
/// Get internal hash as a byte array
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public byte[] GetHash()
|
||||
#else
|
||||
public byte[]? GetHash()
|
||||
#endif
|
||||
{
|
||||
if (_hasher == null)
|
||||
return null;
|
||||
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
return BitConverter.GetBytes((_hasher as OptimizedCRC).Value).Reverse().ToArray();
|
||||
case Hash.CRC32:
|
||||
return (_hasher as NonCryptographicHashAlgorithm)?.GetCurrentHash()?.Reverse().ToArray();
|
||||
|
||||
case Hash.MD5:
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
return (_hasher as HashAlgorithm).Hash;
|
||||
return (_hasher as HashAlgorithm)?.Hash;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -141,12 +156,16 @@ namespace MPF.Core.Hashing
|
||||
/// <summary>
|
||||
/// Get internal hash as a string
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string GetHashString()
|
||||
#else
|
||||
public string? GetHashString()
|
||||
#endif
|
||||
{
|
||||
byte[] hash = GetHash();
|
||||
var hash = GetHash();
|
||||
if (hash == null)
|
||||
return null;
|
||||
|
||||
|
||||
return ByteArrayToString(hash);
|
||||
}
|
||||
|
||||
@@ -156,7 +175,11 @@ namespace MPF.Core.Hashing
|
||||
/// <param name="bytes">Byte array to convert</param>
|
||||
/// <returns>Hex string representing the byte array</returns>
|
||||
/// <link>http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa</link>
|
||||
#if NET48
|
||||
private static string ByteArrayToString(byte[] bytes)
|
||||
#else
|
||||
private static string? ByteArrayToString(byte[]? bytes)
|
||||
#endif
|
||||
{
|
||||
// If we get null in, we send null out
|
||||
if (bytes == null)
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2012-2015 Eugene Larchenko (spct@mail.ru)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
//namespace OptimizedCRC
|
||||
namespace MPF.Core.Hashing
|
||||
{
|
||||
internal class OptimizedCRC : IDisposable
|
||||
{
|
||||
private const uint kCrcPoly = 0xEDB88320;
|
||||
private const uint kInitial = 0xFFFFFFFF;
|
||||
private const int CRC_NUM_TABLES = 8;
|
||||
private static readonly uint[] Table;
|
||||
|
||||
static OptimizedCRC()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
Table = new uint[256 * CRC_NUM_TABLES];
|
||||
int i;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
uint r = (uint)i;
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
|
||||
}
|
||||
Table[i] = r;
|
||||
}
|
||||
for (; i < 256 * CRC_NUM_TABLES; i++)
|
||||
{
|
||||
uint r = Table[i - 256];
|
||||
Table[i] = Table[r & 0xFF] ^ (r >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint UnsignedValue;
|
||||
|
||||
public OptimizedCRC()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset CRC
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
UnsignedValue = kInitial;
|
||||
}
|
||||
|
||||
public int Value
|
||||
{
|
||||
get { return (int)~UnsignedValue; }
|
||||
}
|
||||
|
||||
public void Update(byte[] data, int offset, int count)
|
||||
{
|
||||
new ArraySegment<byte>(data, offset, count); // check arguments
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var table = OptimizedCRC.Table;
|
||||
|
||||
uint crc = UnsignedValue;
|
||||
|
||||
for (; (offset & 7) != 0 && count != 0; count--)
|
||||
{
|
||||
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
|
||||
}
|
||||
|
||||
if (count >= 8)
|
||||
{
|
||||
/*
|
||||
* Idea from 7-zip project sources (http://7-zip.org/sdk.html)
|
||||
*/
|
||||
|
||||
int end = (count - 8) & ~7;
|
||||
count -= end;
|
||||
end += offset;
|
||||
|
||||
while (offset != end)
|
||||
{
|
||||
crc ^= (uint)(data[offset] + (data[offset + 1] << 8) + (data[offset + 2] << 16) + (data[offset + 3] << 24));
|
||||
uint high = (uint)(data[offset + 4] + (data[offset + 5] << 8) + (data[offset + 6] << 16) + (data[offset + 7] << 24));
|
||||
offset += 8;
|
||||
|
||||
crc = table[(byte)crc + 0x700]
|
||||
^ table[(byte)(crc >>= 8) + 0x600]
|
||||
^ table[(byte)(crc >>= 8) + 0x500]
|
||||
^ table[/*(byte)*/(crc >> 8) + 0x400]
|
||||
^ table[(byte)(high) + 0x300]
|
||||
^ table[(byte)(high >>= 8) + 0x200]
|
||||
^ table[(byte)(high >>= 8) + 0x100]
|
||||
^ table[/*(byte)*/(high >> 8) + 0x000];
|
||||
}
|
||||
}
|
||||
|
||||
while (count-- != 0)
|
||||
{
|
||||
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
|
||||
}
|
||||
|
||||
UnsignedValue = crc;
|
||||
}
|
||||
|
||||
static public int Compute(byte[] data, int offset, int count)
|
||||
{
|
||||
var crc = new OptimizedCRC();
|
||||
crc.Update(data, offset, count);
|
||||
return crc.Value;
|
||||
}
|
||||
|
||||
static public int Compute(byte[] data)
|
||||
{
|
||||
return Compute(data, 0, data.Length);
|
||||
}
|
||||
|
||||
static public int Compute(ArraySegment<byte> block)
|
||||
{
|
||||
return Compute(block.Array, block.Offset, block.Count);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
UnsignedValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,11 @@ namespace MPF.Core.Hashing
|
||||
private readonly AutoResetEvent _outEvent;
|
||||
private readonly Thread _tWorker;
|
||||
|
||||
#if NET48
|
||||
private byte[] _buffer;
|
||||
#else
|
||||
private byte[]? _buffer;
|
||||
#endif
|
||||
private int _size;
|
||||
private readonly Stream _ds;
|
||||
private bool _finished;
|
||||
@@ -48,7 +52,8 @@ namespace MPF.Core.Hashing
|
||||
}
|
||||
try
|
||||
{
|
||||
SizeRead = _ds.Read(_buffer, 0, _size);
|
||||
if (_buffer != null)
|
||||
SizeRead = _ds.Read(_buffer, 0, _size);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
2869
MPF.Core/InfoTool.cs
Normal file
2869
MPF.Core/InfoTool.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,64 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2022</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.4</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>2.7.3</VersionPrefix>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<COMReference Include="IMAPI2">
|
||||
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="IMAPI2FS">
|
||||
<Guid>{2C941FD0-975B-59BE-A960-9A2A262853A5}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="MPF.Test" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<ProjectReference Include="..\Aaru\CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
|
||||
<ProjectReference Include="..\Aaru\Aaru.CommonTypes\Aaru.CommonTypes.csproj" />
|
||||
<ProjectReference Include="..\Aaru\Aaru.Core\Aaru.Core.csproj" />
|
||||
<ProjectReference Include="..\Aaru\Aaru.Devices\Aaru.Devices.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.1" />
|
||||
<PackageReference Include="System.Management" Version="6.0.0" />
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.9.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0-preview.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="psxt001z" Version="0.21.0-beta1" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.1.5" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.1.7" />
|
||||
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.Hashing" Version="7.0.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF.Modules.Aaru
|
||||
namespace MPF.Core.Modules.Aaru
|
||||
{
|
||||
/// <summary>
|
||||
/// Top-level commands for Aaru
|
||||
@@ -1,6 +1,6 @@
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.Aaru
|
||||
namespace MPF.Core.Modules.Aaru
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
File diff suppressed because it is too large
Load Diff
7391
MPF.Core/Modules/Aaru/cicm.cs
Normal file
7391
MPF.Core/Modules/Aaru/cicm.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,16 +2,13 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Hashing;
|
||||
using MPF.Core.Utilities;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules
|
||||
namespace MPF.Core.Modules
|
||||
{
|
||||
public abstract class BaseParameters
|
||||
{
|
||||
@@ -21,7 +18,11 @@ namespace MPF.Modules
|
||||
/// Geneeic way of reporting a message
|
||||
/// </summary>
|
||||
/// <param name="message">String value to report</param>
|
||||
#if NET48
|
||||
public EventHandler<string> ReportStatus;
|
||||
#else
|
||||
public EventHandler<string>? ReportStatus;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -30,12 +31,20 @@ namespace MPF.Modules
|
||||
/// <summary>
|
||||
/// Base command to run
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string BaseCommand { get; set; }
|
||||
#else
|
||||
public string? BaseCommand { get; set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Set of flags to pass to the executable
|
||||
/// </summary>
|
||||
#if NET48
|
||||
protected Dictionary<string, bool?> flags = new Dictionary<string, bool?>();
|
||||
#else
|
||||
protected Dictionary<string, bool?> flags = new();
|
||||
#endif
|
||||
protected internal IEnumerable<string> Keys => flags.Keys;
|
||||
|
||||
/// <summary>
|
||||
@@ -59,7 +68,11 @@ namespace MPF.Modules
|
||||
/// <summary>
|
||||
/// Process to track external program
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private Process process;
|
||||
#else
|
||||
private Process? process;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -68,18 +81,30 @@ namespace MPF.Modules
|
||||
/// <summary>
|
||||
/// Command to flag support mappings
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Dictionary<string, List<string>> CommandSupport => GetCommandSupport();
|
||||
#else
|
||||
public Dictionary<string, List<string>>? CommandSupport => GetCommandSupport();
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Input path for operations
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public virtual string InputPath => null;
|
||||
#else
|
||||
public virtual string? InputPath => null;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Output path for operations
|
||||
/// </summary>
|
||||
/// <returns>String representing the path, null on error</returns>
|
||||
#if NET48
|
||||
public virtual string OutputPath => null;
|
||||
#else
|
||||
public virtual string? OutputPath => null;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get the processing speed from the implementation
|
||||
@@ -93,7 +118,11 @@ namespace MPF.Modules
|
||||
/// <summary>
|
||||
/// Path to the executable
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string ExecutablePath { get; set; }
|
||||
#else
|
||||
public string? ExecutablePath { get; set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Program that this set of parameters represents
|
||||
@@ -116,7 +145,11 @@ namespace MPF.Modules
|
||||
/// Populate a Parameters object from a param string
|
||||
/// </summary>
|
||||
/// <param name="parameters">String possibly representing a set of parameters</param>
|
||||
#if NET48
|
||||
public BaseParameters(string parameters)
|
||||
#else
|
||||
public BaseParameters(string? parameters)
|
||||
#endif
|
||||
{
|
||||
// If any parameters are not valid, wipe out everything
|
||||
if (!ValidateAndSetParameters(parameters))
|
||||
@@ -128,15 +161,19 @@ namespace MPF.Modules
|
||||
/// </summary>
|
||||
/// <param name="system">RedumpSystem value to use</param>
|
||||
/// <param name="type">MediaType value to use</param>
|
||||
/// <param name="driveLetter">Drive letter to use</param>
|
||||
/// <param name="drivePath">Drive path to use</param>
|
||||
/// <param name="filename">Filename to use</param>
|
||||
/// <param name="driveSpeed">Drive speed to use</param>
|
||||
/// <param name="options">Options object containing all settings that may be used for setting parameters</param>
|
||||
public BaseParameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
#if NET48
|
||||
public BaseParameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
public BaseParameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
{
|
||||
this.System = system;
|
||||
this.Type = type;
|
||||
SetDefaultParameters(driveLetter, filename, driveSpeed, options);
|
||||
SetDefaultParameters(drivePath, filename, driveSpeed, options);
|
||||
}
|
||||
|
||||
#region Abstract Methods
|
||||
@@ -157,7 +194,11 @@ namespace MPF.Modules
|
||||
/// <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>
|
||||
#if NET48
|
||||
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, Options options, string basePath, Drive drive, bool includeArtifacts);
|
||||
#else
|
||||
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, Options options, string basePath, Drive? drive, bool includeArtifacts);
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -167,27 +208,54 @@ namespace MPF.Modules
|
||||
/// Get all commands mapped to the supported flags
|
||||
/// </summary>
|
||||
/// <returns>Mappings from command to supported flags</returns>
|
||||
#if NET48
|
||||
public virtual Dictionary<string, List<string>> GetCommandSupport() => null;
|
||||
#else
|
||||
public virtual Dictionary<string, List<string>>? GetCommandSupport() => null;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Blindly generate a parameter string based on the inputs
|
||||
/// </summary>
|
||||
/// <returns>Parameter string for invocation, null on error</returns>
|
||||
#if NET48
|
||||
public virtual string GenerateParameters() => null;
|
||||
#else
|
||||
public virtual string? GenerateParameters() => null;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get the default extension for a given media type
|
||||
/// </summary>
|
||||
/// <param name="mediaType">MediaType value to check</param>
|
||||
/// <returns>String representing the media type, null on error</returns>
|
||||
#if NET48
|
||||
public virtual string GetDefaultExtension(MediaType? mediaType) => null;
|
||||
#else
|
||||
public virtual string? GetDefaultExtension(MediaType? mediaType) => null;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all deleteable files generated
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <returns>List of all deleteable file paths, empty otherwise</returns>
|
||||
#if NET48
|
||||
public virtual List<string> GetDeleteableFilePaths(string basePath) => new List<string>();
|
||||
#else
|
||||
public virtual List<string> GetDeleteableFilePaths(string basePath) => new();
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all log files generated
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <returns>List of all log file paths, empty otherwise</returns>
|
||||
#if NET48
|
||||
public virtual List<string> GetLogFilePaths(string basePath) => new List<string>();
|
||||
#else
|
||||
public virtual List<string> GetLogFilePaths(string basePath) => new();
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get the MediaType from the current set of parameters
|
||||
@@ -231,18 +299,26 @@ namespace MPF.Modules
|
||||
/// <summary>
|
||||
/// Set default parameters for a given system and media type
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use</param>
|
||||
/// <param name="drivePath">Drive path to use</param>
|
||||
/// <param name="filename">Filename to use</param>
|
||||
/// <param name="driveSpeed">Drive speed to use</param>
|
||||
/// <param name="options">Options object containing all settings that may be used for setting parameters</param>
|
||||
protected virtual void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options) { }
|
||||
#if NET48
|
||||
protected virtual void SetDefaultParameters(string drivePath, string filename, int? driveSpeed, Options options) { }
|
||||
#else
|
||||
protected virtual void SetDefaultParameters(string? drivePath, string filename, int? driveSpeed, Options options) { }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Scan a possible parameter string and populate whatever possible
|
||||
/// </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;
|
||||
#if NET48
|
||||
protected virtual bool ValidateAndSetParameters(string parameters) => !string.IsNullOrWhiteSpace(parameters);
|
||||
#else
|
||||
protected virtual bool ValidateAndSetParameters(string? parameters) => !string.IsNullOrWhiteSpace(parameters);
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -274,8 +350,8 @@ namespace MPF.Modules
|
||||
// Start processing tasks, if necessary
|
||||
if (!separateWindow)
|
||||
{
|
||||
Logging.OutputToLog(process.StandardOutput, this, ReportStatus);
|
||||
Logging.OutputToLog(process.StandardError, this, ReportStatus);
|
||||
_ = Logging.OutputToLog(process.StandardOutput, this, ReportStatus);
|
||||
_ = Logging.OutputToLog(process.StandardError, this, ReportStatus);
|
||||
}
|
||||
|
||||
process.WaitForExit();
|
||||
@@ -316,7 +392,11 @@ namespace MPF.Modules
|
||||
/// </summary>
|
||||
/// <param name="content">String content to encode</param>
|
||||
/// <returns>Base64-encoded contents, if possible</returns>
|
||||
#if NET48
|
||||
protected static string GetBase64(string content)
|
||||
#else
|
||||
protected static string? GetBase64(string? content)
|
||||
#endif
|
||||
{
|
||||
if (string.IsNullOrEmpty(content))
|
||||
return null;
|
||||
@@ -331,7 +411,11 @@ namespace MPF.Modules
|
||||
/// <param name="filename">file location</param>
|
||||
/// <param name="binary">True if should read as binary, false otherwise (default)</param>
|
||||
/// <returns>Full text of the file, null on error</returns>
|
||||
#if NET48
|
||||
protected static string GetFullFile(string filename, bool binary = false)
|
||||
#else
|
||||
protected static string? GetFullFile(string filename, bool binary = false)
|
||||
#endif
|
||||
{
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(filename))
|
||||
@@ -344,7 +428,7 @@ namespace MPF.Modules
|
||||
return BitConverter.ToString(bytes).Replace("-", string.Empty);
|
||||
}
|
||||
|
||||
return string.Join("\n", File.ReadAllLines(filename));
|
||||
return File.ReadAllText(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -461,7 +545,11 @@ namespace MPF.Modules
|
||||
/// <param name="longFlagString">Long flag string, if available</param>
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
|
||||
#if NET48
|
||||
protected bool ProcessFlagParameter(List<string> parts, string shortFlagString, string longFlagString, ref int i)
|
||||
#else
|
||||
protected bool ProcessFlagParameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i)
|
||||
#endif
|
||||
{
|
||||
if (parts == null)
|
||||
return false;
|
||||
@@ -497,7 +585,11 @@ namespace MPF.Modules
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>True if the parameter was processed successfully or skipped, false otherwise</returns>
|
||||
#if NET48
|
||||
protected bool ProcessBooleanParameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#else
|
||||
protected bool ProcessBooleanParameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#endif
|
||||
{
|
||||
if (parts == null)
|
||||
return false;
|
||||
@@ -572,7 +664,11 @@ namespace MPF.Modules
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>SByte value if success, SByte.MinValue if skipped, null on error/returns>
|
||||
#if NET48
|
||||
protected sbyte? ProcessInt8Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#else
|
||||
protected sbyte? ProcessInt8Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#endif
|
||||
{
|
||||
if (parts == null)
|
||||
return null;
|
||||
@@ -622,6 +718,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (sbyte)(sbyte.Parse(value) * factor);
|
||||
}
|
||||
@@ -649,7 +746,11 @@ namespace MPF.Modules
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>Int16 value if success, Int16.MinValue if skipped, null on error/returns>
|
||||
#if NET48
|
||||
protected short? ProcessInt16Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#else
|
||||
protected short? ProcessInt16Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#endif
|
||||
{
|
||||
if (parts == null)
|
||||
return null;
|
||||
@@ -698,6 +799,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (short)(short.Parse(value) * factor);
|
||||
}
|
||||
@@ -725,7 +827,11 @@ namespace MPF.Modules
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>Int32 value if success, Int32.MinValue if skipped, null on error/returns>
|
||||
#if NET48
|
||||
protected int? ProcessInt32Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#else
|
||||
protected int? ProcessInt32Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#endif
|
||||
{
|
||||
if (parts == null)
|
||||
return null;
|
||||
@@ -774,6 +880,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (int)(int.Parse(value) * factor);
|
||||
}
|
||||
@@ -801,7 +908,11 @@ namespace MPF.Modules
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>Int64 value if success, Int64.MinValue if skipped, null on error/returns>
|
||||
#if NET48
|
||||
protected long? ProcessInt64Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#else
|
||||
protected long? ProcessInt64Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#endif
|
||||
{
|
||||
if (parts == null)
|
||||
return null;
|
||||
@@ -850,6 +961,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return long.Parse(value) * factor;
|
||||
}
|
||||
@@ -865,8 +977,12 @@ namespace MPF.Modules
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
|
||||
#if NET48
|
||||
protected string ProcessStringParameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
|
||||
=> ProcessStringParameter(parts, null, flagString, ref i, missingAllowed);
|
||||
#else
|
||||
protected string? ProcessStringParameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
|
||||
#endif
|
||||
=> ProcessStringParameter(parts, null, flagString, ref i, missingAllowed);
|
||||
|
||||
/// <summary>
|
||||
/// Process a string parameter
|
||||
@@ -877,7 +993,11 @@ namespace MPF.Modules
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>String value if possible, string.Empty on missing, null on error</returns>
|
||||
#if NET48
|
||||
protected string ProcessStringParameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#else
|
||||
protected string? ProcessStringParameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#endif
|
||||
{
|
||||
if (parts == null)
|
||||
return null;
|
||||
@@ -952,7 +1072,11 @@ namespace MPF.Modules
|
||||
/// <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>
|
||||
#if NET48
|
||||
protected byte? ProcessUInt8Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#else
|
||||
protected byte? ProcessUInt8Parameter(List<string> parts, string? shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
#endif
|
||||
{
|
||||
if (parts == null)
|
||||
return null;
|
||||
@@ -1002,6 +1126,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (byte)(byte.Parse(value) * factor);
|
||||
}
|
||||
@@ -1072,475 +1197,5 @@ namespace MPF.Modules
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Common Information Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input file path
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <returns>True if hashing was successful, false otherwise</returns>
|
||||
protected static bool GetFileHashes(string filename, out long size, out string crc32, out string md5, out string sha1)
|
||||
{
|
||||
// Set all initial values
|
||||
size = -1; crc32 = null; md5 = null; sha1 = null;
|
||||
|
||||
// If the file doesn't exist, we can't do anything
|
||||
if (!File.Exists(filename))
|
||||
return false;
|
||||
|
||||
// Set the file size
|
||||
size = new FileInfo(filename).Length;
|
||||
|
||||
// Open the input file
|
||||
var input = File.OpenRead(filename);
|
||||
|
||||
try
|
||||
{
|
||||
// Get a list of hashers to run over the buffer
|
||||
List<Hasher> hashers = new List<Hasher>
|
||||
{
|
||||
new Hasher(Hash.CRC),
|
||||
new Hasher(Hash.MD5),
|
||||
new Hasher(Hash.SHA1),
|
||||
new Hasher(Hash.SHA256),
|
||||
new Hasher(Hash.SHA384),
|
||||
new Hasher(Hash.SHA512),
|
||||
};
|
||||
|
||||
// Initialize the hashing helpers
|
||||
var loadBuffer = new ThreadLoadBuffer(input);
|
||||
int buffersize = 3 * 1024 * 1024;
|
||||
byte[] buffer0 = new byte[buffersize];
|
||||
byte[] buffer1 = new byte[buffersize];
|
||||
|
||||
/*
|
||||
Please note that some of the following code is adapted from
|
||||
RomVault. This is a modified version of how RomVault does
|
||||
threaded hashing. As such, some of the terminology and code
|
||||
is the same, though variable names and comments may have
|
||||
been tweaked to better fit this code base.
|
||||
*/
|
||||
|
||||
// Pre load the first buffer
|
||||
long refsize = size;
|
||||
int next = refsize > buffersize ? buffersize : (int)refsize;
|
||||
input.Read(buffer0, 0, next);
|
||||
int current = next;
|
||||
refsize -= next;
|
||||
bool bufferSelect = true;
|
||||
|
||||
while (current > 0)
|
||||
{
|
||||
// Trigger the buffer load on the second buffer
|
||||
next = refsize > buffersize ? buffersize : (int)refsize;
|
||||
if (next > 0)
|
||||
loadBuffer.Trigger(bufferSelect ? buffer1 : buffer0, next);
|
||||
|
||||
byte[] buffer = bufferSelect ? buffer0 : buffer1;
|
||||
|
||||
// Run hashes in parallel
|
||||
Parallel.ForEach(hashers, h => h.Process(buffer, current));
|
||||
|
||||
// Wait for the load buffer worker, if needed
|
||||
if (next > 0)
|
||||
loadBuffer.Wait();
|
||||
|
||||
// Setup for the next hashing step
|
||||
current = next;
|
||||
refsize -= next;
|
||||
bufferSelect = !bufferSelect;
|
||||
}
|
||||
|
||||
// Finalize all hashing helpers
|
||||
loadBuffer.Finish();
|
||||
Parallel.ForEach(hashers, h => h.Terminate());
|
||||
|
||||
// Get the results
|
||||
crc32 = hashers.First(h => h.HashType == Hash.CRC).GetHashString();
|
||||
md5 = hashers.First(h => h.HashType == Hash.MD5).GetHashString();
|
||||
sha1 = hashers.First(h => h.HashType == Hash.SHA1).GetHashString();
|
||||
//sha256 = hashers.First(h => h.HashType == Hash.SHA256).GetHashString();
|
||||
//sha384 = hashers.First(h => h.HashType == Hash.SHA384).GetHashString();
|
||||
//sha512 = hashers.First(h => h.HashType == Hash.SHA512).GetHashString();
|
||||
|
||||
// Dispose of the hashers
|
||||
loadBuffer.Dispose();
|
||||
hashers.ForEach(h => h.Dispose());
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
input.Dispose();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the split values for ISO-based media
|
||||
/// </summary>
|
||||
/// <param name="hashData">String representing the combined hash data</param>
|
||||
/// <returns>True if extraction was successful, false otherwise</returns>
|
||||
protected static bool GetISOHashValues(string hashData, out long size, out string crc32, out string md5, out string sha1)
|
||||
{
|
||||
size = -1; crc32 = null; md5 = null; sha1 = null;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(hashData))
|
||||
return false;
|
||||
|
||||
Regex hashreg = new Regex(@"<rom name="".*?"" size=""(.*?)"" crc=""(.*?)"" md5=""(.*?)"" sha1=""(.*?)""");
|
||||
Match m = hashreg.Match(hashData);
|
||||
if (m.Success)
|
||||
{
|
||||
Int64.TryParse(m.Groups[1].Value, out size);
|
||||
crc32 = m.Groups[2].Value;
|
||||
md5 = m.Groups[3].Value;
|
||||
sha1 = m.Groups[4].Value;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the EXE date from a PlayStation disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <param name="serial">Internal disc serial, if possible</param>
|
||||
/// <param name="region">Output region, if possible</param>
|
||||
/// <param name="date">Output EXE date in "yyyy-mm-dd" format if possible, null on error</param>
|
||||
/// <returns></returns>
|
||||
protected static bool GetPlayStationExecutableInfo(char? driveLetter, out string serial, out Region? region, out string date)
|
||||
{
|
||||
serial = null; region = null; date = null;
|
||||
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return false;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return false;
|
||||
|
||||
// Get the two paths that we will need to check
|
||||
string psxExePath = Path.Combine(drivePath, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
|
||||
|
||||
// Try both of the common paths that contain information
|
||||
string exeName = null;
|
||||
|
||||
// Read the CNF file as an INI file
|
||||
var systemCnf = new IniFile(systemCnfPath);
|
||||
string bootValue = string.Empty;
|
||||
|
||||
// PlayStation uses "BOOT" as the key
|
||||
if (systemCnf.ContainsKey("BOOT"))
|
||||
bootValue = systemCnf["BOOT"];
|
||||
|
||||
// PlayStation 2 uses "BOOT2" as the key
|
||||
if (systemCnf.ContainsKey("BOOT2"))
|
||||
bootValue = systemCnf["BOOT2"];
|
||||
|
||||
// If we had any boot value, parse it and get the executable name
|
||||
if (!string.IsNullOrEmpty(bootValue))
|
||||
{
|
||||
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)");
|
||||
if (match.Groups.Count > 1)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// If the SYSTEM.CNF value can't be found, try PSX.EXE
|
||||
if (string.IsNullOrWhiteSpace(exeName) && File.Exists(psxExePath))
|
||||
exeName = "PSX.EXE";
|
||||
|
||||
// If neither can be found, we return false
|
||||
if (string.IsNullOrWhiteSpace(exeName))
|
||||
return false;
|
||||
|
||||
// Get the region, if possible
|
||||
region = GetPlayStationRegion(exeName);
|
||||
|
||||
// Now that we have the EXE name, try to get the fileinfo for it
|
||||
string exePath = Path.Combine(drivePath, exeName);
|
||||
if (!File.Exists(exePath))
|
||||
return false;
|
||||
|
||||
// Fix the Y2K timestamp issue
|
||||
FileInfo fi = new FileInfo(exePath);
|
||||
DateTime dt = new DateTime(fi.LastWriteTimeUtc.Year >= 1900 && fi.LastWriteTimeUtc.Year < 1920 ? 2000 + fi.LastWriteTimeUtc.Year % 100 : fi.LastWriteTimeUtc.Year,
|
||||
fi.LastWriteTimeUtc.Month, fi.LastWriteTimeUtc.Day);
|
||||
date = dt.ToString("yyyy-MM-dd");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 2 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 GetPlayStation2Version(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;
|
||||
|
||||
// Get the SYSTEM.CNF path to check
|
||||
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
|
||||
|
||||
// Try to parse the SYSTEM.CNF file
|
||||
var systemCnf = new IniFile(systemCnfPath);
|
||||
if (systemCnf.ContainsKey("VER"))
|
||||
return systemCnf["VER"];
|
||||
|
||||
// If "VER" can't be found, we can't do much
|
||||
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>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
protected static string GetPlayStation4Version(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 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 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>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
protected static string GetPlayStation5Version(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 version in the unencrypted JSON
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(File.OpenRead(paramJsonPath)))
|
||||
{
|
||||
br.BaseStream.Seek(0x89E, SeekOrigin.Begin);
|
||||
return new string(br.ReadChars(5));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Category Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Determine the category based on the UMDImageCreator string
|
||||
/// </summary>
|
||||
/// <param name="region">String representing the category</param>
|
||||
/// <returns>Category, if possible</returns>
|
||||
protected static DiscCategory? GetUMDCategory(string category)
|
||||
{
|
||||
switch (category)
|
||||
{
|
||||
case "GAME": return DiscCategory.Games;
|
||||
case "VIDEO": return DiscCategory.Video;
|
||||
case "AUDIO": return DiscCategory.Audio;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Determine the region based on the PlayStation serial code
|
||||
/// </summary>
|
||||
/// <param name="serial">PlayStation serial code</param>
|
||||
/// <returns>Region mapped from name, if possible</returns>
|
||||
protected static Region? GetPlayStationRegion(string serial)
|
||||
{
|
||||
// Standardized "S" serials
|
||||
if (serial.StartsWith("S"))
|
||||
{
|
||||
// string publisher = serial[0] + serial[1];
|
||||
// char secondRegion = serial[3];
|
||||
switch (serial[2])
|
||||
{
|
||||
case 'A': return Region.Asia;
|
||||
case 'C': return Region.China;
|
||||
case 'E': return Region.Europe;
|
||||
case 'J': return Region.JapanKorea;
|
||||
case 'K': return Region.SouthKorea;
|
||||
case 'P': return Region.Japan;
|
||||
case 'U': return Region.UnitedStatesOfAmerica;
|
||||
}
|
||||
}
|
||||
|
||||
// Japan-only special serial
|
||||
else if (serial.StartsWith("PAPX"))
|
||||
return Region.Japan;
|
||||
|
||||
// Region appears entirely random
|
||||
else if (serial.StartsWith("PABX"))
|
||||
return null;
|
||||
|
||||
// Japan-only special serial
|
||||
else if (serial.StartsWith("PCBX"))
|
||||
return Region.Japan;
|
||||
|
||||
// Single disc known, Japan
|
||||
else if (serial.StartsWith("PDBX"))
|
||||
return Region.Japan;
|
||||
|
||||
// Single disc known, Europe
|
||||
else if (serial.StartsWith("PEBX"))
|
||||
return Region.Europe;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,11 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.CleanRip
|
||||
#pragma warning disable IDE0051 // Remove unused private members
|
||||
|
||||
namespace MPF.Core.Modules.CleanRip
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic set of CleanRip parameters
|
||||
@@ -21,11 +23,19 @@ namespace MPF.Modules.CleanRip
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public Parameters(string parameters) : base(parameters) { }
|
||||
#else
|
||||
public Parameters(string? parameters) : base(parameters) { }
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, driveLetter, filename, driveSpeed, options)
|
||||
#if NET48
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
: base(system, type, drivePath, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -34,7 +44,7 @@ namespace MPF.Modules.CleanRip
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
List<string> missingFiles = new List<string>();
|
||||
var missingFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.DVD: // Only added here to help users; not strictly correct
|
||||
@@ -59,16 +69,33 @@ namespace MPF.Modules.CleanRip
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
#else
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
|
||||
#endif
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
|
||||
// TODO: Determine if there's a CleanRip version anywhere
|
||||
#if NET48
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
info.TracksAndWriteOffsets.ClrMameProData = GetCleanripDatfile(basePath + ".iso", basePath + "-dumpinfo.txt");
|
||||
|
||||
#else
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
#endif
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
var datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
|
||||
|
||||
// Get the individual hash data, as per internal
|
||||
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out string crc32, out string md5, out string sha1))
|
||||
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
#if NET48
|
||||
info.SizeAndChecksums.Size = size;
|
||||
#else
|
||||
info.SizeAndChecksums!.Size = size;
|
||||
#endif
|
||||
info.SizeAndChecksums.CRC32 = crc32;
|
||||
info.SizeAndChecksums.MD5 = md5;
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
@@ -76,7 +103,7 @@ namespace MPF.Modules.CleanRip
|
||||
// Dual-layer discs have the same size and layerbreak
|
||||
if (size == 8511160320)
|
||||
info.SizeAndChecksums.Layerbreak = 2084960;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (this.Type)
|
||||
@@ -85,13 +112,23 @@ namespace MPF.Modules.CleanRip
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
#if NET48
|
||||
info.Extras.BCA = GetBCA(basePath + ".bca");
|
||||
#else
|
||||
info.Extras!.BCA = GetBCA(basePath + ".bca");
|
||||
#endif
|
||||
|
||||
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out string gcVersion, out string gcName))
|
||||
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out var gcVersion, out var gcName))
|
||||
{
|
||||
#if NET48
|
||||
info.CommonDiscInfo.Region = gcRegion ?? info.CommonDiscInfo.Region;
|
||||
info.VersionAndEditions.Version = gcVersion ?? info.VersionAndEditions.Version;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = gcName ?? string.Empty;
|
||||
#else
|
||||
info.CommonDiscInfo!.Region = gcRegion ?? info.CommonDiscInfo.Region;
|
||||
info.VersionAndEditions!.Version = gcVersion ?? info.VersionAndEditions.Version;
|
||||
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalName] = gcName ?? string.Empty;
|
||||
#endif
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -100,17 +137,23 @@ namespace MPF.Modules.CleanRip
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
#if NET48
|
||||
if (info.Artifacts == null) info.Artifacts = new Dictionary<string, string>();
|
||||
#else
|
||||
info.Artifacts ??= new Dictionary<string, string>();
|
||||
#endif
|
||||
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true));
|
||||
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty;
|
||||
if (File.Exists(basePath + "-dumpinfo.txt"))
|
||||
info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt"));
|
||||
info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt")) ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
{
|
||||
List<string> logFiles = new List<string>();
|
||||
var logFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.DVD: // Only added here to help users; not strictly correct
|
||||
@@ -131,13 +174,91 @@ namespace MPF.Modules.CleanRip
|
||||
|
||||
#region Information Extraction Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted datfile from the cleanrip output, if possible
|
||||
/// </summary>
|
||||
/// <param name="iso">Path to ISO file</param>
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <returns></returns>
|
||||
#if NET48
|
||||
private static Datafile GenerateCleanripDatafile(string iso, string dumpinfo)
|
||||
#else
|
||||
private static Datafile? GenerateCleanripDatafile(string iso, string dumpinfo)
|
||||
#endif
|
||||
{
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(dumpinfo))
|
||||
return null;
|
||||
|
||||
using (var sr = File.OpenText(dumpinfo))
|
||||
{
|
||||
long size = new FileInfo(iso).Length;
|
||||
string crc = string.Empty;
|
||||
string md5 = string.Empty;
|
||||
string sha1 = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
// Make sure this file is a dumpinfo
|
||||
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
|
||||
return null;
|
||||
|
||||
// Read all lines and gather dat information
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
#if NET48
|
||||
else if (line.StartsWith("CRC32"))
|
||||
crc = line.Substring(7).ToLowerInvariant();
|
||||
else if (line.StartsWith("MD5"))
|
||||
md5 = line.Substring(5);
|
||||
else if (line.StartsWith("SHA-1"))
|
||||
sha1 = line.Substring(7);
|
||||
#else
|
||||
else if (line.StartsWith("CRC32"))
|
||||
crc = line[7..].ToLowerInvariant();
|
||||
else if (line.StartsWith("MD5"))
|
||||
md5 = line[5..];
|
||||
else if (line.StartsWith("SHA-1"))
|
||||
sha1 = line[7..];
|
||||
#endif
|
||||
}
|
||||
|
||||
return new Datafile
|
||||
{
|
||||
Games = new Game[]
|
||||
{
|
||||
new Game
|
||||
{
|
||||
Roms = new Rom[]
|
||||
{
|
||||
new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1 },
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the hex contents of the BCA file
|
||||
/// </summary>
|
||||
/// <param name="bcaPath">Path to the BCA file associated with the dump</param>
|
||||
/// <returns>BCA data as a hex string if possible, null on error</returns>
|
||||
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
|
||||
#if NET48
|
||||
private static string GetBCA(string bcaPath)
|
||||
#else
|
||||
private static string? GetBCA(string bcaPath)
|
||||
#endif
|
||||
{
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(bcaPath))
|
||||
@@ -145,7 +266,10 @@ namespace MPF.Modules.CleanRip
|
||||
|
||||
try
|
||||
{
|
||||
string hex = GetFullFile(bcaPath, true);
|
||||
var hex = GetFullFile(bcaPath, true);
|
||||
if (hex == null)
|
||||
return null;
|
||||
|
||||
return Regex.Replace(hex, ".{32}", "$0\n");
|
||||
}
|
||||
catch
|
||||
@@ -161,13 +285,17 @@ namespace MPF.Modules.CleanRip
|
||||
/// <param name="iso">Path to ISO file</param>
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <returns></returns>
|
||||
#if NET48
|
||||
private static string GetCleanripDatfile(string iso, string dumpinfo)
|
||||
#else
|
||||
private static string? GetCleanripDatfile(string iso, string dumpinfo)
|
||||
#endif
|
||||
{
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(dumpinfo))
|
||||
return null;
|
||||
|
||||
using (StreamReader sr = File.OpenText(dumpinfo))
|
||||
using (var sr = File.OpenText(dumpinfo))
|
||||
{
|
||||
long size = new FileInfo(iso).Length;
|
||||
string crc = string.Empty;
|
||||
@@ -177,19 +305,30 @@ namespace MPF.Modules.CleanRip
|
||||
try
|
||||
{
|
||||
// Make sure this file is a dumpinfo
|
||||
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
|
||||
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
|
||||
return null;
|
||||
|
||||
// Read all lines and gather dat information
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string line = sr.ReadLine().Trim();
|
||||
if (line.StartsWith("CRC32"))
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
#if NET48
|
||||
else if (line.StartsWith("CRC32"))
|
||||
crc = line.Substring(7).ToLowerInvariant();
|
||||
else if (line.StartsWith("MD5"))
|
||||
md5 = line.Substring(5);
|
||||
else if (line.StartsWith("SHA-1"))
|
||||
sha1 = line.Substring(7);
|
||||
#else
|
||||
else if (line.StartsWith("CRC32"))
|
||||
crc = line[7..].ToLowerInvariant();
|
||||
else if (line.StartsWith("MD5"))
|
||||
md5 = line[5..];
|
||||
else if (line.StartsWith("SHA-1"))
|
||||
sha1 = line[7..];
|
||||
#endif
|
||||
}
|
||||
|
||||
return $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
|
||||
@@ -210,7 +349,11 @@ namespace MPF.Modules.CleanRip
|
||||
/// <param name="version">Output internal version of the game</param>
|
||||
/// <param name="name">Output internal name of the game</param>
|
||||
/// <returns></returns>
|
||||
#if NET48
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string version, out string name)
|
||||
#else
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string? version, out string? name)
|
||||
#endif
|
||||
{
|
||||
region = null; version = null; name = null;
|
||||
|
||||
@@ -218,29 +361,45 @@ namespace MPF.Modules.CleanRip
|
||||
if (!File.Exists(dumpinfo))
|
||||
return false;
|
||||
|
||||
using (StreamReader sr = File.OpenText(dumpinfo))
|
||||
using (var sr = File.OpenText(dumpinfo))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Make sure this file is a dumpinfo
|
||||
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
|
||||
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
|
||||
return false;
|
||||
|
||||
// Read all lines and gather dat information
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string line = sr.ReadLine().Trim();
|
||||
if (line.StartsWith("Version"))
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (line.StartsWith("Version"))
|
||||
{
|
||||
#if NET48
|
||||
version = line.Substring("Version: ".Length);
|
||||
#else
|
||||
version = line["Version: ".Length..];
|
||||
#endif
|
||||
}
|
||||
else if (line.StartsWith("Internal Name"))
|
||||
{
|
||||
#if NET48
|
||||
name = line.Substring("Internal Name: ".Length);
|
||||
#else
|
||||
name = line["Internal Name: ".Length..];
|
||||
#endif
|
||||
}
|
||||
else if (line.StartsWith("Filename"))
|
||||
{
|
||||
#if NET48
|
||||
string serial = line.Substring("Filename: ".Length);
|
||||
#else
|
||||
string serial = line["Filename: ".Length..];
|
||||
#endif
|
||||
|
||||
// char gameType = serial[0];
|
||||
// string gameid = serial[1] + serial[2];
|
||||
145
MPF.Core/Modules/Datafile.cs
Normal file
145
MPF.Core/Modules/Datafile.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace MPF.Core.Modules
|
||||
{
|
||||
[XmlRoot("datafile")]
|
||||
public class Datafile
|
||||
{
|
||||
[XmlElement("header")]
|
||||
#if NET48
|
||||
public Header Header;
|
||||
#else
|
||||
public Header? Header;
|
||||
#endif
|
||||
|
||||
[XmlElement("game")]
|
||||
#if NET48
|
||||
public Game[] Games;
|
||||
#else
|
||||
public Game[]? Games;
|
||||
#endif
|
||||
}
|
||||
|
||||
public class Header
|
||||
{
|
||||
[XmlElement("name")]
|
||||
#if NET48
|
||||
public string Name;
|
||||
#else
|
||||
public string? Name;
|
||||
#endif
|
||||
|
||||
[XmlElement("description")]
|
||||
#if NET48
|
||||
public string Description;
|
||||
#else
|
||||
public string? Description;
|
||||
#endif
|
||||
|
||||
[XmlElement("version")]
|
||||
#if NET48
|
||||
public string Version;
|
||||
#else
|
||||
public string? Version;
|
||||
#endif
|
||||
|
||||
[XmlElement("date")]
|
||||
#if NET48
|
||||
public string Date;
|
||||
#else
|
||||
public string? Date;
|
||||
#endif
|
||||
|
||||
[XmlElement("author")]
|
||||
#if NET48
|
||||
public string Author;
|
||||
#else
|
||||
public string? Author;
|
||||
#endif
|
||||
|
||||
[XmlElement("homepage")]
|
||||
#if NET48
|
||||
public string Homepage;
|
||||
#else
|
||||
public string? Homepage;
|
||||
#endif
|
||||
|
||||
[XmlElement("url")]
|
||||
#if NET48
|
||||
public string Url;
|
||||
#else
|
||||
public string? Url;
|
||||
#endif
|
||||
}
|
||||
|
||||
public class Game
|
||||
{
|
||||
[XmlAttribute("name")]
|
||||
#if NET48
|
||||
public string Name;
|
||||
#else
|
||||
public string? Name;
|
||||
#endif
|
||||
|
||||
[XmlElement("category")]
|
||||
#if NET48
|
||||
public string Category;
|
||||
#else
|
||||
public string? Category;
|
||||
#endif
|
||||
|
||||
[XmlElement("description")]
|
||||
#if NET48
|
||||
public string Description;
|
||||
#else
|
||||
public string? Description;
|
||||
#endif
|
||||
|
||||
[XmlElement("rom")]
|
||||
#if NET48
|
||||
public Rom[] Roms;
|
||||
#else
|
||||
public Rom[]? Roms;
|
||||
#endif
|
||||
}
|
||||
|
||||
public class Rom
|
||||
{
|
||||
[XmlAttribute("name")]
|
||||
#if NET48
|
||||
public string Name;
|
||||
#else
|
||||
public string? Name;
|
||||
#endif
|
||||
|
||||
[XmlAttribute("size")]
|
||||
#if NET48
|
||||
public string Size;
|
||||
#else
|
||||
public string? Size;
|
||||
#endif
|
||||
|
||||
[XmlAttribute("crc")]
|
||||
#if NET48
|
||||
public string Crc;
|
||||
#else
|
||||
public string? Crc;
|
||||
#endif
|
||||
|
||||
[XmlAttribute("md5")]
|
||||
#if NET48
|
||||
public string Md5;
|
||||
#else
|
||||
public string? Md5;
|
||||
#endif
|
||||
|
||||
[XmlAttribute("sha1")]
|
||||
#if NET48
|
||||
public string Sha1;
|
||||
#else
|
||||
public string? Sha1;
|
||||
#endif
|
||||
|
||||
// TODO: Add extended hashes here
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF.Modules.DiscImageCreator
|
||||
namespace MPF.Core.Modules.DiscImageCreator
|
||||
{
|
||||
/// <summary>
|
||||
/// Top-level commands for DiscImageCreator
|
||||
@@ -26,6 +26,7 @@ namespace MPF.Modules.DiscImageCreator
|
||||
public const string Sub = "sub";
|
||||
public const string Swap = "swap";
|
||||
public const string Tape = "tape";
|
||||
public const string Version = "/v";
|
||||
public const string XBOX = "xbox";
|
||||
public const string XBOXSwap = "xboxswap";
|
||||
public const string XGD2Swap = "xgd2swap";
|
||||
@@ -44,13 +45,13 @@ namespace MPF.Modules.DiscImageCreator
|
||||
public const string C2Opcode = "/c2";
|
||||
public const string CopyrightManagementInformation = "/c";
|
||||
public const string D8Opcode = "/d8";
|
||||
public const string DatExpand = "/d";
|
||||
public const string DisableBeep = "/q";
|
||||
public const string DVDReread = "/rr";
|
||||
public const string ExtractMicroSoftCabFile = "/mscf";
|
||||
public const string Fix = "/fix";
|
||||
public const string ForceUnitAccess = "/f";
|
||||
public const string MultiSectorRead = "/mr";
|
||||
public const string MultiSession = "/ms";
|
||||
public const string NoFixSubP = "/np";
|
||||
public const string NoFixSubQ = "/nq";
|
||||
public const string NoFixSubQLibCrypt = "/nl";
|
||||
@@ -58,6 +59,7 @@ namespace MPF.Modules.DiscImageCreator
|
||||
public const string NoFixSubQSecuROM = "/ns";
|
||||
public const string NoSkipSS = "/nss";
|
||||
public const string PadSector = "/ps";
|
||||
public const string Range = "/ra";
|
||||
public const string Raw = "/raw";
|
||||
public const string Resume = "/re";
|
||||
public const string Reverse = "/r";
|
||||
@@ -68,7 +70,6 @@ namespace MPF.Modules.DiscImageCreator
|
||||
public const string SkipSector = "/sk";
|
||||
public const string SubchannelReadLevel = "/s";
|
||||
public const string UseAnchorVolumeDescriptorPointer = "/avdp";
|
||||
public const string VerifyAudio = "/vrfy";
|
||||
public const string VideoNow = "/vn";
|
||||
public const string VideoNowColor = "/vnc";
|
||||
public const string VideoNowXP = "/vnx";
|
||||
@@ -1,6 +1,6 @@
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.DiscImageCreator
|
||||
namespace MPF.Core.Modules.DiscImageCreator
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
@@ -48,7 +48,11 @@ namespace MPF.Modules.DiscImageCreator
|
||||
/// <param name="baseCommand">Command value to check</param>
|
||||
/// <returns>MediaType if possible, null on error</returns>
|
||||
/// <remarks>This takes the "safe" route by assuming the larger of any given format</remarks>
|
||||
#if NET48
|
||||
public static MediaType? ToMediaType(string baseCommand)
|
||||
#else
|
||||
public static MediaType? ToMediaType(string? baseCommand)
|
||||
#endif
|
||||
{
|
||||
switch (baseCommand)
|
||||
{
|
||||
@@ -86,7 +90,11 @@ namespace MPF.Modules.DiscImageCreator
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType value to check</param>
|
||||
/// <returns>Valid extension (with leading '.'), null on error</returns>
|
||||
#if NET48
|
||||
public static string Extension(MediaType? type)
|
||||
#else
|
||||
public static string? Extension(MediaType? type)
|
||||
#endif
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
namespace MPF.Modules.Redumper
|
||||
namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
/// <summary>
|
||||
/// Top-level commands for Redumper
|
||||
@@ -7,11 +7,17 @@ namespace MPF.Modules.Redumper
|
||||
{
|
||||
public const string NONE = "";
|
||||
public const string CD = "cd";
|
||||
public const string DVD = "dvd"; // Synonym for CD
|
||||
public const string BluRay = "bd"; // Synonym for CD
|
||||
public const string SACD = "sacd"; // Synonym for CD
|
||||
public const string Dump = "dump";
|
||||
public const string Info = "info";
|
||||
public const string Protection = "protection";
|
||||
public const string Refine = "refine";
|
||||
public const string Split = "split";
|
||||
public const string Verify = "verify";
|
||||
public const string DVDKey = "dvdkey";
|
||||
public const string DVDIsoKey = "dvdisokey";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -19,32 +25,50 @@ namespace MPF.Modules.Redumper
|
||||
/// </summary>
|
||||
public static class FlagStrings
|
||||
{
|
||||
public const string AudioSilenceThreshold = "--audio-silence-threshold";
|
||||
public const string CDiCorrectOffset = "--cdi-correct-offset";
|
||||
public const string CDiReadyNormalize = "--cdi-ready-normalize";
|
||||
public const string DescrambleNew = "--descramble-new";
|
||||
public const string Drive = "--drive";
|
||||
public const string ForceOffset = "--force-offset";
|
||||
public const string ForceQTOC = "--force-qtoc";
|
||||
public const string ForceSplit = "--force-split";
|
||||
public const string ForceTOC = "--force-toc";
|
||||
// General
|
||||
public const string HelpLong = "--help";
|
||||
public const string HelpShort = "-h";
|
||||
public const string ISO9660Trim = "--iso9660-trim";
|
||||
public const string ImageName = "--image-name";
|
||||
public const string ImagePath = "--image-path";
|
||||
public const string LeaveUnchanged = "--leave-unchanged";
|
||||
public const string Overwrite = "--overwrite";
|
||||
public const string RefineSubchannel = "--refine-subchannel";
|
||||
public const string Retries = "--retries";
|
||||
public const string RingSize = "--ring-size";
|
||||
public const string Skip = "--skip";
|
||||
public const string SkipFill = "--skip-fill";
|
||||
public const string SkipLeadIn = "--skip-leadin";
|
||||
public const string SkipSize = "--skip-size";
|
||||
public const string Speed = "--speed";
|
||||
public const string StopLBA = "--stop-lba";
|
||||
public const string Unsupported = "--unsupported";
|
||||
public const string Verbose = "--verbose";
|
||||
public const string Debug = "--debug";
|
||||
public const string Drive = "--drive";
|
||||
public const string Speed = "--speed";
|
||||
public const string Retries = "--retries";
|
||||
public const string ImagePath = "--image-path";
|
||||
public const string ImageName = "--image-name";
|
||||
public const string Overwrite = "--overwrite";
|
||||
|
||||
// Drive Configuration
|
||||
public const string DriveType = "--drive-type";
|
||||
public const string DriveReadOffset = "--drive-read-offset";
|
||||
public const string DriveC2Shift = "--drive-c2-shift";
|
||||
public const string DrivePregapStart = "--drive-pregap-start";
|
||||
public const string DriveReadMethod = "--drive-read-method";
|
||||
public const string DriveSectorOrder = "--drive-sector-order";
|
||||
|
||||
// Drive Specific
|
||||
public const string PlextorLeadinSkip = "--plextor-leadin-skip";
|
||||
public const string PlextorLeadinRetries = "--plextor-leadin-retries";
|
||||
public const string AsusSkipLeadout = "--asus-skip-leadout";
|
||||
|
||||
// Offset
|
||||
public const string ForceOffset = "--force-offset";
|
||||
public const string AudioSilenceThreshold = "--audio-silence-threshold";
|
||||
public const string CorrectOffsetShift = "--correct-offset-shift";
|
||||
public const string OffsetShiftRelocate = "--offset-shift-relocate";
|
||||
|
||||
// Split
|
||||
public const string ForceSplit = "--force-split";
|
||||
public const string LeaveUnchanged = "--leave-unchanged";
|
||||
public const string ForceQTOC = "--force-qtoc";
|
||||
public const string SkipFill = "--skip-fill";
|
||||
public const string ISO9660Trim = "--iso9660-trim";
|
||||
|
||||
// Miscellaneous
|
||||
public const string LBAStart = "--lba-start";
|
||||
public const string LBAEnd = "--lba-end";
|
||||
public const string RefineSubchannel = "--refine-subchannel";
|
||||
public const string Skip = "--skip";
|
||||
public const string DumpReadSize = "--dump-read-size";
|
||||
public const string OverreadLeadout = "--overread-leadout";
|
||||
}
|
||||
}
|
||||
36
MPF.Core/Modules/Redumper/Converters.cs
Normal file
36
MPF.Core/Modules/Redumper/Converters.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
#region Cross-enumeration conversions
|
||||
|
||||
/// <summary>
|
||||
/// Get the default extension for a given disc type
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType value to check</param>
|
||||
/// <returns>Valid extension (with leading '.'), null on error</returns>
|
||||
#if NET48
|
||||
public static string Extension(MediaType? type)
|
||||
#else
|
||||
public static string? Extension(MediaType? type)
|
||||
#endif
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
return ".bin";
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
return ".iso";
|
||||
case MediaType.NONE:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
2484
MPF.Core/Modules/Redumper/Parameters.cs
Normal file
2484
MPF.Core/Modules/Redumper/Parameters.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -4,10 +4,9 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.UmdImageCreator
|
||||
namespace MPF.Core.Modules.UmdImageCreator
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic set of UmdImageCreator parameters
|
||||
@@ -22,11 +21,19 @@ namespace MPF.Modules.UmdImageCreator
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public Parameters(string parameters) : base(parameters) { }
|
||||
#else
|
||||
public Parameters(string? parameters) : base(parameters) { }
|
||||
#endif
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, driveLetter, filename, driveSpeed, options)
|
||||
#if NET48
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string drivePath, string filename, int? driveSpeed, Options options)
|
||||
#else
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
#endif
|
||||
: base(system, type, drivePath, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -35,7 +42,7 @@ namespace MPF.Modules.UmdImageCreator
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
List<string> missingFiles = new List<string>();
|
||||
var missingFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.UMD:
|
||||
@@ -62,31 +69,58 @@ namespace MPF.Modules.UmdImageCreator
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
#else
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
|
||||
#endif
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
|
||||
// TODO: Determine if there's a UMDImageCreator version anywhere
|
||||
#if NET48
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
#else
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
#endif
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.UMD:
|
||||
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? "";
|
||||
#if NET48
|
||||
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
|
||||
#else
|
||||
info.Extras!.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
|
||||
#endif
|
||||
|
||||
if (GetFileHashes(basePath + ".iso", out long filesize, out string crc32, out string md5, out string sha1))
|
||||
if (InfoTool.GetFileHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
#if NET48
|
||||
info.SizeAndChecksums.Size = filesize;
|
||||
#else
|
||||
info.SizeAndChecksums!.Size = filesize;
|
||||
#endif
|
||||
info.SizeAndChecksums.CRC32 = crc32;
|
||||
info.SizeAndChecksums.MD5 = md5;
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
}
|
||||
|
||||
if (GetUMDAuxInfo(basePath + "_disc.txt", out string title, out DiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize))
|
||||
if (GetUMDAuxInfo(basePath + "_disc.txt", out var title, out DiscCategory? umdcat, out var umdversion, out var umdlayer, out long umdsize))
|
||||
{
|
||||
info.CommonDiscInfo.Title = title ?? "";
|
||||
#if NET48
|
||||
info.CommonDiscInfo.Title = title ?? string.Empty;
|
||||
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
|
||||
info.VersionAndEditions.Version = umdversion ?? "";
|
||||
info.VersionAndEditions.Version = umdversion ?? string.Empty;
|
||||
info.SizeAndChecksums.Size = umdsize;
|
||||
#else
|
||||
info.CommonDiscInfo!.Title = title ?? string.Empty;
|
||||
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
|
||||
info.VersionAndEditions!.Version = umdversion ?? string.Empty;
|
||||
info.SizeAndChecksums!.Size = umdsize;
|
||||
#endif
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(umdlayer))
|
||||
info.SizeAndChecksums.Layerbreak = Int64.Parse(umdlayer ?? "-1");
|
||||
@@ -98,26 +132,36 @@ namespace MPF.Modules.UmdImageCreator
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
#if NET48
|
||||
if (info.Artifacts == null) info.Artifacts = new Dictionary<string, string>();
|
||||
#else
|
||||
info.Artifacts ??= new Dictionary<string, string>();
|
||||
#endif
|
||||
|
||||
if (File.Exists(basePath + "_disc.txt"))
|
||||
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt"));
|
||||
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt")) ?? string.Empty;
|
||||
if (File.Exists(basePath + "_drive.txt"))
|
||||
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt")) ?? string.Empty;
|
||||
if (File.Exists(basePath + "_mainError.txt"))
|
||||
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt"));
|
||||
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt")) ?? string.Empty;
|
||||
if (File.Exists(basePath + "_mainInfo.txt"))
|
||||
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt"));
|
||||
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt")) ?? string.Empty;
|
||||
if (File.Exists(basePath + "_volDesc.txt"))
|
||||
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt"));
|
||||
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt")) ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
{
|
||||
List<string> logFiles = new List<string>();
|
||||
var logFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.UMD:
|
||||
if (File.Exists($"{basePath}_disc.txt"))
|
||||
logFiles.Add($"{basePath}_disc.txt");
|
||||
if (File.Exists($"{basePath}_drive.txt"))
|
||||
logFiles.Add($"{basePath}_drive.txt");
|
||||
if (File.Exists($"{basePath}_mainError.txt"))
|
||||
logFiles.Add($"{basePath}_mainError.txt");
|
||||
if (File.Exists($"{basePath}_mainInfo.txt"))
|
||||
@@ -140,21 +184,25 @@ namespace MPF.Modules.UmdImageCreator
|
||||
/// </summary>
|
||||
/// <param name="mainInfo">_mainInfo.txt file location</param>
|
||||
/// <returns>Newline-deliminated PVD if possible, null on error</returns>
|
||||
#if NET48
|
||||
private static string GetPVD(string mainInfo)
|
||||
#else
|
||||
private static string? GetPVD(string mainInfo)
|
||||
#endif
|
||||
{
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(mainInfo))
|
||||
return null;
|
||||
|
||||
using (StreamReader sr = File.OpenText(mainInfo))
|
||||
using (var sr = File.OpenText(mainInfo))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Make sure we're in the right sector
|
||||
while (!sr.ReadLine().StartsWith("========== LBA[000016, 0x0000010]: Main Channel ==========")) ;
|
||||
while (sr.ReadLine()?.StartsWith("========== LBA[000016, 0x0000010]: Main Channel ==========") == false) ;
|
||||
|
||||
// Fast forward to the PVD
|
||||
while (!sr.ReadLine().StartsWith("0310")) ;
|
||||
while (sr.ReadLine()?.StartsWith("0310") == false) ;
|
||||
|
||||
// Now that we're at the PVD, read each line in and concatenate
|
||||
string pvd = "";
|
||||
@@ -176,7 +224,11 @@ namespace MPF.Modules.UmdImageCreator
|
||||
/// </summary>
|
||||
/// <param name="disc">_disc.txt file location</param>
|
||||
/// <returns>True on successful extraction of info, false otherwise</returns>
|
||||
#if NET48
|
||||
private static bool GetUMDAuxInfo(string disc, out string title, out DiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize)
|
||||
#else
|
||||
private static bool GetUMDAuxInfo(string disc, out string? title, out DiscCategory? umdcat, out string? umdversion, out string? umdlayer, out long umdsize)
|
||||
#endif
|
||||
{
|
||||
title = null; umdcat = null; umdversion = null; umdlayer = null; umdsize = -1;
|
||||
|
||||
@@ -184,22 +236,28 @@ namespace MPF.Modules.UmdImageCreator
|
||||
if (!File.Exists(disc))
|
||||
return false;
|
||||
|
||||
using (StreamReader sr = File.OpenText(disc))
|
||||
using (var sr = File.OpenText(disc))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Loop through everything to get the first instance of each required field
|
||||
string line = string.Empty;
|
||||
var line = string.Empty;
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
line = sr.ReadLine().Trim();
|
||||
line = sr.ReadLine()?.Trim();
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
if (line.StartsWith("TITLE") && title == null)
|
||||
#if NET48
|
||||
title = line.Substring("TITLE: ".Length);
|
||||
#else
|
||||
title = line["TITLE: ".Length..];
|
||||
#endif
|
||||
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
|
||||
umdversion = line.Split(' ')[1];
|
||||
else if (line.StartsWith("pspUmdTypes"))
|
||||
umdcat = GetUMDCategory(line.Split(' ')[1]);
|
||||
umdcat = InfoTool.GetUMDCategory(line.Split(' ')[1]);
|
||||
else if (line.StartsWith("L0 length"))
|
||||
umdlayer = line.Split(' ')[2];
|
||||
else if (line.StartsWith("FileSize:"))
|
||||
@@ -207,7 +265,7 @@ namespace MPF.Modules.UmdImageCreator
|
||||
}
|
||||
|
||||
// If the L0 length is the size of the full disc, there's no layerbreak
|
||||
if (Int64.Parse(umdlayer) * 2048 == umdsize)
|
||||
if (Int64.TryParse(umdlayer, out long umdlayerValue) && umdlayerValue * 2048 == umdsize)
|
||||
umdlayer = null;
|
||||
|
||||
return true;
|
||||
@@ -4,12 +4,11 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using BurnOutSharp;
|
||||
using BurnOutSharp.ProtectionType;
|
||||
using MPF.Core.Data;
|
||||
using BinaryObjectScanner.Protection;
|
||||
using BinaryObjectScanner;
|
||||
using psxt001z;
|
||||
|
||||
namespace MPF.Library
|
||||
namespace MPF.Core
|
||||
{
|
||||
public static class Protection
|
||||
{
|
||||
@@ -20,7 +19,11 @@ namespace MPF.Library
|
||||
/// <param name="options">Options object that determines what to scan</param>
|
||||
/// <param name="progress">Optional progress callback</param>
|
||||
/// <returns>Set of all detected copy protections with an optional error string</returns>
|
||||
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Options options, IProgress<ProtectionProgress> progress = null)
|
||||
#if NET48
|
||||
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Data.Options options, IProgress<ProtectionProgress> progress = null)
|
||||
#else
|
||||
public static async Task<(Dictionary<string, List<string>>?, string?)> RunProtectionScanOnPath(string path, Data.Options options, IProgress<ProtectionProgress>? progress = null)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -28,7 +31,10 @@ namespace MPF.Library
|
||||
{
|
||||
var scanner = new Scanner(
|
||||
options.ScanArchivesForProtection,
|
||||
scanContents: true, // Hardcoded value to avoid issues
|
||||
scanGameEngines: false, // Hardcoded value to avoid issues
|
||||
options.ScanPackersForProtection,
|
||||
scanPaths: true, // Hardcoded value to avoid issues
|
||||
options.IncludeDebugProtectionInformation,
|
||||
progress);
|
||||
|
||||
@@ -60,11 +66,15 @@ namespace MPF.Library
|
||||
/// </summary>
|
||||
/// <param name="protections">Dictionary of file to list of protection mappings</param>
|
||||
/// <returns>Detected protections, if any</returns>
|
||||
#if NET48
|
||||
public static string FormatProtections(Dictionary<string, List<string>> protections)
|
||||
#else
|
||||
public static string? FormatProtections(Dictionary<string, List<string>>? protections)
|
||||
#endif
|
||||
{
|
||||
// If the filtered list is empty in some way, return
|
||||
if (protections == null || !protections.Any())
|
||||
return "None found";
|
||||
return "None found [OMIT FROM SUBMISSION]";
|
||||
|
||||
// Get an ordered list of distinct found protections
|
||||
var orderedDistinctProtections = protections
|
||||
@@ -75,7 +85,7 @@ namespace MPF.Library
|
||||
// Sanitize and join protections for writing
|
||||
string protectionString = SanitizeFoundProtections(orderedDistinctProtections);
|
||||
if (string.IsNullOrWhiteSpace(protectionString))
|
||||
return "None found";
|
||||
return "None found [OMIT FROM SUBMISSION]";
|
||||
|
||||
return protectionString;
|
||||
}
|
||||
@@ -85,8 +95,16 @@ namespace MPF.Library
|
||||
/// </summary>
|
||||
/// <param name="path">Path to scan for anti-modchip strings</param>
|
||||
/// <returns>Anti-modchip existence if possible, false on error</returns>
|
||||
#if NET48
|
||||
public static async Task<bool> GetPlayStationAntiModchipDetected(string path)
|
||||
#else
|
||||
public static async Task<bool> GetPlayStationAntiModchipDetected(string? path)
|
||||
#endif
|
||||
{
|
||||
// If there is no valid path
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return false;
|
||||
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
@@ -97,7 +115,7 @@ namespace MPF.Library
|
||||
try
|
||||
{
|
||||
byte[] fileContent = File.ReadAllBytes(file);
|
||||
string protection = antiModchip.CheckContents(file, fileContent, false);
|
||||
var protection = antiModchip.CheckContents(file, fileContent, false);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
return true;
|
||||
}
|
||||
@@ -130,6 +148,15 @@ namespace MPF.Library
|
||||
/// <param name="foundProtections">Enumerable of found protections</param>
|
||||
public static string SanitizeFoundProtections(IEnumerable<string> foundProtections)
|
||||
{
|
||||
// EXCEPTIONS
|
||||
if (foundProtections.Any(p => p.StartsWith("[Exception opening file")))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("[Exception opening file"));
|
||||
foundProtections = foundProtections
|
||||
.Prepend("Exception occurred while scanning [RESCAN NEEDED]")
|
||||
.OrderBy(p => p);
|
||||
}
|
||||
|
||||
// ActiveMARK
|
||||
if (foundProtections.Any(p => p == "ActiveMARK 5") && foundProtections.Any(p => p == "ActiveMARK"))
|
||||
foundProtections = foundProtections.Where(p => p != "ActiveMARK");
|
||||
@@ -212,80 +239,57 @@ namespace MPF.Library
|
||||
}
|
||||
|
||||
// SafeDisc
|
||||
// TODO: Update based on new internal naming schemes
|
||||
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
|
||||
{
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}")))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "SafeDisc")
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
.Where(p => !p.StartsWith("Macrovision Security Driver"))
|
||||
.Where(p => p != "SafeDisc")
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}")))
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
|
||||
.Where(p => p != "SafeDisc 1/Lite")
|
||||
.Where(p => p != "SafeDisc 1-3")
|
||||
.Where(p => p != "SafeDisc 2")
|
||||
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
|
||||
.Where(p => !p.StartsWith("SafeDisc (dplayerx.dll)"))
|
||||
.Where(p => !p.StartsWith("SafeDisc (drvmgt.dll)"))
|
||||
.Where(p => !p.StartsWith("SafeDisc (secdrv.sys)"))
|
||||
.Where(p => p != "SafeDisc Lite");
|
||||
.Where(p => p != "SafeDisc 2+");
|
||||
}
|
||||
else if (foundProtections.Any(p => p.StartsWith("SafeDisc (drvmgt.dll)")))
|
||||
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}")))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "SafeDisc")
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
.Where(p => !p.StartsWith("Macrovision Security Driver"))
|
||||
.Where(p => p != "SafeDisc")
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
|
||||
.Where(p => p != "SafeDisc 1/Lite")
|
||||
.Where(p => p != "SafeDisc 1-3")
|
||||
.Where(p => p != "SafeDisc 2")
|
||||
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
|
||||
.Where(p => !p.StartsWith("SafeDisc (dplayerx.dll)"))
|
||||
.Where(p => !p.StartsWith("SafeDisc (secdrv.sys)"))
|
||||
.Where(p => p != "SafeDisc Lite");
|
||||
.Where(p => p != "SafeDisc 2+");
|
||||
}
|
||||
else if (foundProtections.Any(p => p.StartsWith("SafeDisc (secdrv.sys)")))
|
||||
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "SafeDisc")
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
.Where(p => !p.StartsWith("Macrovision Security Driver"))
|
||||
.Where(p => p != "SafeDisc")
|
||||
.Where(p => p != "SafeDisc 1/Lite")
|
||||
.Where(p => p != "SafeDisc 1-3")
|
||||
.Where(p => p != "SafeDisc 2")
|
||||
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
|
||||
.Where(p => !p.StartsWith("SafeDisc (dplayerx.dll)"))
|
||||
.Where(p => p != "SafeDisc Lite");
|
||||
.Where(p => p != "SafeDisc 2+");
|
||||
}
|
||||
else if (foundProtections.Any(p => p.StartsWith("SafeDisc (dplayerx.dll)")))
|
||||
else if (foundProtections.Any(p => p.StartsWith("Macrovision Security Driver")))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "SafeDisc")
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
.Where(p => p != "SafeDisc")
|
||||
.Where(p => p != "SafeDisc 1/Lite")
|
||||
.Where(p => p != "SafeDisc 1-3")
|
||||
.Where(p => p != "SafeDisc 2")
|
||||
.Where(p => p != "SafeDisc 3.20-4.xx (version removed)")
|
||||
.Where(p => p != "SafeDisc Lite");
|
||||
.Where(p => p != "SafeDisc 2+");
|
||||
}
|
||||
else if (foundProtections.Any(p => p == "SafeDisc 3.20-4.xx (version removed)"))
|
||||
else if (foundProtections.Any(p => p == "SafeDisc 2+"))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "SafeDisc")
|
||||
.Where(p => p != "SafeDisc 1/Lite")
|
||||
.Where(p => p != "SafeDisc 1-3")
|
||||
.Where(p => p != "SafeDisc 2")
|
||||
.Where(p => p != "SafeDisc Lite");
|
||||
}
|
||||
else if (foundProtections.Any(p => p == "SafeDisc 2"))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "SafeDisc")
|
||||
.Where(p => p != "SafeDisc 1/Lite")
|
||||
.Where(p => p != "SafeDisc 1-3")
|
||||
.Where(p => p != "SafeDisc Lite");
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
.Where(p => p != "SafeDisc");
|
||||
}
|
||||
else if (foundProtections.Any(p => p == "SafeDisc 1/Lite"))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "SafeDisc")
|
||||
.Where(p => p != "SafeDisc 1-3")
|
||||
.Where(p => p != "SafeDisc Lite");
|
||||
}
|
||||
else if (foundProtections.Any(p => p == "SafeDisc Lite"))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "SafeDisc")
|
||||
.Where(p => p != "SafeDisc 1-3");
|
||||
}
|
||||
else if (foundProtections.Any(p => p == "SafeDisc 1-3"))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "SafeDisc");
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
.Where(p => p != "SafeDisc");
|
||||
}
|
||||
}
|
||||
|
||||
1922
MPF.Core/SubmissionInfoTool.cs
Normal file
1922
MPF.Core/SubmissionInfoTool.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,13 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
|
||||
namespace MPF.UI.Core.ComboBoxItems
|
||||
namespace MPF.Core.UI.ComboBoxItems
|
||||
{
|
||||
/// <summary>
|
||||
/// A generic combo box element
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Enum type representing the possible values</typeparam>
|
||||
public class Element<T> : IElement where T : struct, Enum
|
||||
public class Element<T> : IEquatable<Element<T>>, IElement where T : struct, Enum
|
||||
{
|
||||
private readonly T Data;
|
||||
|
||||
@@ -47,5 +47,18 @@ namespace MPF.UI.Core.ComboBoxItems
|
||||
.OfType<T>()
|
||||
.Select(e => new Element<T>(e));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public bool Equals(Element<T> other)
|
||||
#else
|
||||
public bool Equals(Element<T>? other)
|
||||
#endif
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return Name == other.Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF.UI.Core.ComboBoxItems
|
||||
namespace MPF.Core.UI.ComboBoxItems
|
||||
{
|
||||
public interface IElement
|
||||
{
|
||||
@@ -1,18 +1,20 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.UI.Core.ComboBoxItems;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF
|
||||
namespace MPF.Core.UI.ComboBoxItems
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single item in the System combo box
|
||||
/// </summary>
|
||||
public class RedumpSystemComboBoxItem : IElement
|
||||
public class RedumpSystemComboBoxItem : IEquatable<RedumpSystemComboBoxItem>, IElement
|
||||
{
|
||||
#if NET48
|
||||
private readonly object Data;
|
||||
#else
|
||||
private readonly object? Data;
|
||||
#endif
|
||||
|
||||
public RedumpSystemComboBoxItem(RedumpSystem? system) => Data = system;
|
||||
public RedumpSystemComboBoxItem(SystemCategory? category) => Data = category;
|
||||
@@ -81,5 +83,18 @@ namespace MPF
|
||||
|
||||
return systemsValues;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public bool Equals(RedumpSystemComboBoxItem other)
|
||||
#else
|
||||
public bool Equals(RedumpSystemComboBoxItem? other)
|
||||
#endif
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return Value == other.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
257
MPF.Core/UI/ViewModels/DiscInformationViewModel.cs
Normal file
257
MPF.Core/UI/ViewModels/DiscInformationViewModel.cs
Normal file
@@ -0,0 +1,257 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ComboBoxItems;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
public class DiscInformationViewModel
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Application-level Options object
|
||||
/// </summary>
|
||||
public Options Options { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SubmissionInfo object to fill and save
|
||||
/// </summary>
|
||||
public SubmissionInfo SubmissionInfo { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lists
|
||||
|
||||
/// <summary>
|
||||
/// List of available disc categories
|
||||
/// </summary>
|
||||
public List<Element<DiscCategory>> Categories { get; private set; } = Element<DiscCategory>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of available regions
|
||||
/// </summary>
|
||||
public List<Element<Region>> Regions { get; private set; } = Element<Region>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of Redump-supported Regions
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static readonly List<Region> RedumpRegions = new List<Region>
|
||||
#else
|
||||
private static readonly List<Region> RedumpRegions = new()
|
||||
#endif
|
||||
{
|
||||
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>
|
||||
#if NET48
|
||||
private static readonly List<Language> RedumpLanguages = new List<Language>
|
||||
#else
|
||||
private static readonly List<Language> RedumpLanguages = new()
|
||||
#endif
|
||||
{
|
||||
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>
|
||||
#if NET48
|
||||
public DiscInformationViewModel(Options options, SubmissionInfo submissionInfo)
|
||||
#else
|
||||
public DiscInformationViewModel(Options options, SubmissionInfo? submissionInfo)
|
||||
#endif
|
||||
{
|
||||
Options = options;
|
||||
SubmissionInfo = submissionInfo?.Clone() as SubmissionInfo ?? new SubmissionInfo();
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Load the current contents of the base SubmissionInfo to the UI
|
||||
/// </summary>
|
||||
/// TODO: Convert selected list item to binding
|
||||
public void Load()
|
||||
{
|
||||
if (SubmissionInfo.CommonDiscInfo?.Languages != null)
|
||||
Languages.ForEach(l => l.IsChecked = SubmissionInfo.CommonDiscInfo.Languages.Contains(l));
|
||||
if (SubmissionInfo.CommonDiscInfo?.LanguageSelection != null)
|
||||
LanguageSelections.ForEach(ls => ls.IsChecked = SubmissionInfo.CommonDiscInfo.LanguageSelection.Contains(ls));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current contents of the UI to the base SubmissionInfo
|
||||
/// </summary>
|
||||
/// TODO: Convert selected list item to binding
|
||||
public void Save()
|
||||
{
|
||||
if (SubmissionInfo.CommonDiscInfo == null) SubmissionInfo.CommonDiscInfo = new CommonDiscInfoSection();
|
||||
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
|
||||
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
|
||||
SubmissionInfo.CommonDiscInfo.Languages = new Language?[] { null };
|
||||
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulate the list of Languages based on Redump support
|
||||
/// </summary>
|
||||
public void SetRedumpLanguages()
|
||||
{
|
||||
this.Languages = RedumpLanguages.Select(l => new Element<Language>(l)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulate the list of Regions based on Redump support
|
||||
/// </summary>
|
||||
public void SetRedumpRegions()
|
||||
{
|
||||
this.Regions = RedumpRegions.Select(r => new Element<Region>(r)).ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1870
MPF.Core/UI/ViewModels/MainViewModel.cs
Normal file
1870
MPF.Core/UI/ViewModels/MainViewModel.cs
Normal file
File diff suppressed because it is too large
Load Diff
124
MPF.Core/UI/ViewModels/OptionsViewModel.cs
Normal file
124
MPF.Core/UI/ViewModels/OptionsViewModel.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ComboBoxItems;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
public class OptionsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Title for the window
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public string Title
|
||||
#else
|
||||
public string? Title
|
||||
#endif
|
||||
{
|
||||
get => _title;
|
||||
set
|
||||
{
|
||||
_title = value;
|
||||
TriggerPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
#if NET48
|
||||
private string _title;
|
||||
#else
|
||||
private string? _title;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Current set of options
|
||||
/// </summary>
|
||||
public Options Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag for if settings were saved or not
|
||||
/// </summary>
|
||||
public bool SavedSettings { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
#if NET48
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
#else
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lists
|
||||
|
||||
/// <summary>
|
||||
/// List of available internal programs
|
||||
/// </summary>
|
||||
public List<Element<InternalProgram>> InternalPrograms => PopulateInternalPrograms();
|
||||
|
||||
/// <summary>
|
||||
/// Current list of supported system profiles
|
||||
/// </summary>
|
||||
public List<RedumpSystemComboBoxItem> Systems => RedumpSystemComboBoxItem.GenerateElements().ToList();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public OptionsViewModel(Options baseOptions)
|
||||
{
|
||||
Options = new Options(baseOptions);
|
||||
}
|
||||
|
||||
#region Population
|
||||
|
||||
/// <summary>
|
||||
/// Get a complete list of supported internal programs
|
||||
/// </summary>
|
||||
private static List<Element<InternalProgram>> PopulateInternalPrograms()
|
||||
{
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.Redumper };
|
||||
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Commands
|
||||
|
||||
/// <summary>
|
||||
/// Test Redump login credentials
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public (bool?, string) TestRedumpLogin(string username, string password)
|
||||
#else
|
||||
public async Task<(bool?, string?)> TestRedumpLogin(string username, string password)
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
return RedumpWebClient.ValidateCredentials(username, password);
|
||||
#else
|
||||
return await RedumpHttpClient.ValidateCredentials(username, password);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Property Updates
|
||||
|
||||
/// <summary>
|
||||
/// Trigger a property changed event
|
||||
/// </summary>
|
||||
private void TriggerPropertyChanged(string propertyName)
|
||||
{
|
||||
// If the property change event is initialized
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
#if FALSE
|
||||
|
||||
using System;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
@@ -394,3 +396,5 @@ namespace MPF.Core.Utilities
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
@@ -72,6 +72,7 @@ namespace MPF.Core.Utilities
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
//case RedumpSystem.SonyPlayStation5:
|
||||
case RedumpSystem.SonyPlayStationPortable:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -94,11 +95,13 @@ namespace MPF.Core.Utilities
|
||||
case RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem:
|
||||
case RedumpSystem.AudioCD:
|
||||
case RedumpSystem.DVDAudio:
|
||||
case RedumpSystem.HasbroiONEducationalGamingSystem:
|
||||
case RedumpSystem.HasbroVideoNow:
|
||||
case RedumpSystem.HasbroVideoNowColor:
|
||||
case RedumpSystem.HasbroVideoNowJr:
|
||||
case RedumpSystem.HasbroVideoNowXP:
|
||||
case RedumpSystem.PhilipsCDi:
|
||||
case RedumpSystem.PlayStationGameSharkUpdates:
|
||||
case RedumpSystem.SuperAudioCD:
|
||||
return true;
|
||||
default:
|
||||
|
||||
@@ -14,7 +14,11 @@ namespace MPF.Core.Utilities
|
||||
/// <param name="reader">TextReader representing the input</param>
|
||||
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
|
||||
/// <param name="handler">Event handler to be invoked to write to log</param>
|
||||
#if NET48
|
||||
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string> handler)
|
||||
#else
|
||||
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string>? handler)
|
||||
#endif
|
||||
{
|
||||
// Initialize the required variables
|
||||
char[] buffer = new char[256];
|
||||
@@ -63,7 +67,11 @@ namespace MPF.Core.Utilities
|
||||
/// <param name="line">Current line to process</param>
|
||||
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
|
||||
/// <param name="handler">Event handler to be invoked to write to log</param>
|
||||
#if NET48
|
||||
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
|
||||
#else
|
||||
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
|
||||
#endif
|
||||
{
|
||||
line = line.Replace("\r\n", "\n");
|
||||
var split = line.Split('\n');
|
||||
@@ -105,7 +113,11 @@ namespace MPF.Core.Utilities
|
||||
/// <param name="line">Current line to process</param>
|
||||
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
|
||||
/// <param name="handler">Event handler to be invoked to write to log</param>
|
||||
#if NET48
|
||||
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
|
||||
#else
|
||||
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
|
||||
#endif
|
||||
{
|
||||
var split = line.Split('\r');
|
||||
|
||||
|
||||
@@ -1,39 +1,150 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
public static class OptionsLoader
|
||||
{
|
||||
private const string ConfigurationPath = "config.json";
|
||||
|
||||
#region Arguments
|
||||
|
||||
/// <summary>
|
||||
/// Process any standalone arguments for the program
|
||||
/// </summary>
|
||||
/// <returns>True if one of the arguments was processed, false otherwise</returns>
|
||||
public static bool? ProcessStandaloneArguments(string[] args)
|
||||
{
|
||||
// Help options
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
|
||||
return false;
|
||||
|
||||
// List options
|
||||
if (args[0] == "-lm" || args[0] == "--listmedia")
|
||||
{
|
||||
Console.WriteLine("Supported Media Types:");
|
||||
foreach (string mediaType in Extensions.ListMediaTypes())
|
||||
{
|
||||
Console.WriteLine(mediaType);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-lp" || args[0] == "--listprograms")
|
||||
{
|
||||
Console.WriteLine("Supported Programs:");
|
||||
foreach (string program in EnumExtensions.ListPrograms())
|
||||
{
|
||||
Console.WriteLine(program);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-ls" || args[0] == "--listsystems")
|
||||
{
|
||||
Console.WriteLine("Supported Systems:");
|
||||
foreach (string system in Extensions.ListSystems())
|
||||
{
|
||||
Console.WriteLine(system);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process common arguments for all functionality
|
||||
/// </summary>
|
||||
/// <returns>True if all arguments pass, false otherwise</returns>
|
||||
#if NET48
|
||||
public static (bool, MediaType, RedumpSystem?, string) ProcessCommonArguments(string[] args)
|
||||
#else
|
||||
public static (bool, MediaType, RedumpSystem?, string?) ProcessCommonArguments(string[] args)
|
||||
#endif
|
||||
{
|
||||
// All other use requires at least 3 arguments
|
||||
if (args.Length < 3)
|
||||
return (false, MediaType.NONE, null, "Invalid number of arguments");
|
||||
|
||||
// Check the MediaType
|
||||
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
|
||||
if (mediaType == MediaType.NONE)
|
||||
return (false, MediaType.NONE, null, $"{args[0]} is not a recognized media type");
|
||||
|
||||
// Check the RedumpSystem
|
||||
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
|
||||
if (knownSystem == null)
|
||||
return (false, MediaType.NONE, null, $"{args[1]} is not a recognized system");
|
||||
|
||||
return (true, mediaType, knownSystem, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from application arguments
|
||||
/// </summary>
|
||||
public static (Options, string, int) LoadFromArguments(string[] args, int startIndex = 0)
|
||||
#if NET48
|
||||
public static (Options, SubmissionInfo, string, int) LoadFromArguments(string[] args, int startIndex = 0)
|
||||
#else
|
||||
public static (Options, SubmissionInfo?, string?, int) LoadFromArguments(string[] args, int startIndex = 0)
|
||||
#endif
|
||||
{
|
||||
// Create the output values
|
||||
var options = new Options();
|
||||
// Create the output values with defaults
|
||||
var options = new Options()
|
||||
{
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
AddFilenameSuffix = false,
|
||||
OutputSubmissionJSON = false,
|
||||
CompressLogFiles = false,
|
||||
DeleteUnnecessaryFiles = false,
|
||||
};
|
||||
|
||||
// Create the submission info to return, if necessary
|
||||
#if NET48
|
||||
SubmissionInfo info = null;
|
||||
string parsedPath = null;
|
||||
#else
|
||||
SubmissionInfo? info = null;
|
||||
string? parsedPath = null;
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
return (options, null, null, 0);
|
||||
|
||||
// If we have an invalid start index, just return
|
||||
if (startIndex < 0 || startIndex >= args.Length)
|
||||
return (options, null, startIndex);
|
||||
return (options, null, null, startIndex);
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
{
|
||||
// Use specific program
|
||||
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
string internalProgram = args[startIndex].Split('=')[1];
|
||||
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
string internalProgram = args[startIndex + 1];
|
||||
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Redump login
|
||||
if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
{
|
||||
string[] credentials = args[startIndex].Split('=')[1].Split(';');
|
||||
options.RedumpUsername = credentials[0];
|
||||
@@ -46,17 +157,10 @@ namespace MPF.Core.Utilities
|
||||
startIndex += 2;
|
||||
}
|
||||
|
||||
// Use specific program
|
||||
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
// Pull all information (requires Redump login)
|
||||
else if (args[startIndex].Equals("-a") || args[startIndex].Equals("--pull-all"))
|
||||
{
|
||||
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++;
|
||||
options.PullAllInformation = true;
|
||||
}
|
||||
|
||||
// Use a device path for physical checks
|
||||
@@ -82,6 +186,25 @@ namespace MPF.Core.Utilities
|
||||
protectFile = true;
|
||||
}
|
||||
|
||||
// Include seed info file
|
||||
else if (args[startIndex].StartsWith("-l=") || args[startIndex].StartsWith("--load-seed="))
|
||||
{
|
||||
string seedInfo = args[startIndex].Split('=')[1];
|
||||
info = SubmissionInfoTool.CreateFromFile(seedInfo);
|
||||
}
|
||||
else if (args[startIndex] == "-l" || args[startIndex] == "--load-seed")
|
||||
{
|
||||
string seedInfo = args[startIndex + 1];
|
||||
info = SubmissionInfoTool.CreateFromFile(seedInfo);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Add filename suffix
|
||||
else if (args[startIndex].Equals("-x") || args[startIndex].Equals("--suffix"))
|
||||
{
|
||||
options.AddFilenameSuffix = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
|
||||
{
|
||||
@@ -94,6 +217,12 @@ namespace MPF.Core.Utilities
|
||||
options.CompressLogFiles = true;
|
||||
}
|
||||
|
||||
// Delete unnecessary files files
|
||||
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
|
||||
{
|
||||
options.DeleteUnnecessaryFiles = true;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
@@ -101,15 +230,11 @@ namespace MPF.Core.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
// We default to DiscImageCreator currently
|
||||
if (options.InternalProgram == InternalProgram.NONE)
|
||||
options.InternalProgram = InternalProgram.DiscImageCreator;
|
||||
|
||||
// Now deal with the complex options
|
||||
options.ScanForProtection = scan && !string.IsNullOrWhiteSpace(parsedPath);
|
||||
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrWhiteSpace(parsedPath);
|
||||
|
||||
return (options, parsedPath, startIndex);
|
||||
return (options, info, parsedPath, startIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -117,20 +242,25 @@ namespace MPF.Core.Utilities
|
||||
/// </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");
|
||||
var supportedArguments = new List<string>
|
||||
{
|
||||
"-u, --use <program> Dumping program output type [REQUIRED]",
|
||||
"-c, --credentials <user> <pw> Redump username and password",
|
||||
"-a, --pull-all Pull all information from Redump (requires --credentials)",
|
||||
"-p, --path <drivepath> Physical drive path for additional checks",
|
||||
"-s, --scan Enable copy protection scan (requires --path)",
|
||||
"-f, --protect-file Output protection to separate file (requires --scan)",
|
||||
"-l, --load-seed <path> Load a seed submission JSON for user information",
|
||||
"-x, --suffix Enable adding filename suffix",
|
||||
"-j, --json Enable submission JSON output",
|
||||
"-z, --zip Enable log file compression",
|
||||
"-d, --delete Enable unnecessary file deletion",
|
||||
};
|
||||
|
||||
return supportedArguments;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Configuration
|
||||
|
||||
@@ -139,17 +269,20 @@ namespace MPF.Core.Utilities
|
||||
/// </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)
|
||||
if (!File.Exists(ConfigurationPath))
|
||||
{
|
||||
dict[key] = settings[key]?.Value ?? string.Empty;
|
||||
_ = File.Create(ConfigurationPath);
|
||||
return new Options();
|
||||
}
|
||||
|
||||
return new Options(dict);
|
||||
var serializer = JsonSerializer.Create();
|
||||
var reader = new StreamReader(ConfigurationPath);
|
||||
#if NET48
|
||||
var settings = serializer.Deserialize(reader, typeof(Dictionary<string, string>)) as Dictionary<string, string>;
|
||||
#else
|
||||
var settings = serializer.Deserialize(reader, typeof(Dictionary<string, string?>)) as Dictionary<string, string?>;
|
||||
#endif
|
||||
return new Options(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -157,16 +290,10 @@ namespace MPF.Core.Utilities
|
||||
/// </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);
|
||||
var serializer = JsonSerializer.Create();
|
||||
var sw = new StreamWriter(ConfigurationPath) { AutoFlush = true };
|
||||
var writer = new JsonTextWriter(sw) { Formatting = Formatting.Indented };
|
||||
serializer.Serialize(writer, options.Settings, typeof(Dictionary<string, string>));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Reflection;
|
||||
using MPF.Core.Data;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
@@ -84,6 +84,7 @@ namespace MPF.Core.Utilities
|
||||
return Result.Failure("Please select a valid system");
|
||||
|
||||
// If we're on an unsupported type, update the status accordingly
|
||||
#if NET48
|
||||
switch (type)
|
||||
{
|
||||
// Fully supported types
|
||||
@@ -116,6 +117,128 @@ namespace MPF.Core.Utilities
|
||||
default:
|
||||
return Result.Failure($"{type.LongName()} media are not supported for dumping");
|
||||
}
|
||||
#else
|
||||
return type switch
|
||||
{
|
||||
// Fully supported types
|
||||
MediaType.BluRay
|
||||
or MediaType.CDROM
|
||||
or MediaType.DVD
|
||||
or MediaType.FloppyDisk
|
||||
or MediaType.HardDisk
|
||||
or MediaType.CompactFlash
|
||||
or MediaType.SDCard
|
||||
or MediaType.FlashDrive
|
||||
or MediaType.HDDVD => Result.Success($"{type.LongName()} ready to dump"),
|
||||
|
||||
// Partially supported types
|
||||
MediaType.GDROM
|
||||
or MediaType.NintendoGameCubeGameDisc
|
||||
or MediaType.NintendoWiiOpticalDisc => Result.Success($"{type.LongName()} partially supported for dumping"),
|
||||
|
||||
// Special case for other supported tools
|
||||
MediaType.UMD => Result.Failure($"{type.LongName()} supported for submission info parsing"),
|
||||
|
||||
// Specifically unknown type
|
||||
MediaType.NONE => Result.Failure($"Please select a valid media type"),
|
||||
|
||||
// Undumpable but recognized types
|
||||
_ => Result.Failure($"{type.LongName()} media are not supported for dumping"),
|
||||
};
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns false if a given InternalProgram does not support a given MediaType
|
||||
/// </summary>
|
||||
public static bool ProgramSupportsMedia(InternalProgram program, MediaType? type)
|
||||
{
|
||||
// If the media type is not set, return false
|
||||
if (type == null || type == MediaType.NONE)
|
||||
return false;
|
||||
|
||||
#if NET48
|
||||
switch (program)
|
||||
{
|
||||
case InternalProgram.Redumper:
|
||||
switch (type)
|
||||
{
|
||||
// Formats considered at least partially dumpable by Redumper
|
||||
case MediaType.CDROM:
|
||||
case MediaType.DVD:
|
||||
case MediaType.GDROM:
|
||||
return true;
|
||||
|
||||
// All other formats considered unsupported
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case InternalProgram.Aaru:
|
||||
case InternalProgram.DiscImageCreator:
|
||||
switch (type)
|
||||
{
|
||||
// Formats considered at least partially supported
|
||||
case MediaType.BluRay:
|
||||
case MediaType.CDROM:
|
||||
case MediaType.DVD:
|
||||
case MediaType.FloppyDisk:
|
||||
case MediaType.HardDisk:
|
||||
case MediaType.CompactFlash:
|
||||
case MediaType.SDCard:
|
||||
case MediaType.FlashDrive:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return true;
|
||||
|
||||
// All other formats considered unsupported
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
// All other InternalPrograms are not used for dumping
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
switch (program)
|
||||
{
|
||||
case InternalProgram.Redumper:
|
||||
return type switch
|
||||
{
|
||||
// Formats considered at least partially supported by Redumper
|
||||
MediaType.CDROM
|
||||
or MediaType.DVD
|
||||
or MediaType.GDROM => true,
|
||||
|
||||
// All other formats considered unsupported
|
||||
_ => false,
|
||||
};
|
||||
case InternalProgram.Aaru:
|
||||
case InternalProgram.DiscImageCreator:
|
||||
return type switch
|
||||
{
|
||||
// Formats considered at least partially supported by MPF
|
||||
MediaType.BluRay
|
||||
or MediaType.CDROM
|
||||
or MediaType.DVD
|
||||
or MediaType.GDROM
|
||||
or MediaType.FloppyDisk
|
||||
or MediaType.CompactFlash
|
||||
or MediaType.SDCard
|
||||
or MediaType.FlashDrive
|
||||
or MediaType.HardDisk
|
||||
or MediaType.HDDVD
|
||||
or MediaType.NintendoGameCubeGameDisc
|
||||
or MediaType.NintendoWiiOpticalDisc => true,
|
||||
|
||||
// All other formats considered unsupported
|
||||
_ => false,
|
||||
};
|
||||
// All other InternalPrograms are not supported for dumping
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -130,16 +253,23 @@ namespace MPF.Core.Utilities
|
||||
/// String representing the message to display the the user.
|
||||
/// String representing the new release URL.
|
||||
/// </returns>
|
||||
#if NET48
|
||||
public static (bool different, string message, string url) CheckForNewVersion()
|
||||
#else
|
||||
public static (bool different, string message, string? url) CheckForNewVersion()
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get current assembly version
|
||||
var assemblyVersion = Assembly.GetEntryAssembly().GetName().Version;
|
||||
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}" + (assemblyVersion.Build != 0 ? $".{assemblyVersion.Build}" : string.Empty);
|
||||
var assemblyVersion = Assembly.GetEntryAssembly()?.GetName()?.Version;
|
||||
if (assemblyVersion == null)
|
||||
return (false, "Assembly version could not be determined", null);
|
||||
|
||||
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}.{assemblyVersion.Build}";
|
||||
|
||||
// Get the latest tag from GitHub
|
||||
(string tag, string url) = GetRemoteVersionAndUrl();
|
||||
var (tag, url) = GetRemoteVersionAndUrl();
|
||||
bool different = version != tag;
|
||||
|
||||
string message = $"Local version: {version}"
|
||||
@@ -159,19 +289,38 @@ namespace MPF.Core.Utilities
|
||||
/// <summary>
|
||||
/// Get the current informational version formatted as a string
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public static string GetCurrentVersion()
|
||||
#else
|
||||
public static string? GetCurrentVersion()
|
||||
#endif
|
||||
{
|
||||
var assemblyVersion = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
|
||||
return assemblyVersion.InformationalVersion;
|
||||
try
|
||||
{
|
||||
var assembly = Assembly.GetEntryAssembly();
|
||||
if (assembly == null)
|
||||
return null;
|
||||
|
||||
var assemblyVersion = Attribute.GetCustomAttribute(assembly, typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
|
||||
return assemblyVersion?.InformationalVersion;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ex.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the latest version of MPF from GitHub and the release URL
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private static (string tag, string url) GetRemoteVersionAndUrl()
|
||||
#else
|
||||
private static (string? tag, string? url) GetRemoteVersionAndUrl()
|
||||
#endif
|
||||
{
|
||||
#if NETFRAMEWORK
|
||||
using (System.Net.WebClient wc = new System.Net.WebClient())
|
||||
#if NET48
|
||||
using (var 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";
|
||||
|
||||
@@ -185,16 +334,22 @@ namespace MPF.Core.Utilities
|
||||
return (latestTag, releaseUrl);
|
||||
}
|
||||
#else
|
||||
using (System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient())
|
||||
using (var 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 latestReleaseJsonString = hc.Send(message)?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
if (latestReleaseJsonString == null)
|
||||
return (null, null);
|
||||
|
||||
var latestReleaseJson = JObject.Parse(latestReleaseJsonString);
|
||||
string latestTag = latestReleaseJson["tag_name"].ToString();
|
||||
string releaseUrl = latestReleaseJson["html_url"].ToString();
|
||||
if (latestReleaseJson == null)
|
||||
return (null, null);
|
||||
|
||||
var latestTag = latestReleaseJson["tag_name"]?.ToString();
|
||||
var releaseUrl = latestReleaseJson["html_url"]?.ToString();
|
||||
|
||||
return (latestTag, releaseUrl);
|
||||
}
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace MPF.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// The audio or data file’s filetype
|
||||
/// </summary>
|
||||
public enum CueFileType
|
||||
{
|
||||
/// <summary>
|
||||
/// Intel binary file (least significant byte first). Use for data files.
|
||||
/// </summary>
|
||||
BINARY,
|
||||
|
||||
/// <summary>
|
||||
/// Motorola binary file (most significant byte first). Use for data files.
|
||||
/// </summary>
|
||||
MOTOROLA,
|
||||
|
||||
/// <summary>
|
||||
/// Audio AIFF file (44.1KHz 16-bit stereo)
|
||||
/// </summary>
|
||||
AIFF,
|
||||
|
||||
/// <summary>
|
||||
/// Audio WAVE file (44.1KHz 16-bit stereo)
|
||||
/// </summary>
|
||||
WAVE,
|
||||
|
||||
/// <summary>
|
||||
/// Audio MP3 file (44.1KHz 16-bit stereo)
|
||||
/// </summary>
|
||||
MP3,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single FILE in a cuesheet
|
||||
/// </summary>
|
||||
public class CueFile
|
||||
{
|
||||
/// <summary>
|
||||
/// filename
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// filetype
|
||||
/// </summary>
|
||||
public CueFileType FileType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of TRACK in FILE
|
||||
/// </summary>
|
||||
public List<CueTrack> Tracks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty FILE
|
||||
/// </summary>
|
||||
public CueFile()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill a FILE from an array of lines
|
||||
/// </summary>
|
||||
/// <param name="fileName">File name to set</param>
|
||||
/// <param name="fileType">File type to set</param>
|
||||
/// <param name="cueLines">Lines array to pull from</param>
|
||||
/// <param name="i">Reference to index in array</param>
|
||||
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
|
||||
public CueFile(string fileName, string fileType, string[] cueLines, ref int i, bool throwOnError = false)
|
||||
{
|
||||
if (cueLines == null)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentNullException(nameof(cueLines));
|
||||
|
||||
return;
|
||||
}
|
||||
else if (i < 0 || i > cueLines.Length)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the current fields
|
||||
this.FileName = fileName.Trim('"');
|
||||
this.FileType = GetFileType(fileType);
|
||||
|
||||
// Increment to start
|
||||
i++;
|
||||
|
||||
for (; i < cueLines.Length; i++)
|
||||
{
|
||||
string line = cueLines[i].Trim();
|
||||
string[] splitLine = line.Split(' ');
|
||||
|
||||
// If we have an empty line, we skip
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
switch (splitLine[0])
|
||||
{
|
||||
// Read comments
|
||||
case "REM":
|
||||
// We ignore all comments for now
|
||||
break;
|
||||
|
||||
// Read track information
|
||||
case "TRACK":
|
||||
if (splitLine.Length < 3)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"TRACK line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.Tracks == null)
|
||||
this.Tracks = new List<CueTrack>();
|
||||
|
||||
var track = new CueTrack(splitLine[1], splitLine[2], cueLines, ref i);
|
||||
if (track == default)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"TRACK line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Tracks.Add(track);
|
||||
break;
|
||||
|
||||
// Default means return
|
||||
default:
|
||||
i--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the FILE out to a stream
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to write to</param>
|
||||
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
|
||||
public void Write(StreamWriter sw, bool throwOnError = false)
|
||||
{
|
||||
// If we don't have any tracks, it's invalid
|
||||
if (this.Tracks == null)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentNullException(nameof(this.Tracks));
|
||||
|
||||
return;
|
||||
}
|
||||
else if (this.Tracks.Count == 0)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentException("No tracks provided to write");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sw.WriteLine($"FILE \"{this.FileName}\" {FromFileType(this.FileType)}");
|
||||
|
||||
foreach (var track in Tracks)
|
||||
{
|
||||
track.Write(sw);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the file type from a given string
|
||||
/// </summary>
|
||||
/// <param name="fileType">String to get value from</param>
|
||||
/// <returns>CueFileType, if possible</returns>
|
||||
private CueFileType GetFileType(string fileType)
|
||||
{
|
||||
switch (fileType.ToLowerInvariant())
|
||||
{
|
||||
case "binary":
|
||||
return CueFileType.BINARY;
|
||||
|
||||
case "motorola":
|
||||
return CueFileType.MOTOROLA;
|
||||
|
||||
case "aiff":
|
||||
return CueFileType.AIFF;
|
||||
|
||||
case "wave":
|
||||
return CueFileType.WAVE;
|
||||
|
||||
case "mp3":
|
||||
return CueFileType.MP3;
|
||||
|
||||
default:
|
||||
return CueFileType.BINARY;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string from a given file type
|
||||
/// </summary>
|
||||
/// <param name="fileType">CueFileType to get value from</param>
|
||||
/// <returns>String, if possible (default BINARY)</returns>
|
||||
private string FromFileType(CueFileType fileType)
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case CueFileType.BINARY:
|
||||
return "BINARY";
|
||||
|
||||
case CueFileType.MOTOROLA:
|
||||
return "MOTOROLA";
|
||||
|
||||
case CueFileType.AIFF:
|
||||
return "AIFF";
|
||||
|
||||
case CueFileType.WAVE:
|
||||
return "WAVE";
|
||||
|
||||
case CueFileType.MP3:
|
||||
return "MP3";
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace MPF.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single INDEX in a TRACK
|
||||
/// </summary>
|
||||
public class CueIndex
|
||||
{
|
||||
/// <summary>
|
||||
/// INDEX number, between 0 and 99
|
||||
/// </summary>
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Starting time of INDEX in minutes
|
||||
/// </summary>
|
||||
public int Minutes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Starting time of INDEX in seconds
|
||||
/// </summary>
|
||||
/// <remarks>There are 60 seconds in a minute</remarks>
|
||||
public int Seconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Starting time of INDEX in frames.
|
||||
/// </summary>
|
||||
/// <remarks>There are 75 frames per second</remarks>
|
||||
public int Frames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty INDEX
|
||||
/// </summary>
|
||||
public CueIndex()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill a INDEX from an array of lines
|
||||
/// </summary>
|
||||
/// <param name="index">Index to set</param>
|
||||
/// <param name="startTime">Start time to set</param>
|
||||
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
|
||||
public CueIndex(string index, string startTime, bool throwOnError = false)
|
||||
{
|
||||
// Set the current fields
|
||||
if (!int.TryParse(index, out int parsedIndex))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentException($"Index was not a number: {index}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (parsedIndex < 0 || parsedIndex > 99)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Index must be between 0 and 99: {parsedIndex}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore empty lines
|
||||
if (string.IsNullOrWhiteSpace(startTime))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentException("Start time was null or whitespace");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore lines that don't contain the correct information
|
||||
if (startTime.Length != 8 || startTime.Count(c => c == ':') != 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Start time was not in a recognized format: {startTime}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Split the line
|
||||
string[] splitTime = startTime.Split(':');
|
||||
if (splitTime.Length != 3)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Start time was not in a recognized format: {startTime}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the lengths
|
||||
int[] lengthSegments = new int[3];
|
||||
|
||||
// Minutes
|
||||
if (!int.TryParse(splitTime[0], out lengthSegments[0]))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Minutes segment was not a number: {splitTime[0]}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (lengthSegments[0] < 0)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Seconds
|
||||
if (!int.TryParse(splitTime[1], out lengthSegments[1]))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Seconds segment was not a number: {splitTime[1]}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Frames
|
||||
if (!int.TryParse(splitTime[2], out lengthSegments[2]))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Frames segment was not a number: {splitTime[2]}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the values
|
||||
this.Index = parsedIndex;
|
||||
this.Minutes = lengthSegments[0];
|
||||
this.Seconds = lengthSegments[1];
|
||||
this.Frames = lengthSegments[2];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the INDEX out to a stream
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to write to</param>
|
||||
public void Write(StreamWriter sw)
|
||||
{
|
||||
sw.WriteLine($" INDEX {this.Index:D2} {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace MPF.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single cuesheet
|
||||
/// </summary>
|
||||
public class CueSheet
|
||||
{
|
||||
/// <summary>
|
||||
/// CATALOG
|
||||
/// </summary>
|
||||
public string Catalog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// CDTEXTFILE
|
||||
/// </summary>
|
||||
public string CdTextFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// PERFORMER
|
||||
/// </summary>
|
||||
public string Performer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SONGWRITER
|
||||
/// </summary>
|
||||
public string Songwriter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// TITLE
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of FILE in cuesheet
|
||||
/// </summary>
|
||||
public List<CueFile> Files { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty cuesheet
|
||||
/// </summary>
|
||||
public CueSheet()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a cuesheet from a file, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename"></param>
|
||||
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
|
||||
public CueSheet(string filename, bool throwOnError = false)
|
||||
{
|
||||
// Check that the file exists
|
||||
if (!File.Exists(filename))
|
||||
return;
|
||||
|
||||
// Check the extension
|
||||
string ext = Path.GetExtension(filename).TrimStart('.');
|
||||
if (!string.Equals(ext, "cue", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(ext, "txt", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the file and begin reading
|
||||
string[] cueLines = File.ReadAllLines(filename);
|
||||
for (int i = 0; i < cueLines.Length; i++)
|
||||
{
|
||||
string line = cueLines[i].Trim();
|
||||
|
||||
// http://stackoverflow.com/questions/554013/regular-expression-to-split-on-spaces-unless-in-quotes
|
||||
string[] splitLine = Regex
|
||||
.Matches(line, @"[^\s""]+|""[^""]*""")
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Groups[0].Value)
|
||||
.ToArray();
|
||||
|
||||
// If we have an empty line, we skip
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
switch (splitLine[0])
|
||||
{
|
||||
// Read comments
|
||||
case "REM":
|
||||
// We ignore all comments for now
|
||||
break;
|
||||
|
||||
// Read MCN
|
||||
case "CATALOG":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"CATALOG line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Catalog = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read external CD-Text file path
|
||||
case "CDTEXTFILE":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"CDTEXTFILE line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.CdTextFile = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced performer
|
||||
case "PERFORMER":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"PERFORMER line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Performer = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced songwriter
|
||||
case "SONGWRITER":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"SONGWRITER line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Songwriter = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced title
|
||||
case "TITLE":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"TITLE line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Title = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read file information
|
||||
case "FILE":
|
||||
if (splitLine.Length < 3)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"FILE line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.Files == null)
|
||||
this.Files = new List<CueFile>();
|
||||
|
||||
var file = new CueFile(splitLine[1], splitLine[2], cueLines, ref i);
|
||||
if (file == default)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"FILE line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Files.Add(file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the cuesheet out to a file
|
||||
/// </summary>
|
||||
/// <param name="filename">File path to write to</param>
|
||||
public void Write(string filename)
|
||||
{
|
||||
using (var fs = File.Open(filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
|
||||
{
|
||||
Write(fs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the cuesheet out to a stream
|
||||
/// </summary>
|
||||
/// <param name="stream">Stream to write to</param>
|
||||
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
|
||||
public void Write(Stream stream, bool throwOnError = false)
|
||||
{
|
||||
// If we don't have any files, it's invalid
|
||||
if (this.Files == null)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentNullException(nameof(this.Files));
|
||||
|
||||
return;
|
||||
}
|
||||
else if (this.Files.Count == 0)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentException("No files provided to write");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
using (var sw = new StreamWriter(stream, Encoding.ASCII, 1024, true))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(this.Catalog))
|
||||
sw.WriteLine($"CATALOG {this.Catalog}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.CdTextFile))
|
||||
sw.WriteLine($"CDTEXTFILE {this.CdTextFile}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Performer))
|
||||
sw.WriteLine($"PERFORMER {this.Performer}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Songwriter))
|
||||
sw.WriteLine($"SONGWRITER {this.Songwriter}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Title))
|
||||
sw.WriteLine($"TITLE {this.Title}");
|
||||
|
||||
foreach (var file in Files)
|
||||
{
|
||||
file.Write(sw);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,554 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace MPF.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// Track datatype
|
||||
/// </summary>
|
||||
public enum CueTrackDataType
|
||||
{
|
||||
/// <summary>
|
||||
/// AUDIO, Audio/Music (2352)
|
||||
/// </summary>
|
||||
AUDIO,
|
||||
|
||||
/// <summary>
|
||||
/// CDG, Karaoke CD+G (2448)
|
||||
/// </summary>
|
||||
CDG,
|
||||
|
||||
/// <summary>
|
||||
/// MODE1/2048, CD-ROM Mode1 Data (cooked)
|
||||
/// </summary>
|
||||
MODE1_2048,
|
||||
|
||||
/// <summary>
|
||||
/// MODE1/2352 CD-ROM Mode1 Data (raw)
|
||||
/// </summary>
|
||||
MODE1_2352,
|
||||
|
||||
/// <summary>
|
||||
/// MODE2/2336, CD-ROM XA Mode2 Data
|
||||
/// </summary>
|
||||
MODE2_2336,
|
||||
|
||||
/// <summary>
|
||||
/// MODE2/2352, CD-ROM XA Mode2 Data
|
||||
/// </summary>
|
||||
MODE2_2352,
|
||||
|
||||
/// <summary>
|
||||
/// CDI/2336, CD-I Mode2 Data
|
||||
/// </summary>
|
||||
CDI_2336,
|
||||
|
||||
/// <summary>
|
||||
/// CDI/2352, CD-I Mode2 Data
|
||||
/// </summary>
|
||||
CDI_2352,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special subcode flags within a track
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum CueTrackFlag
|
||||
{
|
||||
/// <summary>
|
||||
/// DCP, Digital copy permitted
|
||||
/// </summary>
|
||||
DCP = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// 4CH, Four channel audio
|
||||
/// </summary>
|
||||
FourCH = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// PRE, Pre-emphasis enabled (audio tracks only)
|
||||
/// </summary>
|
||||
PRE = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// SCMS, Serial Copy Management System (not supported by all recorders)
|
||||
/// </summary>
|
||||
SCMS = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// DATA, set for data files. This flag is set automatically based on the track’s filetype
|
||||
/// </summary>
|
||||
DATA = 1 << 4,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single TRACK in a FILE
|
||||
/// </summary>
|
||||
public class CueTrack
|
||||
{
|
||||
/// <summary>
|
||||
/// Track number. The range is 1 to 99.
|
||||
/// </summary>
|
||||
public int Number { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Track datatype
|
||||
/// </summary>
|
||||
public CueTrackDataType DataType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// FLAGS
|
||||
/// </summary>
|
||||
public CueTrackFlag Flags { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ISRC
|
||||
/// </summary>
|
||||
/// <remarks>12 characters in length</remarks>
|
||||
public string ISRC { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// PERFORMER
|
||||
/// </summary>
|
||||
public string Performer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SONGWRITER
|
||||
/// </summary>
|
||||
public string Songwriter { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// TITLE
|
||||
/// </summary>
|
||||
public string Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// PREGAP
|
||||
/// </summary>
|
||||
public PreGap PreGap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of INDEX in TRACK
|
||||
/// </summary>
|
||||
/// <remarks>Must start with 0 or 1 and then sequential</remarks>
|
||||
public List<CueIndex> Indices { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// POSTGAP
|
||||
/// </summary>
|
||||
public PostGap PostGap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty TRACK
|
||||
/// </summary>
|
||||
public CueTrack()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill a TRACK from an array of lines
|
||||
/// </summary>
|
||||
/// <param name="number">Number to set</param>
|
||||
/// <param name="dataType">Data type to set</param>
|
||||
/// <param name="cueLines">Lines array to pull from</param>
|
||||
/// <param name="i">Reference to index in array</param>
|
||||
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
|
||||
public CueTrack(string number, string dataType, string[] cueLines, ref int i, bool throwOnError = false)
|
||||
{
|
||||
if (cueLines == null)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentNullException(nameof(cueLines));
|
||||
|
||||
return;
|
||||
}
|
||||
else if (i < 0 || i > cueLines.Length)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the current fields
|
||||
if (!int.TryParse(number, out int parsedNumber))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentException($"Number was not a number: {number}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (parsedNumber < 1 || parsedNumber > 99)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Index must be between 1 and 99: {parsedNumber}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.Number = parsedNumber;
|
||||
this.DataType = GetDataType(dataType);
|
||||
|
||||
// Increment to start
|
||||
i++;
|
||||
|
||||
for (; i < cueLines.Length; i++)
|
||||
{
|
||||
string line = cueLines[i].Trim();
|
||||
string[] splitLine = line.Split(' ');
|
||||
|
||||
// If we have an empty line, we skip
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
continue;
|
||||
|
||||
switch (splitLine[0])
|
||||
{
|
||||
// Read comments
|
||||
case "REM":
|
||||
// We ignore all comments for now
|
||||
break;
|
||||
|
||||
// Read flag information
|
||||
case "FLAGS":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"FLAGS line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Flags = GetFlags(splitLine);
|
||||
break;
|
||||
|
||||
// Read International Standard Recording Code
|
||||
case "ISRC":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"ISRC line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.ISRC = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced performer
|
||||
case "PERFORMER":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"PERFORMER line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Performer = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced songwriter
|
||||
case "SONGWRITER":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"SONGWRITER line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Songwriter = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read CD-Text enhanced title
|
||||
case "TITLE":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"TITLE line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Title = splitLine[1];
|
||||
break;
|
||||
|
||||
// Read pregap information
|
||||
case "PREGAP":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"PREGAP line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var pregap = new PreGap(splitLine[1]);
|
||||
if (pregap == default)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"PREGAP line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.PreGap = pregap;
|
||||
break;
|
||||
|
||||
// Read index information
|
||||
case "INDEX":
|
||||
if (splitLine.Length < 3)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"INDEX line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (this.Indices == null)
|
||||
this.Indices = new List<CueIndex>();
|
||||
|
||||
var index = new CueIndex(splitLine[1], splitLine[2]);
|
||||
if (index == default)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"INDEX line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Indices.Add(index);
|
||||
break;
|
||||
|
||||
// Read postgap information
|
||||
case "POSTGAP":
|
||||
if (splitLine.Length < 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"POSTGAP line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var postgap = new PostGap(splitLine[1]);
|
||||
if (postgap == default)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"POSTGAP line malformed: {line}");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
this.PostGap = postgap;
|
||||
break;
|
||||
|
||||
// Default means return
|
||||
default:
|
||||
i--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the TRACK out to a stream
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to write to</param
|
||||
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
|
||||
public void Write(StreamWriter sw, bool throwOnError = false)
|
||||
{
|
||||
// If we don't have any indices, it's invalid
|
||||
if (this.Indices == null)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentNullException(nameof(this.Indices));
|
||||
|
||||
return;
|
||||
}
|
||||
else if (this.Indices.Count == 0)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentException("No indices provided to write");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sw.WriteLine($" TRACK {this.Number:D2} {FromDataType(this.DataType)}");
|
||||
|
||||
if (this.Flags != 0)
|
||||
sw.WriteLine($" FLAGS {FromFlags(this.Flags)}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.ISRC))
|
||||
sw.WriteLine($"ISRC {this.ISRC}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Performer))
|
||||
sw.WriteLine($"PERFORMER {this.Performer}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Songwriter))
|
||||
sw.WriteLine($"SONGWRITER {this.Songwriter}");
|
||||
|
||||
if (!string.IsNullOrEmpty(this.Title))
|
||||
sw.WriteLine($"TITLE {this.Title}");
|
||||
|
||||
if (this.PreGap != null)
|
||||
this.PreGap.Write(sw);
|
||||
|
||||
foreach (var index in Indices)
|
||||
{
|
||||
index.Write(sw);
|
||||
}
|
||||
|
||||
if (this.PostGap != null)
|
||||
this.PostGap.Write(sw);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the data type from a given string
|
||||
/// </summary>
|
||||
/// <param name="dataType">String to get value from</param>
|
||||
/// <returns>CueTrackDataType, if possible (default AUDIO)</returns>
|
||||
private CueTrackDataType GetDataType(string dataType)
|
||||
{
|
||||
switch (dataType.ToLowerInvariant())
|
||||
{
|
||||
case "audio":
|
||||
return CueTrackDataType.AUDIO;
|
||||
|
||||
case "cdg":
|
||||
return CueTrackDataType.CDG;
|
||||
|
||||
case "mode1/2048":
|
||||
return CueTrackDataType.MODE1_2048;
|
||||
|
||||
case "mode1/2352":
|
||||
return CueTrackDataType.MODE1_2352;
|
||||
|
||||
case "mode2/2336":
|
||||
return CueTrackDataType.MODE2_2336;
|
||||
|
||||
case "mode2/2352":
|
||||
return CueTrackDataType.MODE2_2352;
|
||||
|
||||
case "cdi/2336":
|
||||
return CueTrackDataType.CDI_2336;
|
||||
|
||||
case "cdi/2352":
|
||||
return CueTrackDataType.CDI_2352;
|
||||
|
||||
default:
|
||||
return CueTrackDataType.AUDIO;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string from a given data type
|
||||
/// </summary>
|
||||
/// <param name="dataType">CueTrackDataType to get value from</param>
|
||||
/// <returns>string, if possible</returns>
|
||||
private string FromDataType(CueTrackDataType dataType)
|
||||
{
|
||||
switch (dataType)
|
||||
{
|
||||
case CueTrackDataType.AUDIO:
|
||||
return "AUDIO";
|
||||
|
||||
case CueTrackDataType.CDG:
|
||||
return "CDG";
|
||||
|
||||
case CueTrackDataType.MODE1_2048:
|
||||
return "MODE1/2048";
|
||||
|
||||
case CueTrackDataType.MODE1_2352:
|
||||
return "MODE1/2352";
|
||||
|
||||
case CueTrackDataType.MODE2_2336:
|
||||
return "MODE2/2336";
|
||||
|
||||
case CueTrackDataType.MODE2_2352:
|
||||
return "MODE2/2352";
|
||||
|
||||
case CueTrackDataType.CDI_2336:
|
||||
return "CDI/2336";
|
||||
|
||||
case CueTrackDataType.CDI_2352:
|
||||
return "CDI/2352";
|
||||
|
||||
default:
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the flag value for an array of strings
|
||||
/// </summary>
|
||||
/// <param name="flagStrings">Possible flags as strings</param>
|
||||
/// <returns>CueTrackFlag value representing the strings, if possible</returns>
|
||||
private CueTrackFlag GetFlags(string[] flagStrings)
|
||||
{
|
||||
CueTrackFlag flag = 0;
|
||||
|
||||
foreach (string flagString in flagStrings)
|
||||
{
|
||||
switch (flagString.ToLowerInvariant())
|
||||
{
|
||||
case "flags":
|
||||
// No-op since this is the start of the line
|
||||
break;
|
||||
|
||||
case "dcp":
|
||||
flag |= CueTrackFlag.DCP;
|
||||
break;
|
||||
|
||||
case "4ch":
|
||||
flag |= CueTrackFlag.FourCH;
|
||||
break;
|
||||
|
||||
case "pre":
|
||||
flag |= CueTrackFlag.PRE;
|
||||
break;
|
||||
|
||||
case "scms":
|
||||
flag |= CueTrackFlag.SCMS;
|
||||
break;
|
||||
|
||||
case "data":
|
||||
flag |= CueTrackFlag.DATA;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the string value for a set of track flags
|
||||
/// </summary>
|
||||
/// <param name="flags">CueTrackFlag to get value from</param>
|
||||
/// <returns>String value representing the CueTrackFlag, if possible</returns>
|
||||
private string FromFlags(CueTrackFlag flags)
|
||||
{
|
||||
string outputFlagString = string.Empty;
|
||||
|
||||
if (flags.HasFlag(CueTrackFlag.DCP))
|
||||
outputFlagString += "DCP ";
|
||||
|
||||
if (flags.HasFlag(CueTrackFlag.FourCH))
|
||||
outputFlagString += "4CH ";
|
||||
|
||||
if (flags.HasFlag(CueTrackFlag.PRE))
|
||||
outputFlagString += "PRE ";
|
||||
|
||||
if (flags.HasFlag(CueTrackFlag.SCMS))
|
||||
outputFlagString += "SCMS ";
|
||||
|
||||
if (flags.HasFlag(CueTrackFlag.DATA))
|
||||
outputFlagString += "DATA ";
|
||||
|
||||
return outputFlagString.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2022</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.4</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,139 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace MPF.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents POSTGAP information of a track
|
||||
/// </summary>
|
||||
public class PostGap
|
||||
{
|
||||
/// <summary>
|
||||
/// Length of POSTGAP in minutes
|
||||
/// </summary>
|
||||
public int Minutes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of POSTGAP in seconds
|
||||
/// </summary>
|
||||
/// <remarks>There are 60 seconds in a minute</remarks>
|
||||
public int Seconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of POSTGAP in frames.
|
||||
/// </summary>
|
||||
/// <remarks>There are 75 frames per second</remarks>
|
||||
public int Frames { get; set; }
|
||||
|
||||
/// Create an empty POSTGAP
|
||||
/// </summary>
|
||||
public PostGap()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a POSTGAP from a mm:ss:ff length
|
||||
/// </summary>
|
||||
/// <param name="length">String to get length information from</param>
|
||||
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
|
||||
public PostGap(string length, bool throwOnError = false)
|
||||
{
|
||||
// Ignore empty lines
|
||||
if (string.IsNullOrWhiteSpace(length))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentException("Length was null or whitespace");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore lines that don't contain the correct information
|
||||
if (length.Length != 8 || length.Count(c => c == ':') != 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Length was not in a recognized format: {length}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Split the line
|
||||
string[] splitLength = length.Split(':');
|
||||
if (splitLength.Length != 3)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Length was not in a recognized format: {length}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the lengths
|
||||
int[] lengthSegments = new int[3];
|
||||
|
||||
// Minutes
|
||||
if (!int.TryParse(splitLength[0], out lengthSegments[0]))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Minutes segment was not a number: {splitLength[0]}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (lengthSegments[0] < 0)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Seconds
|
||||
if (!int.TryParse(splitLength[1], out lengthSegments[1]))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Seconds segment was not a number: {splitLength[1]}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Frames
|
||||
if (!int.TryParse(splitLength[2], out lengthSegments[2]))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Frames segment was not a number: {splitLength[2]}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the values
|
||||
this.Minutes = lengthSegments[0];
|
||||
this.Seconds = lengthSegments[1];
|
||||
this.Frames = lengthSegments[2];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the POSTGAP out to a stream
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to write to</param>
|
||||
public void Write(StreamWriter sw)
|
||||
{
|
||||
sw.WriteLine($" POSTGAP {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
/// <remarks>
|
||||
/// Information sourced from http://web.archive.org/web/20070221154246/http://www.goldenhawk.com/download/cdrwin.pdf
|
||||
/// </remarks>
|
||||
namespace MPF.CueSheets
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents PREGAP information of a track
|
||||
/// </summary>
|
||||
public class PreGap
|
||||
{
|
||||
/// <summary>
|
||||
/// Length of PREGAP in minutes
|
||||
/// </summary>
|
||||
public int Minutes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of PREGAP in seconds
|
||||
/// </summary>
|
||||
/// <remarks>There are 60 seconds in a minute</remarks>
|
||||
public int Seconds { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length of PREGAP in frames.
|
||||
/// </summary>
|
||||
/// <remarks>There are 75 frames per second</remarks>
|
||||
public int Frames { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create an empty PREGAP
|
||||
/// </summary>
|
||||
public PreGap()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a PREGAP from a mm:ss:ff length
|
||||
/// </summary>
|
||||
/// <param name="length">String to get length information from</param>
|
||||
/// <param name="throwOnError">True if errors throw an exception, false otherwise</param>
|
||||
public PreGap(string length, bool throwOnError = false)
|
||||
{
|
||||
// Ignore empty lines
|
||||
if (string.IsNullOrWhiteSpace(length))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new ArgumentException("Length was null or whitespace");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore lines that don't contain the correct information
|
||||
if (length.Length != 8 || length.Count(c => c == ':') != 2)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Length was not in a recognized format: {length}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Split the line
|
||||
string[] splitLength = length.Split(':');
|
||||
if (splitLength.Length != 3)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Length was not in a recognized format: {length}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the lengths
|
||||
int[] lengthSegments = new int[3];
|
||||
|
||||
// Minutes
|
||||
if (!int.TryParse(splitLength[0], out lengthSegments[0]))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Minutes segment was not a number: {splitLength[0]}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (lengthSegments[0] < 0)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Minutes segment must be 0 or greater: {lengthSegments[0]}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Seconds
|
||||
if (!int.TryParse(splitLength[1], out lengthSegments[1]))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Seconds segment was not a number: {splitLength[1]}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (lengthSegments[1] < 0 || lengthSegments[1] > 60)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Seconds segment must be between 0 and 60: {lengthSegments[1]}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Frames
|
||||
if (!int.TryParse(splitLength[2], out lengthSegments[2]))
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new FormatException($"Frames segment was not a number: {splitLength[2]}");
|
||||
|
||||
return;
|
||||
}
|
||||
else if (lengthSegments[2] < 0 || lengthSegments[2] > 75)
|
||||
{
|
||||
if (throwOnError)
|
||||
throw new IndexOutOfRangeException($"Frames segment must be between 0 and 75: {lengthSegments[2]}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the values
|
||||
this.Minutes = lengthSegments[0];
|
||||
this.Seconds = lengthSegments[1];
|
||||
this.Frames = lengthSegments[2];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write the PREGAP out to a stream
|
||||
/// </summary>
|
||||
/// <param name="sw">StreamWriter to write to</param>
|
||||
public void Write(StreamWriter sw)
|
||||
{
|
||||
sw.WriteLine($" PREGAP {this.Minutes:D2}:{this.Seconds:D2}:{this.Frames:D2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Anything marked as internal can be used by the test methods
|
||||
[assembly: InternalsVisibleTo("MPF.Test")]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,48 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<AssemblyName>MPF.Library</AssemblyName>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2022</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.4</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.3.4" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
|
||||
<PackageReference Include="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>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,4 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Anything marked as internal can be used by the test methods
|
||||
[assembly: InternalsVisibleTo("MPF.Test")]
|
||||
@@ -1,32 +0,0 @@
|
||||
namespace MPF.Modules.DD
|
||||
{
|
||||
/// <summary>
|
||||
/// Top-level commands for DD
|
||||
/// </summary>
|
||||
public static class CommandStrings
|
||||
{
|
||||
public const string NONE = "";
|
||||
public const string List = "--list";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dumping flags for DD
|
||||
/// </summary>
|
||||
public static class FlagStrings
|
||||
{
|
||||
// Boolean flags
|
||||
public const string Progress = "--progress";
|
||||
public const string Size = "--size";
|
||||
|
||||
// Int64 flags
|
||||
public const string BlockSize = "bs";
|
||||
public const string Count = "count";
|
||||
public const string Seek = "seek";
|
||||
public const string Skip = "skip";
|
||||
|
||||
// String flags
|
||||
public const string Filter = "--filter";
|
||||
public const string InputFile = "if";
|
||||
public const string OutputFile = "of";
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.DD
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
#region Cross-enumeration conversions
|
||||
|
||||
/// <summary>
|
||||
/// Get the default extension for a given disc type
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType value to check</param>
|
||||
/// <returns>Valid extension (with leading '.'), null on error</returns>
|
||||
public static string Extension(MediaType? type)
|
||||
{
|
||||
// DD has a single, unified output format by default
|
||||
return ".bin";
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,410 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.DD
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic set of DD parameters
|
||||
/// </summary>
|
||||
public class Parameters : BaseParameters
|
||||
{
|
||||
#region Generic Dumping Information
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string InputPath => InputFileValue?.TrimStart('\\', '?');
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string OutputPath => OutputFileValue;
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <inheritdoc/>
|
||||
public override int? Speed
|
||||
{
|
||||
get { return 1; }
|
||||
set { }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Metadata
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override InternalProgram InternalProgram => InternalProgram.DD;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Flag Values
|
||||
|
||||
public long? BlockSizeValue { get; set; }
|
||||
|
||||
public long? CountValue { get; set; }
|
||||
|
||||
// fixed, removable, disk, partition
|
||||
public string FilterValue { get; set; }
|
||||
|
||||
public string InputFileValue { get; set; }
|
||||
|
||||
public string OutputFileValue { get; set; }
|
||||
|
||||
public long? SeekValue { get; set; }
|
||||
|
||||
public long? SkipValue { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(string parameters) : base(parameters) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, driveLetter, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
#region BaseParameters Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
// TODO: Figure out what sort of output files are expected... just `.bin`?
|
||||
return (true, new List<string>());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
// TODO: Fill in submission info specifics for DD
|
||||
string outputDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
// TODO: Determine if there's a DD version anywhere
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
|
||||
switch (this.Type)
|
||||
{
|
||||
// Determine type-specific differences
|
||||
}
|
||||
|
||||
switch (this.System)
|
||||
{
|
||||
case RedumpSystem.KonamiPython2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GenerateParameters()
|
||||
{
|
||||
List<string> parameters = new List<string>();
|
||||
|
||||
if (BaseCommand == null)
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
if (!string.IsNullOrEmpty(BaseCommand))
|
||||
parameters.Add(BaseCommand);
|
||||
|
||||
#region Boolean flags
|
||||
|
||||
// Progress
|
||||
if (IsFlagSupported(FlagStrings.Progress))
|
||||
{
|
||||
if (this[FlagStrings.Progress] == true)
|
||||
parameters.Add($"{FlagStrings.Progress}");
|
||||
}
|
||||
|
||||
// Size
|
||||
if (IsFlagSupported(FlagStrings.Size))
|
||||
{
|
||||
if (this[FlagStrings.Size] == true)
|
||||
parameters.Add($"{FlagStrings.Size}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Int64 flags
|
||||
|
||||
// Block Size
|
||||
if (IsFlagSupported(FlagStrings.BlockSize))
|
||||
{
|
||||
if (this[FlagStrings.BlockSize] == true && BlockSizeValue != null)
|
||||
parameters.Add($"{FlagStrings.BlockSize}={BlockSizeValue}");
|
||||
}
|
||||
|
||||
// Count
|
||||
if (IsFlagSupported(FlagStrings.Count))
|
||||
{
|
||||
if (this[FlagStrings.Count] == true && CountValue != null)
|
||||
parameters.Add($"{FlagStrings.Count}={CountValue}");
|
||||
}
|
||||
|
||||
// Seek
|
||||
if (IsFlagSupported(FlagStrings.Seek))
|
||||
{
|
||||
if (this[FlagStrings.Seek] == true && SeekValue != null)
|
||||
parameters.Add($"{FlagStrings.Seek}={SeekValue}");
|
||||
}
|
||||
|
||||
// Skip
|
||||
if (IsFlagSupported(FlagStrings.Skip))
|
||||
{
|
||||
if (this[FlagStrings.Skip] == true && SkipValue != null)
|
||||
parameters.Add($"{FlagStrings.Skip}={SkipValue}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String flags
|
||||
|
||||
// Filter
|
||||
if (IsFlagSupported(FlagStrings.Filter))
|
||||
{
|
||||
if (this[FlagStrings.Filter] == true && FilterValue != null)
|
||||
parameters.Add($"{FlagStrings.Filter}={FilterValue}");
|
||||
}
|
||||
|
||||
// Input File
|
||||
if (IsFlagSupported(FlagStrings.InputFile))
|
||||
{
|
||||
if (this[FlagStrings.InputFile] == true && InputFileValue != null)
|
||||
parameters.Add($"{FlagStrings.InputFile}=\"{InputFileValue}\"");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
// Output File
|
||||
if (IsFlagSupported(FlagStrings.OutputFile))
|
||||
{
|
||||
if (this[FlagStrings.OutputFile] == true && OutputFileValue != null)
|
||||
parameters.Add($"{FlagStrings.OutputFile}=\"{OutputFileValue}\"");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return string.Join(" ", parameters);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Dictionary<string, List<string>> GetCommandSupport()
|
||||
{
|
||||
return new Dictionary<string, List<string>>()
|
||||
{
|
||||
[CommandStrings.NONE] = new List<string>()
|
||||
{
|
||||
FlagStrings.BlockSize,
|
||||
FlagStrings.Count,
|
||||
FlagStrings.Filter,
|
||||
FlagStrings.InputFile,
|
||||
FlagStrings.OutputFile,
|
||||
FlagStrings.Progress,
|
||||
FlagStrings.Seek,
|
||||
FlagStrings.Size,
|
||||
FlagStrings.Skip,
|
||||
},
|
||||
|
||||
[CommandStrings.List] = new List<string>()
|
||||
{
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsDumpingCommand()
|
||||
{
|
||||
switch (this.BaseCommand)
|
||||
{
|
||||
case CommandStrings.List:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void ResetValues()
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
flags = new Dictionary<string, bool?>();
|
||||
|
||||
BlockSizeValue = null;
|
||||
CountValue = null;
|
||||
InputFileValue = null;
|
||||
OutputFileValue = null;
|
||||
SeekValue = null;
|
||||
SkipValue = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
this[FlagStrings.InputFile] = true;
|
||||
InputFileValue = $"\\\\?\\{driveLetter}:";
|
||||
|
||||
this[FlagStrings.OutputFile] = true;
|
||||
OutputFileValue = filename;
|
||||
|
||||
// TODO: Add more common block sizes
|
||||
this[FlagStrings.BlockSize] = true;
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.FloppyDisk:
|
||||
BlockSizeValue = 1440 * 1024;
|
||||
break;
|
||||
|
||||
default:
|
||||
BlockSizeValue = 1024 * 1024 * 1024;
|
||||
break;
|
||||
}
|
||||
|
||||
this[FlagStrings.Progress] = true;
|
||||
this[FlagStrings.Size] = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool ValidateAndSetParameters(string parameters)
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
// The string has to be valid by itself first
|
||||
if (string.IsNullOrWhiteSpace(parameters))
|
||||
return false;
|
||||
|
||||
// Now split the string into parts for easier validation
|
||||
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
|
||||
parameters = parameters.Trim();
|
||||
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+")
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Value)
|
||||
.ToList();
|
||||
|
||||
// Determine what the commandline should look like given the first item
|
||||
int start = 0;
|
||||
if (parts[0] == CommandStrings.List)
|
||||
{
|
||||
BaseCommand = parts[0];
|
||||
start = 1;
|
||||
}
|
||||
|
||||
// Loop through all auxilary flags, if necessary
|
||||
for (int i = start; i < parts.Count; i++)
|
||||
{
|
||||
// Flag read-out values
|
||||
long? longValue = null;
|
||||
string stringValue = null;
|
||||
|
||||
// Keep a count of keys to determine if we should break out to filename handling or not
|
||||
int keyCount = Keys.Count();
|
||||
|
||||
#region Boolean flags
|
||||
|
||||
// Progress
|
||||
ProcessFlagParameter(parts, FlagStrings.Progress, ref i);
|
||||
|
||||
// Size
|
||||
ProcessFlagParameter(parts, FlagStrings.Size, ref i);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Int64 flags
|
||||
|
||||
// Block Size
|
||||
longValue = ProcessInt64Parameter(parts, FlagStrings.BlockSize, ref i);
|
||||
if (longValue != null)
|
||||
BlockSizeValue = longValue;
|
||||
|
||||
// Count
|
||||
longValue = ProcessInt64Parameter(parts, FlagStrings.Count, ref i);
|
||||
if (longValue != null)
|
||||
CountValue = longValue;
|
||||
|
||||
// Seek
|
||||
longValue = ProcessInt64Parameter(parts, FlagStrings.Seek, ref i);
|
||||
if (longValue != null)
|
||||
SeekValue = longValue;
|
||||
|
||||
// Skip
|
||||
longValue = ProcessInt64Parameter(parts, FlagStrings.Skip, ref i);
|
||||
if (longValue != null)
|
||||
SkipValue = longValue;
|
||||
|
||||
#endregion
|
||||
|
||||
#region String flags
|
||||
|
||||
// Filter (fixed, removable, disk, partition)
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.Filter, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
FilterValue = stringValue;
|
||||
|
||||
// Input File
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.InputFile, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
InputFileValue = stringValue;
|
||||
|
||||
// Output File
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.OutputFile, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
OutputFileValue = stringValue;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2022</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.4</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.CueSheets\MPF.CueSheets.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<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>
|
||||
@@ -1,22 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -1,863 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.Redumper
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic set of Redumper parameters
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TODO: Redumper natively supports multiple, chained commands but MPF
|
||||
/// doesn't have an easy way of dealing with that right now. Maybe have
|
||||
/// BaseCommand be a space-deliminated list of the commands?
|
||||
/// TODO: With chained commands, how do we tell what parameters are
|
||||
/// actually supported? Do we go one by one and if it doesn't match
|
||||
/// any, then it's incorrect?
|
||||
/// </remarks>
|
||||
public class Parameters : BaseParameters
|
||||
{
|
||||
#region Generic Dumping Information
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string InputPath => DriveValue;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string OutputPath => Path.Combine(ImagePathValue ?? string.Empty, ImageNameValue ?? string.Empty);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int? Speed => SpeedValue;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Metadata
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override InternalProgram InternalProgram => InternalProgram.Redumper;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Flag Values
|
||||
|
||||
/// <summary>
|
||||
/// Maximum absolute sample value to treat it as silence (default: 32)
|
||||
/// </summary>
|
||||
public int? AudioSilenceThresholdValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Drive to use, first available drive with disc, if not provided
|
||||
/// </summary>
|
||||
public string DriveValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Override offset autodetection and use supplied value
|
||||
/// </summary>
|
||||
public int? ForceOffsetValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dump files prefix, autogenerated in dump mode, if not provided
|
||||
/// </summary>
|
||||
public string ImageNameValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dump files base directory
|
||||
/// </summary>
|
||||
public string ImagePathValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of sector retries in case of SCSI/C2 error (default: 0)
|
||||
/// </summary>
|
||||
public int? RetriesValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rings mode, maximum ring size to stop subdivision (rings, default: 1024)
|
||||
/// </summary>
|
||||
public int? RingSizeValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// LBA ranges of sectors to skip
|
||||
/// </summary>
|
||||
public string SkipValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fill byte value for skipped sectors (default: 0x55)
|
||||
/// </summary>
|
||||
public byte? SkipFillValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Rings mode, number of sectors to skip on SCSI error (default: 4096)
|
||||
/// </summary>
|
||||
public int? SkipSizeValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Drive read speed, optimal drive speed will be used if not provided
|
||||
/// </summary>
|
||||
public int? SpeedValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// LBA to stop dumping at (everything before the value, useful for discs with fake TOC
|
||||
/// </summary>
|
||||
public int? StopLBAValue { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(string parameters) : base(parameters) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, driveLetter, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
#region BaseParameters Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
// TODO: Fill out
|
||||
return (true, new List<string>());
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
// TODO: Fill in submission info specifics for Redumper
|
||||
string outputDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
// TODO: Determine if there's a Redumper version anywhere
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
|
||||
switch (this.Type)
|
||||
{
|
||||
// Determine type-specific differences
|
||||
}
|
||||
|
||||
switch (this.System)
|
||||
{
|
||||
case RedumpSystem.KonamiPython2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string pythonTwoSerial, out Region? pythonTwoRegion, out string pythonTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationSerial, out Region? playstationRegion, out string playstationDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
if (GetPlayStationExecutableInfo(drive?.Letter, out string playstationTwoSerial, out Region? playstationTwoRegion, out string playstationTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GenerateParameters()
|
||||
{
|
||||
List<string> parameters = new List<string>();
|
||||
|
||||
if (BaseCommand == null)
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(BaseCommand))
|
||||
parameters.Add(BaseCommand);
|
||||
else
|
||||
return null;
|
||||
|
||||
// Audio Silence Threshold
|
||||
if (IsFlagSupported(FlagStrings.AudioSilenceThreshold))
|
||||
{
|
||||
if (this[FlagStrings.AudioSilenceThreshold] == true)
|
||||
{
|
||||
if (AudioSilenceThresholdValue != null && AudioSilenceThresholdValue >= 0)
|
||||
parameters.Add($"{FlagStrings.AudioSilenceThreshold}={AudioSilenceThresholdValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// CD-i Correct Offset
|
||||
if (IsFlagSupported(FlagStrings.CDiCorrectOffset))
|
||||
{
|
||||
if (this[FlagStrings.CDiCorrectOffset] == true)
|
||||
parameters.Add(FlagStrings.CDiCorrectOffset);
|
||||
}
|
||||
|
||||
// CD-i Ready Normalize
|
||||
if (IsFlagSupported(FlagStrings.CDiReadyNormalize))
|
||||
{
|
||||
if (this[FlagStrings.CDiReadyNormalize] == true)
|
||||
parameters.Add(FlagStrings.CDiReadyNormalize);
|
||||
}
|
||||
|
||||
// Descramble New
|
||||
if (IsFlagSupported(FlagStrings.DescrambleNew))
|
||||
{
|
||||
if (this[FlagStrings.DescrambleNew] == true)
|
||||
parameters.Add(FlagStrings.DescrambleNew);
|
||||
}
|
||||
|
||||
// Drive
|
||||
if (IsFlagSupported(FlagStrings.Drive))
|
||||
{
|
||||
if (this[FlagStrings.Drive] == true)
|
||||
{
|
||||
if (DriveValue != null)
|
||||
parameters.Add($"{FlagStrings.Drive}={DriveValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// ForceOffset
|
||||
if (IsFlagSupported(FlagStrings.ForceOffset))
|
||||
{
|
||||
if (this[FlagStrings.ForceOffset] == true)
|
||||
{
|
||||
if (ForceOffsetValue != null)
|
||||
parameters.Add($"{FlagStrings.ForceOffset}={ForceOffsetValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Force QTOC
|
||||
if (IsFlagSupported(FlagStrings.ForceQTOC))
|
||||
{
|
||||
if (this[FlagStrings.ForceQTOC] == true)
|
||||
parameters.Add(FlagStrings.ForceQTOC);
|
||||
}
|
||||
|
||||
// Force Split
|
||||
if (IsFlagSupported(FlagStrings.ForceSplit))
|
||||
{
|
||||
if (this[FlagStrings.ForceSplit] == true)
|
||||
parameters.Add(FlagStrings.ForceSplit);
|
||||
}
|
||||
|
||||
// Force TOC
|
||||
if (IsFlagSupported(FlagStrings.ForceTOC))
|
||||
{
|
||||
if (this[FlagStrings.ForceTOC] == true)
|
||||
parameters.Add(FlagStrings.ForceTOC);
|
||||
}
|
||||
|
||||
// Help
|
||||
if (IsFlagSupported(FlagStrings.HelpLong))
|
||||
{
|
||||
if (this[FlagStrings.HelpLong] == true)
|
||||
parameters.Add(FlagStrings.HelpLong);
|
||||
}
|
||||
|
||||
// ISO9660 Trim
|
||||
if (IsFlagSupported(FlagStrings.ISO9660Trim))
|
||||
{
|
||||
if (this[FlagStrings.ISO9660Trim] == true)
|
||||
parameters.Add(FlagStrings.ISO9660Trim);
|
||||
}
|
||||
|
||||
// Image Name
|
||||
if (IsFlagSupported(FlagStrings.ImageName))
|
||||
{
|
||||
if (this[FlagStrings.ImageName] == true)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ImageNameValue))
|
||||
parameters.Add($"{FlagStrings.ImageName}={ImageNameValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Image Path
|
||||
if (IsFlagSupported(FlagStrings.ImagePath))
|
||||
{
|
||||
if (this[FlagStrings.ImagePath] == true)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ImagePathValue))
|
||||
parameters.Add($"{FlagStrings.ImagePath}={ImagePathValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Leave Unchanged
|
||||
if (IsFlagSupported(FlagStrings.LeaveUnchanged))
|
||||
{
|
||||
if (this[FlagStrings.LeaveUnchanged] == true)
|
||||
parameters.Add(FlagStrings.LeaveUnchanged);
|
||||
}
|
||||
|
||||
// Overwrite
|
||||
if (IsFlagSupported(FlagStrings.Overwrite))
|
||||
{
|
||||
if (this[FlagStrings.Overwrite] == true)
|
||||
parameters.Add(FlagStrings.Overwrite);
|
||||
}
|
||||
|
||||
// Refine Subchannel
|
||||
if (IsFlagSupported(FlagStrings.RefineSubchannel))
|
||||
{
|
||||
if (this[FlagStrings.RefineSubchannel] == true)
|
||||
parameters.Add(FlagStrings.RefineSubchannel);
|
||||
}
|
||||
|
||||
// Retries
|
||||
if (IsFlagSupported(FlagStrings.Retries))
|
||||
{
|
||||
if (this[FlagStrings.Retries] == true)
|
||||
{
|
||||
if (RetriesValue != null && RetriesValue >= 0)
|
||||
parameters.Add($"{FlagStrings.Retries}={RetriesValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Ring Size
|
||||
if (IsFlagSupported(FlagStrings.RingSize))
|
||||
{
|
||||
if (this[FlagStrings.RingSize] == true)
|
||||
{
|
||||
if (RingSizeValue != null && RingSizeValue >= 0)
|
||||
parameters.Add($"{FlagStrings.RingSize}={RingSizeValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip
|
||||
if (IsFlagSupported(FlagStrings.Skip))
|
||||
{
|
||||
if (this[FlagStrings.Skip] == true)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(SkipValue))
|
||||
parameters.Add($"{FlagStrings.Skip}={SkipValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip Fill
|
||||
if (IsFlagSupported(FlagStrings.SkipFill))
|
||||
{
|
||||
if (this[FlagStrings.SkipFill] == true)
|
||||
{
|
||||
if (SkipFillValue != null && SkipFillValue >= 0)
|
||||
parameters.Add($"{FlagStrings.SkipFill}={SkipFillValue:x}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip Lead-In
|
||||
if (IsFlagSupported(FlagStrings.SkipLeadIn))
|
||||
{
|
||||
if (this[FlagStrings.SkipLeadIn] == true)
|
||||
parameters.Add(FlagStrings.SkipLeadIn);
|
||||
}
|
||||
|
||||
// Skip Size
|
||||
if (IsFlagSupported(FlagStrings.SkipSize))
|
||||
{
|
||||
if (this[FlagStrings.SkipSize] == true)
|
||||
{
|
||||
if (SkipSizeValue != null && SkipSizeValue >= 0)
|
||||
parameters.Add($"{FlagStrings.SkipSize}={SkipSizeValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Speed
|
||||
if (IsFlagSupported(FlagStrings.Speed))
|
||||
{
|
||||
if (this[FlagStrings.Speed] == true)
|
||||
{
|
||||
if (SpeedValue != null && SkipSizeValue >= 1)
|
||||
parameters.Add($"{FlagStrings.Speed}={SpeedValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Stop LBA
|
||||
if (IsFlagSupported(FlagStrings.StopLBA))
|
||||
{
|
||||
if (this[FlagStrings.StopLBA] == true)
|
||||
{
|
||||
if (StopLBAValue != null)
|
||||
parameters.Add($"{FlagStrings.StopLBA}={StopLBAValue}");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Unsupported
|
||||
if (IsFlagSupported(FlagStrings.Unsupported))
|
||||
{
|
||||
if (this[FlagStrings.Unsupported] == true)
|
||||
parameters.Add(FlagStrings.Unsupported);
|
||||
}
|
||||
|
||||
// Verbose
|
||||
if (IsFlagSupported(FlagStrings.Verbose))
|
||||
{
|
||||
if (this[FlagStrings.Verbose] == true)
|
||||
parameters.Add(FlagStrings.Verbose);
|
||||
}
|
||||
|
||||
return string.Join(" ", parameters);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Dictionary<string, List<string>> GetCommandSupport()
|
||||
{
|
||||
// TODO: Figure out actual support for each flag
|
||||
return new Dictionary<string, List<string>>()
|
||||
{
|
||||
[CommandStrings.NONE] = new List<string>()
|
||||
{
|
||||
FlagStrings.HelpLong,
|
||||
FlagStrings.HelpShort,
|
||||
},
|
||||
[CommandStrings.CD] = new List<string>()
|
||||
{
|
||||
FlagStrings.AudioSilenceThreshold,
|
||||
FlagStrings.CDiCorrectOffset,
|
||||
FlagStrings.CDiReadyNormalize,
|
||||
FlagStrings.DescrambleNew,
|
||||
FlagStrings.Drive,
|
||||
FlagStrings.ForceOffset,
|
||||
FlagStrings.ForceQTOC,
|
||||
FlagStrings.ForceSplit,
|
||||
FlagStrings.ForceTOC,
|
||||
FlagStrings.ISO9660Trim,
|
||||
FlagStrings.ImageName,
|
||||
FlagStrings.ImagePath,
|
||||
FlagStrings.LeaveUnchanged,
|
||||
FlagStrings.Overwrite,
|
||||
FlagStrings.RefineSubchannel,
|
||||
FlagStrings.Retries,
|
||||
FlagStrings.RingSize,
|
||||
FlagStrings.Skip,
|
||||
FlagStrings.SkipFill,
|
||||
FlagStrings.SkipLeadIn,
|
||||
FlagStrings.SkipSize,
|
||||
FlagStrings.Speed,
|
||||
FlagStrings.StopLBA,
|
||||
FlagStrings.Unsupported,
|
||||
FlagStrings.Verbose,
|
||||
},
|
||||
[CommandStrings.Dump] = new List<string>()
|
||||
{
|
||||
FlagStrings.AudioSilenceThreshold,
|
||||
FlagStrings.CDiCorrectOffset,
|
||||
FlagStrings.CDiReadyNormalize,
|
||||
FlagStrings.DescrambleNew,
|
||||
FlagStrings.Drive,
|
||||
FlagStrings.ForceOffset,
|
||||
FlagStrings.ForceQTOC,
|
||||
FlagStrings.ForceSplit,
|
||||
FlagStrings.ForceTOC,
|
||||
FlagStrings.ISO9660Trim,
|
||||
FlagStrings.ImageName,
|
||||
FlagStrings.ImagePath,
|
||||
FlagStrings.LeaveUnchanged,
|
||||
FlagStrings.Overwrite,
|
||||
FlagStrings.RefineSubchannel,
|
||||
FlagStrings.Retries,
|
||||
FlagStrings.RingSize,
|
||||
FlagStrings.Skip,
|
||||
FlagStrings.SkipFill,
|
||||
FlagStrings.SkipLeadIn,
|
||||
FlagStrings.SkipSize,
|
||||
FlagStrings.Speed,
|
||||
FlagStrings.StopLBA,
|
||||
FlagStrings.Unsupported,
|
||||
FlagStrings.Verbose,
|
||||
},
|
||||
[CommandStrings.Info] = new List<string>()
|
||||
{
|
||||
FlagStrings.AudioSilenceThreshold,
|
||||
FlagStrings.CDiCorrectOffset,
|
||||
FlagStrings.CDiReadyNormalize,
|
||||
FlagStrings.DescrambleNew,
|
||||
FlagStrings.Drive,
|
||||
FlagStrings.ForceOffset,
|
||||
FlagStrings.ForceQTOC,
|
||||
FlagStrings.ForceSplit,
|
||||
FlagStrings.ForceTOC,
|
||||
FlagStrings.ISO9660Trim,
|
||||
FlagStrings.ImageName,
|
||||
FlagStrings.ImagePath,
|
||||
FlagStrings.LeaveUnchanged,
|
||||
FlagStrings.Overwrite,
|
||||
FlagStrings.RefineSubchannel,
|
||||
FlagStrings.Retries,
|
||||
FlagStrings.RingSize,
|
||||
FlagStrings.Skip,
|
||||
FlagStrings.SkipFill,
|
||||
FlagStrings.SkipLeadIn,
|
||||
FlagStrings.SkipSize,
|
||||
FlagStrings.Speed,
|
||||
FlagStrings.StopLBA,
|
||||
FlagStrings.Unsupported,
|
||||
FlagStrings.Verbose,
|
||||
},
|
||||
[CommandStrings.Protection] = new List<string>()
|
||||
{
|
||||
FlagStrings.AudioSilenceThreshold,
|
||||
FlagStrings.CDiCorrectOffset,
|
||||
FlagStrings.CDiReadyNormalize,
|
||||
FlagStrings.DescrambleNew,
|
||||
FlagStrings.Drive,
|
||||
FlagStrings.ForceOffset,
|
||||
FlagStrings.ForceQTOC,
|
||||
FlagStrings.ForceSplit,
|
||||
FlagStrings.ForceTOC,
|
||||
FlagStrings.ISO9660Trim,
|
||||
FlagStrings.ImageName,
|
||||
FlagStrings.ImagePath,
|
||||
FlagStrings.LeaveUnchanged,
|
||||
FlagStrings.Overwrite,
|
||||
FlagStrings.RefineSubchannel,
|
||||
FlagStrings.Retries,
|
||||
FlagStrings.RingSize,
|
||||
FlagStrings.Skip,
|
||||
FlagStrings.SkipFill,
|
||||
FlagStrings.SkipLeadIn,
|
||||
FlagStrings.SkipSize,
|
||||
FlagStrings.Speed,
|
||||
FlagStrings.StopLBA,
|
||||
FlagStrings.Unsupported,
|
||||
FlagStrings.Verbose,
|
||||
},
|
||||
[CommandStrings.Refine] = new List<string>()
|
||||
{
|
||||
FlagStrings.AudioSilenceThreshold,
|
||||
FlagStrings.CDiCorrectOffset,
|
||||
FlagStrings.CDiReadyNormalize,
|
||||
FlagStrings.DescrambleNew,
|
||||
FlagStrings.Drive,
|
||||
FlagStrings.ForceOffset,
|
||||
FlagStrings.ForceQTOC,
|
||||
FlagStrings.ForceSplit,
|
||||
FlagStrings.ForceTOC,
|
||||
FlagStrings.ISO9660Trim,
|
||||
FlagStrings.ImageName,
|
||||
FlagStrings.ImagePath,
|
||||
FlagStrings.LeaveUnchanged,
|
||||
FlagStrings.Overwrite,
|
||||
FlagStrings.RefineSubchannel,
|
||||
FlagStrings.Retries,
|
||||
FlagStrings.RingSize,
|
||||
FlagStrings.Skip,
|
||||
FlagStrings.SkipFill,
|
||||
FlagStrings.SkipLeadIn,
|
||||
FlagStrings.SkipSize,
|
||||
FlagStrings.Speed,
|
||||
FlagStrings.StopLBA,
|
||||
FlagStrings.Unsupported,
|
||||
FlagStrings.Verbose,
|
||||
},
|
||||
[CommandStrings.Split] = new List<string>()
|
||||
{
|
||||
FlagStrings.AudioSilenceThreshold,
|
||||
FlagStrings.CDiCorrectOffset,
|
||||
FlagStrings.CDiReadyNormalize,
|
||||
FlagStrings.DescrambleNew,
|
||||
FlagStrings.Drive,
|
||||
FlagStrings.ForceOffset,
|
||||
FlagStrings.ForceQTOC,
|
||||
FlagStrings.ForceSplit,
|
||||
FlagStrings.ForceTOC,
|
||||
FlagStrings.ISO9660Trim,
|
||||
FlagStrings.ImageName,
|
||||
FlagStrings.ImagePath,
|
||||
FlagStrings.LeaveUnchanged,
|
||||
FlagStrings.Overwrite,
|
||||
FlagStrings.RefineSubchannel,
|
||||
FlagStrings.Retries,
|
||||
FlagStrings.RingSize,
|
||||
FlagStrings.Skip,
|
||||
FlagStrings.SkipFill,
|
||||
FlagStrings.SkipLeadIn,
|
||||
FlagStrings.SkipSize,
|
||||
FlagStrings.Speed,
|
||||
FlagStrings.StopLBA,
|
||||
FlagStrings.Unsupported,
|
||||
FlagStrings.Verbose,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
{
|
||||
List<string> logFiles = new List<string>();
|
||||
|
||||
// TODO: Determine output logfiles for Redumper
|
||||
|
||||
return logFiles;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsDumpingCommand()
|
||||
{
|
||||
switch (this.BaseCommand)
|
||||
{
|
||||
case CommandStrings.CD:
|
||||
case CommandStrings.Dump:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void ResetValues()
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
flags = new Dictionary<string, bool?>();
|
||||
|
||||
AudioSilenceThresholdValue = null;
|
||||
DriveValue = null;
|
||||
ForceOffsetValue = null;
|
||||
ImageNameValue = null;
|
||||
ImagePathValue = null;
|
||||
RetriesValue = null;
|
||||
RingSizeValue = null;
|
||||
SkipValue = null;
|
||||
SkipFillValue = null;
|
||||
SkipSizeValue = null;
|
||||
SpeedValue = null;
|
||||
StopLBAValue = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
{
|
||||
// TODO: Fill out
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool ValidateAndSetParameters(string parameters)
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
// The string has to be valid by itself first
|
||||
if (string.IsNullOrWhiteSpace(parameters))
|
||||
return false;
|
||||
|
||||
// Now split the string into parts for easier validation
|
||||
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
|
||||
parameters = parameters.Trim();
|
||||
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+")
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Value)
|
||||
.ToList();
|
||||
|
||||
// Determine what the commandline should look like given the first item
|
||||
BaseCommand = parts[0];
|
||||
|
||||
int index = 0;
|
||||
switch (BaseCommand)
|
||||
{
|
||||
// These two are technically CommandStrings.NONE
|
||||
case FlagStrings.HelpLong:
|
||||
case FlagStrings.HelpShort:
|
||||
|
||||
// Normal commands
|
||||
case CommandStrings.CD:
|
||||
case CommandStrings.Dump:
|
||||
case CommandStrings.Info:
|
||||
case CommandStrings.Protection:
|
||||
case CommandStrings.Refine:
|
||||
case CommandStrings.Split:
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Loop through all auxiliary flags, if necessary
|
||||
for (int i = index; i < parts.Count; i++)
|
||||
{
|
||||
// Flag read-out values
|
||||
byte? byteValue = null;
|
||||
int? intValue = null;
|
||||
string stringValue = null;
|
||||
|
||||
// Audio Silence Threshold
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.AudioSilenceThreshold, ref i);
|
||||
if (intValue != null)
|
||||
{
|
||||
if (intValue >= 0)
|
||||
AudioSilenceThresholdValue = intValue;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// CD-i Correct Offset
|
||||
ProcessFlagParameter(parts, FlagStrings.CDiCorrectOffset, ref i);
|
||||
|
||||
// CD-i Ready Normalize
|
||||
ProcessFlagParameter(parts, FlagStrings.CDiReadyNormalize, ref i);
|
||||
|
||||
// Descramble New
|
||||
ProcessFlagParameter(parts, FlagStrings.DescrambleNew, ref i);
|
||||
|
||||
// Drive -- TODO: No drive is technically supported
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.Drive, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
DriveValue = stringValue;
|
||||
|
||||
// Force Offset
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.ForceOffset, ref i);
|
||||
if (intValue != null)
|
||||
ForceOffsetValue = intValue;
|
||||
|
||||
// Force QTOC
|
||||
ProcessFlagParameter(parts, FlagStrings.ForceQTOC, ref i);
|
||||
|
||||
// Force Split
|
||||
ProcessFlagParameter(parts, FlagStrings.ForceSplit, ref i);
|
||||
|
||||
// Force TOC
|
||||
ProcessFlagParameter(parts, FlagStrings.ForceTOC, ref i);
|
||||
|
||||
// Help
|
||||
ProcessFlagParameter(parts, FlagStrings.HelpShort, FlagStrings.HelpLong, ref i);
|
||||
|
||||
// ISO9660 Trim
|
||||
ProcessFlagParameter(parts, FlagStrings.ISO9660Trim, ref i);
|
||||
|
||||
// Image Name -- TODO: Empty image name technically supported
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.ImageName, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
ImageNameValue = stringValue;
|
||||
|
||||
// Image Path -- TODO: Empty image path technically supported
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.ImagePath, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
ImagePathValue = stringValue;
|
||||
|
||||
// Leave Unchanged
|
||||
ProcessFlagParameter(parts, FlagStrings.LeaveUnchanged, ref i);
|
||||
|
||||
// Overwrite
|
||||
ProcessFlagParameter(parts, FlagStrings.Overwrite, ref i);
|
||||
|
||||
// Refine Subchannel
|
||||
ProcessFlagParameter(parts, FlagStrings.RefineSubchannel, ref i);
|
||||
|
||||
// Retries
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.Retries, ref i);
|
||||
if (intValue != null)
|
||||
{
|
||||
if (intValue >= 0)
|
||||
RetriesValue = intValue;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ring Size
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.RingSize, ref i);
|
||||
if (intValue != null)
|
||||
{
|
||||
if (intValue >= 0)
|
||||
RetriesValue = intValue;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip -- TODO: Validate how this value should look
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.Skip, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
SkipValue = stringValue;
|
||||
|
||||
// Skip Fill
|
||||
byteValue = ProcessUInt8Parameter(parts, FlagStrings.RingSize, ref i);
|
||||
if (byteValue != null)
|
||||
SkipFillValue = byteValue;
|
||||
|
||||
// Skip Lead-In
|
||||
ProcessFlagParameter(parts, FlagStrings.SkipLeadIn, ref i);
|
||||
|
||||
// Skip Size
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.SkipSize, ref i);
|
||||
if (intValue != null)
|
||||
{
|
||||
if (intValue >= 0)
|
||||
SkipSizeValue = intValue;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Speed
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.Speed, ref i);
|
||||
if (intValue != null)
|
||||
{
|
||||
if (intValue >= 1)
|
||||
Speed = intValue;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stop LBA
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.StopLBA, ref i);
|
||||
if (intValue != null)
|
||||
StopLBAValue = intValue;
|
||||
|
||||
// Unsupported
|
||||
ProcessFlagParameter(parts, FlagStrings.Unsupported, ref i);
|
||||
|
||||
// Verbose
|
||||
ProcessFlagParameter(parts, FlagStrings.Verbose, ref i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#endif
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using Xunit;
|
||||
@@ -25,35 +22,6 @@ namespace MPF.Test.Core.Converters
|
||||
DriveType.Removable,
|
||||
};
|
||||
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// IMAPI_MEDIA_PHYSICAL_TYPE values that map to MediaType
|
||||
/// </summary>
|
||||
private static readonly IMAPI_MEDIA_PHYSICAL_TYPE[] _mappableImapiTypes = new IMAPI_MEDIA_PHYSICAL_TYPE[]
|
||||
{
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_UNKNOWN,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDROM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDR,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDRW,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDROM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDRAM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR_DUALLAYER,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHRW,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR_DUALLAYER,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DISK,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW_DUALLAYER,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDROM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDR,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDRAM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDROM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDR,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDRE,
|
||||
};
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Check that every supported DriveType maps to an InternalDriveType
|
||||
/// </summary>
|
||||
@@ -71,64 +39,41 @@ namespace MPF.Test.Core.Converters
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Check that every supported IMAPI_MEDIA_PHYSICAL_TYPE maps to an MediaType
|
||||
/// </summary>
|
||||
/// <param name="imapiType">IMAPI_MEDIA_PHYSICAL_TYPE value to check</param>
|
||||
/// <param name="expectNull">True to expect a null mapping, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateImapiTypeMappingTestData))]
|
||||
public void IMAPIToMediaTypeTest(IMAPI_MEDIA_PHYSICAL_TYPE imapiType, bool expectNull)
|
||||
{
|
||||
var actual = imapiType.IMAPIToMediaType();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of DriveType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DriveType values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateDriveTypeMappingTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateDriveTypeMappingTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (DriveType driveType in Enum.GetValues(typeof(DriveType)))
|
||||
{
|
||||
if (_mappableDriveTypes.Contains(driveType))
|
||||
#if NET48
|
||||
testData.Add(new object[] { driveType, false });
|
||||
else
|
||||
testData.Add(new object[] { driveType, true });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Generate a test set of IMAPI_MEDIA_PHYSICAL_TYPE values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of IMAPI_MEDIA_PHYSICAL_TYPE values</returns>
|
||||
public static List<object[]> GenerateImapiTypeMappingTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
foreach (IMAPI_MEDIA_PHYSICAL_TYPE imapiType in Enum.GetValues(typeof(IMAPI_MEDIA_PHYSICAL_TYPE)))
|
||||
{
|
||||
if (_mappableImapiTypes.Contains(imapiType))
|
||||
testData.Add(new object[] { imapiType, false });
|
||||
else
|
||||
testData.Add(new object[] { imapiType, true });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
#else
|
||||
testData.Add(new object?[] { driveType, false });
|
||||
#endif
|
||||
else
|
||||
#if NET48
|
||||
testData.Add(new object[] { driveType, true });
|
||||
#else
|
||||
testData.Add(new object?[] { driveType, true });
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Convert to Long Name
|
||||
|
||||
@@ -150,18 +95,30 @@ namespace MPF.Test.Core.Converters
|
||||
/// Generate a test set of InternalProgram values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of InternalProgram values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateInternalProgramTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateInternalProgramTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null } };
|
||||
#endif
|
||||
foreach (InternalProgram? internalProgram in Enum.GetValues(typeof(InternalProgram)))
|
||||
{
|
||||
#if NET48
|
||||
testData.Add(new object[] { internalProgram });
|
||||
#else
|
||||
testData.Add(new object?[] { internalProgram });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
// TODO: Add from-string tests
|
||||
}
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
using MPF.Core.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Core.Data
|
||||
{
|
||||
public class XgdInfoTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData("1234567")]
|
||||
[InlineData("1234567\0")]
|
||||
[InlineData("123456789")]
|
||||
public void UnmatchedStringTests(string invalidString)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(invalidString);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("AV00100W", "AV", "001", "00", 'W')]
|
||||
[InlineData("AV00100W\0", "AV", "001", "00", 'W')]
|
||||
public void XGD1ValidTests(string validString, string publisher, string gameId, string version, char regionIdentifier)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(validString, validate: true);
|
||||
|
||||
Assert.True(xgdInfo.Initialized);
|
||||
Assert.Equal(publisher, xgdInfo.PublisherIdentifier);
|
||||
Assert.Equal(gameId, xgdInfo.GameID);
|
||||
Assert.Equal(version, xgdInfo.SKU);
|
||||
Assert.Equal(regionIdentifier, xgdInfo.RegionIdentifier);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Invalid publisher identifier
|
||||
[InlineData("ZZ000000")]
|
||||
[InlineData("ZZ000000\0")]
|
||||
// Invalid region identifier
|
||||
[InlineData("AV00000Z")]
|
||||
[InlineData("AV00000Z\0")]
|
||||
public void XGD1InvalidTests(string invalidString)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(invalidString, validate: true);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("AV200100W0F11", "AV", "001", "00", 'W', "0", 'F', "11", null)]
|
||||
[InlineData("AV200100W0F11\0", "AV", "001", "00", 'W', "0", 'F', "11", null)]
|
||||
[InlineData("AV200100W01F11", "AV", "001", "00", 'W', "01", 'F', "11", null)]
|
||||
[InlineData("AV200100W01F11\0", "AV", "001", "00", 'W', "01", 'F', "11", null)]
|
||||
[InlineData("AV200100W0F11DEADBEEF", "AV", "001", "00", 'W', "0", 'F', "11", "DEADBEEF")]
|
||||
[InlineData("AV200100W0F11DEADBEEF\0", "AV", "001", "00", 'W', "0", 'F', "11", "DEADBEEF")]
|
||||
[InlineData("AV200100W01F11DEADBEEF", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
|
||||
[InlineData("AV200100W01F11DEADBEEF\0", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
|
||||
public void XGD23ValidTests(string validString, string publisher, string gameId, string sku, char regionIdentifier, string baseVersion, char mediaSubtype, string discNumber, string cert)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(validString, validate: true);
|
||||
|
||||
Assert.True(xgdInfo.Initialized);
|
||||
Assert.Equal(publisher, xgdInfo.PublisherIdentifier);
|
||||
Assert.Equal('2', xgdInfo.PlatformIdentifier);
|
||||
Assert.Equal(gameId, xgdInfo.GameID);
|
||||
Assert.Equal(sku, xgdInfo.SKU);
|
||||
Assert.Equal(regionIdentifier, xgdInfo.RegionIdentifier);
|
||||
Assert.Equal(baseVersion, xgdInfo.BaseVersion);
|
||||
Assert.Equal(mediaSubtype, xgdInfo.MediaSubtypeIdentifier);
|
||||
Assert.Equal(discNumber, xgdInfo.DiscNumberIdentifier);
|
||||
Assert.Equal(cert, xgdInfo.CertificationSubmissionIdentifier);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
// Invalid publisher identifier
|
||||
[InlineData("ZZ00000000000")]
|
||||
[InlineData("ZZ00000000000\0")]
|
||||
[InlineData("ZZ000000000000")]
|
||||
[InlineData("ZZ000000000000\0")]
|
||||
[InlineData("ZZ0000000000000000000")]
|
||||
[InlineData("ZZ0000000000000000000\0")]
|
||||
[InlineData("ZZ00000000000000000000")]
|
||||
[InlineData("ZZ00000000000000000000\0")]
|
||||
// Invalid platform identifier
|
||||
[InlineData("AV90000000000")]
|
||||
[InlineData("AV90000000000\0")]
|
||||
[InlineData("AV900000000000")]
|
||||
[InlineData("AV900000000000\0")]
|
||||
[InlineData("AV9000000000000000000")]
|
||||
[InlineData("AV9000000000000000000\0")]
|
||||
[InlineData("AV90000000000000000000")]
|
||||
[InlineData("AV90000000000000000000\0")]
|
||||
// Invalid region identifier
|
||||
[InlineData("AV200000Z0000")]
|
||||
[InlineData("AV200000Z0000\0")]
|
||||
[InlineData("AV200000Z00000")]
|
||||
[InlineData("AV200000Z00000\0")]
|
||||
[InlineData("AV200000Z000000000000")]
|
||||
[InlineData("AV200000Z000000000000\0")]
|
||||
[InlineData("AV200000Z0000000000000")]
|
||||
[InlineData("AV200000Z0000000000000\0")]
|
||||
// Invalid media subtype identifier
|
||||
[InlineData("AV200000W0A00")]
|
||||
[InlineData("AV200000W0A00\0")]
|
||||
[InlineData("AV200000W00A00")]
|
||||
[InlineData("AV200000W00A00\0")]
|
||||
[InlineData("AV200000W00A000000000")]
|
||||
[InlineData("AV200000W00A000000000\0")]
|
||||
[InlineData("AV200000W00A0000000000")]
|
||||
[InlineData("AV200000W00A0000000000\0")]
|
||||
public void XGD23InvalidTests(string invalidString)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(invalidString, validate: true);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Utilities;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Core.Utilities
|
||||
@@ -31,10 +31,12 @@ namespace MPF.Test.Core.Utilities
|
||||
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem,
|
||||
RedumpSystem.AudioCD,
|
||||
RedumpSystem.DVDAudio,
|
||||
RedumpSystem.HasbroiONEducationalGamingSystem,
|
||||
RedumpSystem.HasbroVideoNow,
|
||||
RedumpSystem.HasbroVideoNowColor,
|
||||
RedumpSystem.HasbroVideoNowJr,
|
||||
RedumpSystem.HasbroVideoNowXP,
|
||||
RedumpSystem.PlayStationGameSharkUpdates,
|
||||
RedumpSystem.PhilipsCDi,
|
||||
RedumpSystem.SuperAudioCD,
|
||||
};
|
||||
@@ -58,6 +60,7 @@ namespace MPF.Test.Core.Utilities
|
||||
RedumpSystem.SonyPlayStation2,
|
||||
RedumpSystem.SonyPlayStation3,
|
||||
RedumpSystem.SonyPlayStation4,
|
||||
RedumpSystem.SonyPlayStationPortable,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -140,15 +143,31 @@ namespace MPF.Test.Core.Utilities
|
||||
/// Generate a test set of MediaType values that support drive speeds
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of MediaType values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateSupportDriveSpeedsTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateSupportDriveSpeedsTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
#endif
|
||||
foreach (MediaType mediaType in Enum.GetValues(typeof(MediaType)))
|
||||
{
|
||||
if (_supportDriveSpeeds.Contains(mediaType))
|
||||
#if NET48
|
||||
testData.Add(new object[] { mediaType, true });
|
||||
#else
|
||||
testData.Add(new object?[] { mediaType, true });
|
||||
#endif
|
||||
else
|
||||
#if NET48
|
||||
testData.Add(new object[] { mediaType, false });
|
||||
#else
|
||||
testData.Add(new object?[] { mediaType, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -158,15 +177,31 @@ namespace MPF.Test.Core.Utilities
|
||||
/// Generate a test set of RedumpSystem values that are considered Audio
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateAudioSystemsTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateAudioSystemsTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
#endif
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_audioSystems.Contains(redumpSystem))
|
||||
#if NET48
|
||||
testData.Add(new object[] { redumpSystem, true });
|
||||
#else
|
||||
testData.Add(new object?[] { redumpSystem, true });
|
||||
#endif
|
||||
else
|
||||
#if NET48
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
#else
|
||||
testData.Add(new object?[] { redumpSystem, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -176,15 +211,31 @@ namespace MPF.Test.Core.Utilities
|
||||
/// Generate a test set of RedumpSystem values that are considered markers
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateMarkerSystemsTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateMarkerSystemsTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
#endif
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_markerSystems.Contains(redumpSystem))
|
||||
#if NET48
|
||||
testData.Add(new object[] { redumpSystem, true });
|
||||
#else
|
||||
testData.Add(new object?[] { redumpSystem, true });
|
||||
#endif
|
||||
else
|
||||
#if NET48
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
#else
|
||||
testData.Add(new object?[] { redumpSystem, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -194,15 +245,31 @@ namespace MPF.Test.Core.Utilities
|
||||
/// Generate a test set of RedumpSystem values that are considered markers
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateReversedRingcodeSystemsTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateReversedRingcodeSystemsTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
#endif
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_reverseRingcodeSystems.Contains(redumpSystem))
|
||||
#if NET48
|
||||
testData.Add(new object[] { redumpSystem, true });
|
||||
#else
|
||||
testData.Add(new object?[] { redumpSystem, true });
|
||||
#endif
|
||||
else
|
||||
#if NET48
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
#else
|
||||
testData.Add(new object?[] { redumpSystem, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -212,15 +279,31 @@ namespace MPF.Test.Core.Utilities
|
||||
/// Generate a test set of RedumpSystem values that are considered XGD
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateXGDSystemsTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateXGDSystemsTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
#endif
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_xgdSystems.Contains(redumpSystem))
|
||||
#if NET48
|
||||
testData.Add(new object[] { redumpSystem, true });
|
||||
#else
|
||||
testData.Add(new object?[] { redumpSystem, true });
|
||||
#endif
|
||||
else
|
||||
#if NET48
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
#else
|
||||
testData.Add(new object?[] { redumpSystem, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.IO;
|
||||
using MPF.Core;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Library;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Library
|
||||
@@ -24,7 +23,7 @@ namespace MPF.Test.Library
|
||||
? Drive.Create(InternalDriveType.Floppy, letter.ToString())
|
||||
: Drive.Create(InternalDriveType.Optical, letter.ToString());
|
||||
|
||||
var env = new DumpEnvironment(options, string.Empty, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, parameters);
|
||||
var env = new DumpEnvironment(options, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, null, parameters);
|
||||
|
||||
bool actual = env.ParametersValid();
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using MPF.Library;
|
||||
using RedumpLib.Data;
|
||||
using System.IO;
|
||||
using MPF.Core;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Library
|
||||
@@ -25,9 +26,9 @@ namespace MPF.Test.Library
|
||||
[InlineData(MediaType.DVD, 12345, 1, 2, 3, "DVD-ROM-9")]
|
||||
[InlineData(MediaType.BluRay, 0, 0, 0, 0, "BD-ROM-25")]
|
||||
[InlineData(MediaType.BluRay, 12345, 0, 0, 0, "BD-ROM-25")]
|
||||
//[InlineData(MediaType.BluRay, 26_843_531_857, 0, 0, 0, "BD-ROM-33")]
|
||||
[InlineData(MediaType.BluRay, 26_843_531_857, 0, 0, 0, "BD-ROM-33")]
|
||||
[InlineData(MediaType.BluRay, 12345, 1, 0, 0, "BD-ROM-50")]
|
||||
//[InlineData(MediaType.BluRay, 53_687_063_713, 1, 0, 0, "BD-ROM-66")]
|
||||
[InlineData(MediaType.BluRay, 53_687_063_713, 1, 0, 0, "BD-ROM-66")]
|
||||
[InlineData(MediaType.BluRay, 12345, 1, 2, 0, "BD-ROM-100")]
|
||||
[InlineData(MediaType.BluRay, 12345, 1, 2, 3, "BD-ROM-128")]
|
||||
[InlineData(MediaType.UMD, 0, 0, 0, 0, "UMD-SL")]
|
||||
@@ -41,44 +42,63 @@ namespace MPF.Test.Library
|
||||
long layerbreak,
|
||||
long layerbreak2,
|
||||
long layerbreak3,
|
||||
#if NET48
|
||||
string expected)
|
||||
#else
|
||||
string? expected)
|
||||
#endif
|
||||
{
|
||||
string actual = InfoTool.GetFixedMediaType(mediaType, size, layerbreak, layerbreak2, layerbreak3);
|
||||
// TODO: Add tests around BDU
|
||||
var actual = InfoTool.GetFixedMediaType(mediaType, null, size, layerbreak, layerbreak2, layerbreak3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, null, null)]
|
||||
[InlineData(" ", "", " ", "")]
|
||||
[InlineData("super", "blah.bin", "super", "blah.bin")]
|
||||
[InlineData("super\\hero", "blah.bin", "super\\hero", "blah.bin")]
|
||||
[InlineData("super.hero", "blah.bin", "super.hero", "blah.bin")]
|
||||
[InlineData("superhero", "blah.rev.bin", "superhero", "blah.rev.bin")]
|
||||
[InlineData("super&hero", "blah.bin", "super&hero", "blah.bin")]
|
||||
[InlineData("superhero", "blah&foo.bin", "superhero", "blah&foo.bin")]
|
||||
public void NormalizeOutputPathsTest(string outputDirectory, string outputFilename, string expectedOutputDirectory, string expectedOutputFilename)
|
||||
[InlineData(null, "")]
|
||||
[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")]
|
||||
#if NET48
|
||||
public void NormalizeOutputPathsTest(string outputPath, string expectedPath)
|
||||
#else
|
||||
public void NormalizeOutputPathsTest(string? outputPath, string? expectedPath)
|
||||
#endif
|
||||
{
|
||||
(string actualOutputDirectory, string actualOutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename);
|
||||
Assert.Equal(expectedOutputDirectory, actualOutputDirectory);
|
||||
Assert.Equal(expectedOutputFilename, actualOutputFilename);
|
||||
if (!string.IsNullOrWhiteSpace(expectedPath))
|
||||
expectedPath = Path.GetFullPath(expectedPath);
|
||||
|
||||
string actualPath = InfoTool.NormalizeOutputPaths(outputPath, true);
|
||||
Assert.Equal(expectedPath, actualPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessSpecialFieldsCompleteTest()
|
||||
{
|
||||
// Create a new SubmissionInfo object
|
||||
SubmissionInfo info = new SubmissionInfo()
|
||||
var info = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = new CommonDiscInfoSection()
|
||||
{
|
||||
Comments = "This is a comments line\n[T:ISBN] ISBN Value",
|
||||
#if NET48
|
||||
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
CommentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
|
||||
},
|
||||
|
||||
Contents = "This is a contents line\n[T:GF] Game Footage",
|
||||
#if NET48
|
||||
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
ContentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.Patches] = "1.04 patch",
|
||||
},
|
||||
@@ -107,7 +127,7 @@ namespace MPF.Test.Library
|
||||
public void ProcessSpecialFieldsNullObjectTest()
|
||||
{
|
||||
// Create a new SubmissionInfo object
|
||||
SubmissionInfo info = new SubmissionInfo()
|
||||
var info = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = null,
|
||||
};
|
||||
@@ -123,18 +143,26 @@ namespace MPF.Test.Library
|
||||
public void ProcessSpecialFieldsNullCommentsContentsTest()
|
||||
{
|
||||
// Create a new SubmissionInfo object
|
||||
SubmissionInfo info = new SubmissionInfo()
|
||||
var info = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = new CommonDiscInfoSection()
|
||||
{
|
||||
Comments = null,
|
||||
#if NET48
|
||||
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
CommentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
|
||||
},
|
||||
|
||||
Contents = null,
|
||||
#if NET48
|
||||
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
ContentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.Patches] = "1.04 patch",
|
||||
},
|
||||
@@ -163,7 +191,7 @@ namespace MPF.Test.Library
|
||||
public void ProcessSpecialFieldsNullDictionariesTest()
|
||||
{
|
||||
// Create a new SubmissionInfo object
|
||||
SubmissionInfo info = new SubmissionInfo()
|
||||
var info = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = new CommonDiscInfoSection()
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Library;
|
||||
using MPF.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Library
|
||||
@@ -178,43 +178,6 @@ namespace MPF.Test.Library
|
||||
Assert.Equal("Anything Else Protection", sanitized);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
[InlineData(2)]
|
||||
[InlineData(3)]
|
||||
[InlineData(4)]
|
||||
[InlineData(5)]
|
||||
[InlineData(6)]
|
||||
[InlineData(7)]
|
||||
[InlineData(8)]
|
||||
public void SanitizeFoundProtectionsSafeDiscTest(int skip)
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
"SafeDisc 1.20.000",
|
||||
"SafeDisc (drvmgt.dll) 1.2.0",
|
||||
"SafeDisc (secdrv.sys) 1.2.0",
|
||||
"SafeDisc (dplayerx.dll) 1.2.0",
|
||||
"SafeDisc 3.20-4.xx (version removed)",
|
||||
"SafeDisc 2",
|
||||
"SafeDisc 1/Lite",
|
||||
"SafeDisc Lite",
|
||||
"SafeDisc 1-3",
|
||||
"SafeDisc",
|
||||
};
|
||||
|
||||
// Safeguard for the future
|
||||
if (skip >= protections.Count)
|
||||
throw new ArgumentException("Invalid skip value", nameof(skip));
|
||||
|
||||
// The list is in order of preference
|
||||
protections = protections.Skip(skip).ToList();
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal(protections[0], sanitized);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(1)]
|
||||
|
||||
@@ -1,55 +1,36 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<COMReference Include="IMAPI2">
|
||||
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="IMAPI2FS">
|
||||
<Guid>{2C941FD0-975B-59BE-A960-9A2A262853A5}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF\MPF.csproj" />
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
|
||||
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.3.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.7.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="psxt001z" Version="0.21.0-beta1" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="xunit" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="1.0.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.core" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.4.2">
|
||||
<PackageReference Include="xunit.analyzers" Version="1.4.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.core" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.5.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Modules.DiscImageCreator;
|
||||
using RedumpLib.Data;
|
||||
using MPF.Core.Modules.DiscImageCreator;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Modules
|
||||
@@ -20,7 +21,7 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromSystemAndTypeTest(RedumpSystem? knownSystem, MediaType? mediaType, string expected)
|
||||
{
|
||||
var options = new Options();
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
Assert.Equal(expected, actual.BaseCommand);
|
||||
}
|
||||
|
||||
@@ -31,9 +32,9 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsSpecialDefaultTest(RedumpSystem? knownSystem, MediaType? mediaType,string[] expected)
|
||||
{
|
||||
var options = new Options();
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
}
|
||||
@@ -44,9 +45,9 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsC2RereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadC2, string[] expected)
|
||||
{
|
||||
var options = new Options { DICRereadCount = rereadC2 };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
@@ -64,9 +65,9 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsDVDRereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadDVDBD, string[] expected)
|
||||
{
|
||||
var options = new Options { DICDVDRereadCount = rereadDVDBD };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
@@ -88,9 +89,9 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsMultiSectorReadTest(RedumpSystem? knownSystem, MediaType? mediaType, bool multiSectorRead, string[] expected)
|
||||
{
|
||||
var options = new Options { DICMultiSectorRead = multiSectorRead };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (expectedSet.Count != 1 && multiSectorRead)
|
||||
@@ -109,9 +110,9 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsParanoidModeTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoidMode, string[] expected)
|
||||
{
|
||||
var options = new Options { DICParanoidMode = paranoidMode };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (paranoidMode)
|
||||
@@ -160,9 +161,13 @@ namespace MPF.Test.Modules
|
||||
[InlineData(MediaType.FloppyDisk, ".img")]
|
||||
[InlineData(MediaType.Cassette, ".wav")]
|
||||
[InlineData(MediaType.NONE, null)]
|
||||
#if NET48
|
||||
public void MediaTypeToExtensionTest(MediaType? mediaType, string expected)
|
||||
#else
|
||||
public void MediaTypeToExtensionTest(MediaType? mediaType, string? expected)
|
||||
#endif
|
||||
{
|
||||
string actual = Converters.Extension(mediaType);
|
||||
var actual = Converters.Extension(mediaType);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
@@ -214,7 +219,7 @@ namespace MPF.Test.Modules
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
[Fact]
|
||||
public void DiscImageCreatorAudioParametersTest()
|
||||
@@ -226,7 +231,7 @@ namespace MPF.Test.Modules
|
||||
Assert.NotNull(parametersObject);
|
||||
|
||||
// Validate that the same set of parameters are generated on the output
|
||||
string newParameters = parametersObject.GenerateParameters();
|
||||
var newParameters = parametersObject.GenerateParameters();
|
||||
Assert.NotNull(newParameters);
|
||||
Assert.Equal(originalParameters, newParameters);
|
||||
}
|
||||
@@ -241,7 +246,7 @@ namespace MPF.Test.Modules
|
||||
Assert.NotNull(parametersObject);
|
||||
|
||||
// Validate that the same set of parameters are generated on the output
|
||||
string newParameters = parametersObject.GenerateParameters();
|
||||
var newParameters = parametersObject.GenerateParameters();
|
||||
Assert.NotNull(newParameters);
|
||||
Assert.Equal(originalParameters, newParameters);
|
||||
}
|
||||
@@ -253,7 +258,7 @@ namespace MPF.Test.Modules
|
||||
/// <returns>HashSet representing the strings</returns>
|
||||
private static HashSet<string> GenerateUsedKeys(Parameters parameters)
|
||||
{
|
||||
HashSet<string> usedKeys = new HashSet<string>();
|
||||
var usedKeys = new HashSet<string>();
|
||||
if (parameters?.Keys == null)
|
||||
return usedKeys;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.RedumpLib
|
||||
@@ -18,9 +18,9 @@ namespace MPF.Test.RedumpLib
|
||||
private static readonly DiscType?[] _mappableDiscTypes = new DiscType?[]
|
||||
{
|
||||
DiscType.BD25,
|
||||
//DiscType.BD33,
|
||||
DiscType.BD33,
|
||||
DiscType.BD50,
|
||||
//DiscType.BD66,
|
||||
DiscType.BD66,
|
||||
DiscType.BD100,
|
||||
DiscType.BD128,
|
||||
DiscType.CD,
|
||||
@@ -95,15 +95,31 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of DiscType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DiscType values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateDiscTypeMappingTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateDiscTypeMappingTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (DiscType? discType in Enum.GetValues(typeof(DiscType)))
|
||||
{
|
||||
if (_mappableDiscTypes.Contains(discType))
|
||||
#if NET48
|
||||
testData.Add(new object[] { discType, false });
|
||||
#else
|
||||
testData.Add(new object?[] { discType, false });
|
||||
#endif
|
||||
else
|
||||
#if NET48
|
||||
testData.Add(new object[] { discType, true });
|
||||
#else
|
||||
testData.Add(new object?[] { discType, true });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -113,12 +129,24 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of RedumpSystem values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateRedumpSystemMappingTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateRedumpSystemMappingTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null } };
|
||||
#endif
|
||||
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
#if NET48
|
||||
testData.Add(new object[] { redumpSystem });
|
||||
#else
|
||||
testData.Add(new object?[] { redumpSystem });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -128,16 +156,32 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of mappable media types
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of MediaTypes</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateMediaTypeMappingTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateMediaTypeMappingTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
|
||||
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
|
||||
{
|
||||
if (_mappableMediaTypes.Contains(mediaType))
|
||||
#if NET48
|
||||
testData.Add(new object[] { mediaType, false });
|
||||
#else
|
||||
testData.Add(new object?[] { mediaType, false });
|
||||
#endif
|
||||
else
|
||||
#if NET48
|
||||
testData.Add(new object[] { mediaType, true });
|
||||
#else
|
||||
testData.Add(new object?[] { mediaType, true });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -156,7 +200,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateDiscCategoryTestData))]
|
||||
public void DiscCategoryLongNameTest(DiscCategory? discCategory, bool expectNull)
|
||||
{
|
||||
string actual = discCategory.LongName();
|
||||
var actual = discCategory.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -168,12 +212,24 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of DiscCategory values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DiscCategory values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateDiscCategoryTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateDiscCategoryTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (DiscCategory? discCategory in Enum.GetValues(typeof(DiscCategory)))
|
||||
{
|
||||
#if NET48
|
||||
testData.Add(new object[] { discCategory, false });
|
||||
#else
|
||||
testData.Add(new object?[] { discCategory, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -192,7 +248,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateDiscTypeTestData))]
|
||||
public void DiscTypeLongNameTest(DiscType? discType, bool expectNull)
|
||||
{
|
||||
string actual = discType.LongName();
|
||||
var actual = discType.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -204,15 +260,31 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of DiscType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DiscType values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateDiscTypeTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateDiscTypeTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (DiscType? discType in Enum.GetValues(typeof(DiscType)))
|
||||
{
|
||||
if (discType == DiscType.NONE)
|
||||
#if NET48
|
||||
testData.Add(new object[] { discType, true });
|
||||
#else
|
||||
testData.Add(new object?[] { discType, true });
|
||||
#endif
|
||||
else
|
||||
#if NET48
|
||||
testData.Add(new object[] { discType, false });
|
||||
#else
|
||||
testData.Add(new object?[] { discType, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -231,7 +303,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateLanguageTestData))]
|
||||
public void LanguageLongNameTest(Language? language, bool expectNull)
|
||||
{
|
||||
string actual = language.LongName();
|
||||
var actual = language.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -248,7 +320,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateLanguageTestData))]
|
||||
public void LanguageShortNameTest(Language? language, bool expectNull)
|
||||
{
|
||||
string actual = language.ShortName();
|
||||
var actual = language.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -268,7 +340,7 @@ namespace MPF.Test.RedumpLib
|
||||
int totalCount = 0;
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
string code = language.TwoLetterCode();
|
||||
var code = language.TwoLetterCode();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
continue;
|
||||
|
||||
@@ -295,7 +367,7 @@ namespace MPF.Test.RedumpLib
|
||||
int totalCount = 0;
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
string code = language.ThreeLetterCode();
|
||||
var code = language.ThreeLetterCode();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
continue;
|
||||
|
||||
@@ -322,7 +394,7 @@ namespace MPF.Test.RedumpLib
|
||||
int totalCount = 0;
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
string code = language.ThreeLetterCodeAlt();
|
||||
var code = language.ThreeLetterCodeAlt();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
continue;
|
||||
|
||||
@@ -341,12 +413,24 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of Language values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of Language values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateLanguageTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateLanguageTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (Language? language in Enum.GetValues(typeof(Language)))
|
||||
{
|
||||
#if NET48
|
||||
testData.Add(new object[] { language, false });
|
||||
#else
|
||||
testData.Add(new object?[] { language, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -365,7 +449,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateLanguageSelectionTestData))]
|
||||
public void LanguageSelectionLongNameTest(LanguageSelection? languageSelection, bool expectNull)
|
||||
{
|
||||
string actual = languageSelection.LongName();
|
||||
var actual = languageSelection.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -377,12 +461,24 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of LanguageSelection values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of LanguageSelection values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateLanguageSelectionTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateLanguageSelectionTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (LanguageSelection? languageSelection in Enum.GetValues(typeof(LanguageSelection)))
|
||||
{
|
||||
#if NET48
|
||||
testData.Add(new object[] { languageSelection, false });
|
||||
#else
|
||||
testData.Add(new object?[] { languageSelection, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -401,7 +497,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateMediaTypeTestData))]
|
||||
public void MediaTypeLongNameTest(MediaType? mediaType, bool expectNull)
|
||||
{
|
||||
string actual = mediaType.LongName();
|
||||
var actual = mediaType.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -418,7 +514,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateMediaTypeTestData))]
|
||||
public void MediaTypeShortNameTest(MediaType? mediaType, bool expectNull)
|
||||
{
|
||||
string actual = mediaType.ShortName();
|
||||
var actual = mediaType.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -430,12 +526,24 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of MediaType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of MediaType values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateMediaTypeTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateMediaTypeTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
|
||||
{
|
||||
#if NET48
|
||||
testData.Add(new object[] { mediaType, false });
|
||||
#else
|
||||
testData.Add(new object?[] { mediaType, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -454,7 +562,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateRegionTestData))]
|
||||
public void RegionLongNameTest(Region? region, bool expectNull)
|
||||
{
|
||||
string actual = region.LongName();
|
||||
var actual = region.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -471,7 +579,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateRegionTestData))]
|
||||
public void RegionShortNameTest(Region? region, bool expectNull)
|
||||
{
|
||||
string actual = region.ShortName();
|
||||
var actual = region.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -491,7 +599,7 @@ namespace MPF.Test.RedumpLib
|
||||
int totalCount = 0;
|
||||
foreach (Region? region in fullRegions)
|
||||
{
|
||||
string code = region.ShortName();
|
||||
var code = region.ShortName();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
continue;
|
||||
|
||||
@@ -510,12 +618,24 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of Region values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of Region values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateRegionTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateRegionTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (Region? region in Enum.GetValues(typeof(Region)))
|
||||
{
|
||||
#if NET48
|
||||
testData.Add(new object[] { region, false });
|
||||
#else
|
||||
testData.Add(new object?[] { region, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -534,7 +654,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateSiteCodeTestData))]
|
||||
public void SiteCodeLongNameTest(SiteCode? siteCode, bool expectNull)
|
||||
{
|
||||
string actual = siteCode.LongName();
|
||||
var actual = siteCode.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -551,7 +671,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateSiteCodeTestData))]
|
||||
public void SiteCodeShortNameTest(SiteCode? siteCode, bool expectNull)
|
||||
{
|
||||
string actual = siteCode.ShortName();
|
||||
var actual = siteCode.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -563,12 +683,24 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of SiteCode values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of SiteCode values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateSiteCodeTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateSiteCodeTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (SiteCode? siteCode in Enum.GetValues(typeof(SiteCode)))
|
||||
{
|
||||
#if NET48
|
||||
testData.Add(new object[] { siteCode, false });
|
||||
#else
|
||||
testData.Add(new object?[] { siteCode, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -587,7 +719,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateRedumpSystemTestData))]
|
||||
public void RedumpSystemLongNameTest(RedumpSystem? redumpSystem, bool expectNull)
|
||||
{
|
||||
string actual = redumpSystem.LongName();
|
||||
var actual = redumpSystem.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -622,16 +754,28 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of RedumpSystem values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateRedumpSystemTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateRedumpSystemTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
// We want to skip all markers for this
|
||||
if (redumpSystem.IsMarker())
|
||||
continue;
|
||||
|
||||
#if NET48
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
#else
|
||||
testData.Add(new object?[] { redumpSystem, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -650,7 +794,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateSystemCategoryTestData))]
|
||||
public void SystemCategoryLongNameTest(SystemCategory? systemCategory, bool expectNull)
|
||||
{
|
||||
string actual = systemCategory.LongName();
|
||||
var actual = systemCategory.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -662,15 +806,31 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of SystemCategory values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of SystemCategory values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateSystemCategoryTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateSystemCategoryTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
#endif
|
||||
foreach (SystemCategory? systemCategory in Enum.GetValues(typeof(SystemCategory)))
|
||||
{
|
||||
if (systemCategory == SystemCategory.NONE)
|
||||
#if NET48
|
||||
testData.Add(new object[] { systemCategory, true });
|
||||
#else
|
||||
testData.Add(new object?[] { systemCategory, true });
|
||||
#endif
|
||||
else
|
||||
#if NET48
|
||||
testData.Add(new object[] { systemCategory, false });
|
||||
#else
|
||||
testData.Add(new object?[] { systemCategory, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -701,12 +861,24 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of YesNo values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of YesNo values</returns>
|
||||
#if NET48
|
||||
public static List<object[]> GenerateYesNoTestData()
|
||||
#else
|
||||
public static List<object?[]> GenerateYesNoTestData()
|
||||
#endif
|
||||
{
|
||||
#if NET48
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
#else
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
#endif
|
||||
foreach (YesNo? yesNo in Enum.GetValues(typeof(YesNo)))
|
||||
{
|
||||
#if NET48
|
||||
testData.Add(new object[] { yesNo, false });
|
||||
#else
|
||||
testData.Add(new object?[] { yesNo, false });
|
||||
#endif
|
||||
}
|
||||
|
||||
return testData;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using RedumpLib.Data;
|
||||
using psxt001z;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.RedumpLib
|
||||
@@ -81,12 +82,20 @@ namespace MPF.Test.RedumpLib
|
||||
EXEDateBuildDate = "19xx-xx-xx",
|
||||
ErrorsCount = "0",
|
||||
Comments = "Comment data line 1\r\nComment data line 2",
|
||||
#if NET48
|
||||
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
CommentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.ISBN] = "ISBN",
|
||||
},
|
||||
Contents = "Special contents 1\r\nSpecial contents 2",
|
||||
#if NET48
|
||||
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
ContentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.PlayableDemos] = "Game Demo 1",
|
||||
},
|
||||
@@ -160,6 +169,7 @@ namespace MPF.Test.RedumpLib
|
||||
DumpingInfo = new DumpingInfoSection()
|
||||
{
|
||||
DumpingProgram = "DiscImageCreator 20500101",
|
||||
DumpingDate = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
Manufacturer = "ATAPI",
|
||||
Model = "Optical Drive",
|
||||
Firmware = "1.23",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Linq;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Data
|
||||
|
||||
@@ -2,22 +2,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using static MPF.Core.Data.Interface;
|
||||
|
||||
namespace MPF
|
||||
namespace MPF.UI.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Variables for UI elements
|
||||
/// </summary>
|
||||
public static class Constants
|
||||
{
|
||||
// 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> 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());
|
||||
64
MPF.UI.Core/ElementConverter.cs
Normal file
64
MPF.UI.Core/ElementConverter.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ComboBoxItems;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.UI.Core
|
||||
{
|
||||
internal class ElementConverter: IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case DiscCategory discCategory:
|
||||
return new Element<DiscCategory>(discCategory);
|
||||
case InternalProgram internalProgram:
|
||||
return new Element<InternalProgram>(internalProgram);
|
||||
case MediaType mediaType:
|
||||
return new Element<MediaType>(mediaType);
|
||||
case RedumpSystem redumpSystem:
|
||||
return new RedumpSystemComboBoxItem(redumpSystem);
|
||||
case Region region:
|
||||
return new Element<Region>(region);
|
||||
|
||||
// Null values are treated as a system value
|
||||
default:
|
||||
return new RedumpSystemComboBoxItem((RedumpSystem?)null);
|
||||
}
|
||||
}
|
||||
|
||||
#if NET48
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
#else
|
||||
public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
#endif
|
||||
{
|
||||
// If it's an IElement but ends up null
|
||||
#if NET48
|
||||
if (!(value is IElement element))
|
||||
#else
|
||||
if (value is not IElement element)
|
||||
#endif
|
||||
return null;
|
||||
|
||||
switch (element)
|
||||
{
|
||||
case Element<DiscCategory> dcElement:
|
||||
return dcElement.Value;
|
||||
case Element<InternalProgram> ipElement:
|
||||
return ipElement.Value;
|
||||
case Element<MediaType> mtElement:
|
||||
return mtElement.Value;
|
||||
case RedumpSystemComboBoxItem rsElement:
|
||||
return rsElement.Value;
|
||||
case Element<Region> reValue:
|
||||
return reValue.Value;
|
||||
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace WPFCustomMessageBox
|
||||
/// Global parameter to enable (true) or disable (false) the removal of the title bar icon.
|
||||
/// If you are using a custom window style, the icon removal may cause issues like displaying two title bar (the default windows one and the custom one).
|
||||
/// </summary>
|
||||
public static bool RemoveTitleBarIcon = true;
|
||||
private static readonly bool RemoveTitleBarIcon = true;
|
||||
|
||||
/// <summary>
|
||||
/// Displays a message box that has a message and returns a result.
|
||||
@@ -173,7 +173,11 @@ namespace WPFCustomMessageBox
|
||||
/// </param>
|
||||
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
|
||||
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
|
||||
#if NET48
|
||||
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
#else
|
||||
public static MessageBoxResult Show(Window owner, string? messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
#endif
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
@@ -515,11 +519,15 @@ namespace WPFCustomMessageBox
|
||||
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
|
||||
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
|
||||
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
|
||||
#if NET48
|
||||
private static MessageBoxResult ShowOKMessage(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
#else
|
||||
private static MessageBoxResult ShowOKMessage(Window? owner, string? messageBoxText, string caption, string? okButtonText, string? cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
#endif
|
||||
{
|
||||
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.OK : MessageBoxButton.OKCancel;
|
||||
|
||||
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
|
||||
var msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
|
||||
if (!string.IsNullOrEmpty(okButtonText))
|
||||
msg.OkButtonText = okButtonText;
|
||||
if (!string.IsNullOrEmpty(cancelButtonText))
|
||||
@@ -545,11 +553,15 @@ namespace WPFCustomMessageBox
|
||||
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
|
||||
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
|
||||
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
|
||||
#if NET48
|
||||
private static MessageBoxResult ShowYesNoMessage(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
#else
|
||||
private static MessageBoxResult ShowYesNoMessage(Window? owner, string? messageBoxText, string caption, string? yesButtonText, string? noButtonText, string? cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
#endif
|
||||
{
|
||||
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.YesNo : MessageBoxButton.YesNoCancel;
|
||||
|
||||
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
|
||||
var msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
|
||||
if (!string.IsNullOrEmpty(yesButtonText))
|
||||
msg.YesButtonText = yesButtonText;
|
||||
if (!string.IsNullOrEmpty(noButtonText))
|
||||
@@ -566,14 +578,14 @@ namespace WPFCustomMessageBox
|
||||
{
|
||||
if (timeout.HasValue && timeout.Value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("timeout", string.Format("Timeout must be greater than 0."));
|
||||
throw new ArgumentOutOfRangeException(nameof(timeout), string.Format("Timeout must be greater than 0."));
|
||||
}
|
||||
|
||||
if (timeout.HasValue)
|
||||
{
|
||||
//System.Threading.Timer timer = null;
|
||||
//timer = new System.Threading.Timer(s => { msg.Close(); timer.Dispose(); }, null, timeout.Value, System.Threading.Timeout.Infinite);
|
||||
System.Timers.Timer timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
|
||||
var timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
|
||||
timer.Elapsed += delegate {
|
||||
Application.Current.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
@@ -4,7 +4,6 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
WindowStyle="None"
|
||||
ShowInTaskbar="False"
|
||||
ResizeMode="NoResize" SizeToContent="WidthAndHeight"
|
||||
TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType" UseLayoutRounding="True"
|
||||
Title="" MinHeight="155" MaxWidth="470" MinWidth="154"
|
||||
@@ -10,9 +10,13 @@ namespace WPFCustomMessageBox
|
||||
/// </summary>
|
||||
internal partial class CustomMessageBoxWindow : Window
|
||||
{
|
||||
private bool _removeTitleBarIcon = true;
|
||||
private readonly bool _removeTitleBarIcon = true;
|
||||
|
||||
#if NET48
|
||||
public string Caption
|
||||
#else
|
||||
public string? Caption
|
||||
#endif
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -24,7 +28,11 @@ namespace WPFCustomMessageBox
|
||||
}
|
||||
}
|
||||
|
||||
#if NET48
|
||||
public string Message
|
||||
#else
|
||||
public string? Message
|
||||
#endif
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -36,7 +44,11 @@ namespace WPFCustomMessageBox
|
||||
}
|
||||
}
|
||||
|
||||
#if NET48
|
||||
public string OkButtonText
|
||||
#else
|
||||
public string? OkButtonText
|
||||
#endif
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -48,7 +60,11 @@ namespace WPFCustomMessageBox
|
||||
}
|
||||
}
|
||||
|
||||
#if NET48
|
||||
public string CancelButtonText
|
||||
#else
|
||||
public string? CancelButtonText
|
||||
#endif
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -60,7 +76,11 @@ namespace WPFCustomMessageBox
|
||||
}
|
||||
}
|
||||
|
||||
#if NET48
|
||||
public string YesButtonText
|
||||
#else
|
||||
public string? YesButtonText
|
||||
#endif
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -72,7 +92,11 @@ namespace WPFCustomMessageBox
|
||||
}
|
||||
}
|
||||
|
||||
#if NET48
|
||||
public string NoButtonText
|
||||
#else
|
||||
public string? NoButtonText
|
||||
#endif
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -86,11 +110,18 @@ namespace WPFCustomMessageBox
|
||||
|
||||
public MessageBoxResult Result { get; set; }
|
||||
|
||||
#if NET48
|
||||
internal CustomMessageBoxWindow(Window owner, string message, string caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
|
||||
#else
|
||||
internal CustomMessageBoxWindow(Window? owner, string? message, string? caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
|
||||
#endif
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
_removeTitleBarIcon = removeTitleBarIcon;
|
||||
Focusable = true;
|
||||
ShowActivated = true;
|
||||
ShowInTaskbar = true;
|
||||
|
||||
if (owner != null)
|
||||
{
|
||||
@@ -106,7 +137,7 @@ namespace WPFCustomMessageBox
|
||||
if (image.HasValue)
|
||||
DisplayImage(image.Value);
|
||||
else
|
||||
Image_MessageBox.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Image_MessageBox.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
protected override void OnSourceInitialized(EventArgs e)
|
||||
@@ -123,39 +154,39 @@ namespace WPFCustomMessageBox
|
||||
{
|
||||
case MessageBoxButton.OKCancel:
|
||||
// Hide all but OK, Cancel
|
||||
Button_OK.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_OK.Visibility = Visibility.Visible;
|
||||
Button_OK.Focus();
|
||||
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_Cancel.Visibility = Visibility.Visible;
|
||||
|
||||
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_No.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_Yes.Visibility = Visibility.Collapsed;
|
||||
Button_No.Visibility = Visibility.Collapsed;
|
||||
break;
|
||||
case MessageBoxButton.YesNo:
|
||||
// Hide all but Yes, No
|
||||
Button_Yes.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_Yes.Visibility = Visibility.Visible;
|
||||
Button_Yes.Focus();
|
||||
Button_No.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_No.Visibility = Visibility.Visible;
|
||||
|
||||
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_OK.Visibility = Visibility.Collapsed;
|
||||
Button_Cancel.Visibility = Visibility.Collapsed;
|
||||
break;
|
||||
case MessageBoxButton.YesNoCancel:
|
||||
// Hide only OK
|
||||
Button_Yes.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_Yes.Visibility = Visibility.Visible;
|
||||
Button_Yes.Focus();
|
||||
Button_No.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_No.Visibility = Visibility.Visible;
|
||||
Button_Cancel.Visibility = Visibility.Visible;
|
||||
|
||||
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_OK.Visibility = Visibility.Collapsed;
|
||||
break;
|
||||
default:
|
||||
// Hide all but OK
|
||||
Button_OK.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_OK.Visibility = Visibility.Visible;
|
||||
Button_OK.Focus();
|
||||
|
||||
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_No.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_Yes.Visibility = Visibility.Collapsed;
|
||||
Button_No.Visibility = Visibility.Collapsed;
|
||||
Button_Cancel.Visibility = Visibility.Collapsed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -184,7 +215,7 @@ namespace WPFCustomMessageBox
|
||||
}
|
||||
|
||||
Image_MessageBox.Source = icon.ToImageSource();
|
||||
Image_MessageBox.Visibility = System.Windows.Visibility.Visible;
|
||||
Image_MessageBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
private void Button_OK_Click(object sender, RoutedEventArgs e)
|
||||
@@ -37,7 +37,7 @@ namespace WPFCustomMessageBox
|
||||
const int SWP_NOMOVE = 0x0002;
|
||||
const int SWP_NOZORDER = 0x0004;
|
||||
const int SWP_FRAMECHANGED = 0x0020;
|
||||
const uint WM_SETICON = 0x0080;
|
||||
//const uint WM_SETICON = 0x0080;
|
||||
|
||||
|
||||
internal static ImageSource ToImageSource(this Icon icon)
|
||||
@@ -60,7 +60,11 @@ namespace WPFCustomMessageBox
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
#if NET48
|
||||
internal static string TryAddKeyboardAccellerator(this string input)
|
||||
#else
|
||||
internal static string? TryAddKeyboardAccellerator(this string? input)
|
||||
#endif
|
||||
{
|
||||
if (input == null)
|
||||
return input;
|
||||
@@ -84,7 +88,7 @@ namespace WPFCustomMessageBox
|
||||
IntPtr hwnd = new WindowInteropHelper(window).Handle;
|
||||
// Change the extended window style to not show a window icon
|
||||
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
|
||||
_ = SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
|
||||
// Update the window's non-client area to reflect the changes
|
||||
SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
}
|
||||
@@ -1,34 +1,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
|
||||
<RuntimeIdentifiers>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-2022</Copyright>
|
||||
<Version>2.4</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>2.7.3</VersionPrefix>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
<PropertyGroup Condition="'$(TargetFramework)'!='net48'">
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\ring-code-guide-1-layer.png" />
|
||||
<Resource Include="Images\ring-code-guide-2-layer.png" />
|
||||
@@ -36,15 +21,14 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<Reference Include="PresentationFramework.Aero" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Images\" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
524
MPF.UI.Core/Theme.cs
Normal file
524
MPF.UI.Core/Theme.cs
Normal file
@@ -0,0 +1,524 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MPF.UI.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents all required mapping values for the UI
|
||||
/// </summary>
|
||||
public abstract class Theme
|
||||
{
|
||||
#region Application-Wide
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush used to paint the active window's border.
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public SolidColorBrush ActiveBorderBrush { get; protected set; }
|
||||
#else
|
||||
public SolidColorBrush? ActiveBorderBrush { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints the face of a three-dimensional display element.
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public SolidColorBrush ControlBrush { get; protected set; }
|
||||
#else
|
||||
public SolidColorBrush? ControlBrush { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints text in a three-dimensional display element.
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public SolidColorBrush ControlTextBrush { get; protected set; }
|
||||
#else
|
||||
public SolidColorBrush? ControlTextBrush { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints disabled text.
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public SolidColorBrush GrayTextBrush { get; protected set; }
|
||||
#else
|
||||
public SolidColorBrush? GrayTextBrush { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints the background of a window's client area.
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public SolidColorBrush WindowBrush { get; protected set; }
|
||||
#else
|
||||
public SolidColorBrush? WindowBrush { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints the text in the client area of a window.
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public SolidColorBrush WindowTextBrush { get; protected set; }
|
||||
#else
|
||||
public SolidColorBrush? WindowTextBrush { get; protected set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Button
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.Disabled.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush Button_Disabled_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? Button_Disabled_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.MouseOver.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush Button_MouseOver_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? Button_MouseOver_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.Pressed.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush Button_Pressed_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? Button_Pressed_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.Static.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush Button_Static_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? Button_Static_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region ComboBox
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Disabled.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_Disabled_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_Disabled_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Disabled.Editable.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_Disabled_Editable_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_Disabled_Editable_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Disabled.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_Disabled_Editable_Button_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_Disabled_Editable_Button_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.MouseOver.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_MouseOver_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_MouseOver_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.MouseOver.Editable.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_MouseOver_Editable_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_MouseOver_Editable_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.MouseOver.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_MouseOver_Editable_Button_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_MouseOver_Editable_Button_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Pressed.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_Pressed_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_Pressed_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Pressed.Editable.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_Pressed_Editable_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_Pressed_Editable_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Pressed.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_Pressed_Editable_Button_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_Pressed_Editable_Button_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Static.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_Static_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_Static_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Static.Editable.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_Static_Editable_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_Static_Editable_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Static.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ComboBox_Static_Editable_Button_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ComboBox_Static_Editable_Button_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region CustomMessageBox
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the CustomMessageBox.Static.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush CustomMessageBox_Static_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? CustomMessageBox_Static_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region MenuItem
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the MenuItem.SubMenu.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush MenuItem_SubMenu_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? MenuItem_SubMenu_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the MenuItem.SubMenu.Border resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush MenuItem_SubMenu_Border { get; protected set; }
|
||||
#else
|
||||
public Brush? MenuItem_SubMenu_Border { get; protected set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region ProgressBar
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ProgressBar.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ProgressBar_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ProgressBar_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region ScrollViewer
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ScrollViewer.ScrollBar.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush ScrollViewer_ScrollBar_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? ScrollViewer_ScrollBar_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region TabItem
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the TabItem.Selected.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush TabItem_Selected_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? TabItem_Selected_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the TabItem.Static.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush TabItem_Static_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? TabItem_Static_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the TabItem.Static.Border resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush TabItem_Static_Border { get; protected set; }
|
||||
#else
|
||||
public Brush? TabItem_Static_Border { get; protected set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region TextBox
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the TextBox.Static.Background resource
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public Brush TextBox_Static_Background { get; protected set; }
|
||||
#else
|
||||
public Brush? TextBox_Static_Background { get; protected set; }
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Apply the theme to the current application
|
||||
/// </summary>
|
||||
public void Apply()
|
||||
{
|
||||
// Handle application-wide resources
|
||||
Application.Current.Resources[SystemColors.ActiveBorderBrushKey] = this.ActiveBorderBrush;
|
||||
Application.Current.Resources[SystemColors.ControlBrushKey] = this.ControlBrush;
|
||||
Application.Current.Resources[SystemColors.ControlTextBrushKey] = this.ControlTextBrush;
|
||||
Application.Current.Resources[SystemColors.GrayTextBrushKey] = this.GrayTextBrush;
|
||||
Application.Current.Resources[SystemColors.WindowBrushKey] = this.WindowBrush;
|
||||
Application.Current.Resources[SystemColors.WindowTextBrushKey] = this.WindowTextBrush;
|
||||
|
||||
// Handle Button-specific resources
|
||||
Application.Current.Resources["Button.Disabled.Background"] = this.Button_Disabled_Background;
|
||||
Application.Current.Resources["Button.MouseOver.Background"] = this.Button_MouseOver_Background;
|
||||
Application.Current.Resources["Button.Pressed.Background"] = this.Button_Pressed_Background;
|
||||
Application.Current.Resources["Button.Static.Background"] = this.Button_Static_Background;
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
Application.Current.Resources["ComboBox.Disabled.Background"] = this.ComboBox_Disabled_Background;
|
||||
Application.Current.Resources["ComboBox.Disabled.Editable.Background"] = this.ComboBox_Disabled_Editable_Background;
|
||||
Application.Current.Resources["ComboBox.Disabled.Editable.Button.Background"] = this.ComboBox_Disabled_Editable_Button_Background;
|
||||
Application.Current.Resources["ComboBox.MouseOver.Background"] = this.ComboBox_MouseOver_Background;
|
||||
Application.Current.Resources["ComboBox.MouseOver.Editable.Background"] = this.ComboBox_MouseOver_Editable_Background;
|
||||
Application.Current.Resources["ComboBox.MouseOver.Editable.Button.Background"] = this.ComboBox_MouseOver_Editable_Button_Background;
|
||||
Application.Current.Resources["ComboBox.Pressed.Background"] = this.ComboBox_Pressed_Background;
|
||||
Application.Current.Resources["ComboBox.Pressed.Editable.Background"] = this.ComboBox_Pressed_Editable_Background;
|
||||
Application.Current.Resources["ComboBox.Pressed.Editable.Button.Background"] = this.ComboBox_Pressed_Editable_Button_Background;
|
||||
Application.Current.Resources["ComboBox.Static.Background"] = this.ComboBox_Static_Background;
|
||||
Application.Current.Resources["ComboBox.Static.Editable.Background"] = this.ComboBox_Static_Editable_Background;
|
||||
Application.Current.Resources["ComboBox.Static.Editable.Button.Background"] = this.ComboBox_Static_Editable_Button_Background;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
Application.Current.Resources["CustomMessageBox.Static.Background"] = this.CustomMessageBox_Static_Background;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
Application.Current.Resources["MenuItem.SubMenu.Background"] = this.MenuItem_SubMenu_Background;
|
||||
Application.Current.Resources["MenuItem.SubMenu.Border"] = this.MenuItem_SubMenu_Border;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
Application.Current.Resources["ProgressBar.Background"] = this.ProgressBar_Background;
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
Application.Current.Resources["ScrollViewer.ScrollBar.Background"] = this.ScrollViewer_ScrollBar_Background;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
Application.Current.Resources["TabItem.Selected.Background"] = this.TabItem_Selected_Background;
|
||||
Application.Current.Resources["TabItem.Static.Background"] = this.TabItem_Static_Background;
|
||||
Application.Current.Resources["TabItem.Static.Border"] = this.TabItem_Static_Border;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
Application.Current.Resources["TextBox.Static.Background"] = this.TextBox_Static_Background;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default light-mode theme
|
||||
/// </summary>
|
||||
public class LightModeTheme : Theme
|
||||
{
|
||||
public LightModeTheme()
|
||||
{
|
||||
// Handle application-wide resources
|
||||
this.ActiveBorderBrush = null;
|
||||
this.ControlBrush = null;
|
||||
this.ControlTextBrush = null;
|
||||
this.GrayTextBrush = null;
|
||||
this.WindowBrush = null;
|
||||
this.WindowTextBrush = null;
|
||||
|
||||
// Handle Button-specific resources
|
||||
this.Button_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF4));
|
||||
this.Button_MouseOver_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xBE, 0xE6, 0xFD));
|
||||
this.Button_Pressed_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xC4, 0xE5, 0xF6));
|
||||
this.Button_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xDD, 0xDD, 0xDD));
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
this.ComboBox_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
|
||||
this.ComboBox_Disabled_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Disabled_Editable_Button_Background = Brushes.Transparent;
|
||||
this.ComboBox_MouseOver_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xEC, 0xF4, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_MouseOver_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_MouseOver_Editable_Button_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xEB, 0xF4, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Pressed_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xDA, 0xEC, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Pressed_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Pressed_Editable_Button_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xDA, 0xEB, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Static_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
|
||||
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Static_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Static_Editable_Button_Background = Brushes.Transparent;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
this.CustomMessageBox_Static_Background = null;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
this.MenuItem_SubMenu_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
|
||||
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
this.ProgressBar_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xE6, 0xE6, 0xE6));
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
this.ScrollViewer_ScrollBar_Background = Brushes.LightGray;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
this.TabItem_Selected_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.TabItem_Static_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
|
||||
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.TabItem_Static_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
this.TextBox_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default dark-mode theme
|
||||
/// </summary>
|
||||
public class DarkModeTheme : Theme
|
||||
{
|
||||
public DarkModeTheme()
|
||||
{
|
||||
// Setup needed brushes
|
||||
var darkModeBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, 0x20, 0x20, 0x20) };
|
||||
|
||||
// Handle application-wide resources
|
||||
this.ActiveBorderBrush = Brushes.Black;
|
||||
this.ControlBrush = darkModeBrush;
|
||||
this.ControlTextBrush = Brushes.White;
|
||||
this.GrayTextBrush = Brushes.DarkGray;
|
||||
this.WindowBrush = darkModeBrush;
|
||||
this.WindowTextBrush = Brushes.White;
|
||||
|
||||
// Handle Button-specific resources
|
||||
this.Button_Disabled_Background = darkModeBrush;
|
||||
this.Button_MouseOver_Background = darkModeBrush;
|
||||
this.Button_Pressed_Background = darkModeBrush;
|
||||
this.Button_Static_Background = darkModeBrush;
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
this.ComboBox_Disabled_Background = darkModeBrush;
|
||||
this.ComboBox_Disabled_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Disabled_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Editable_Button_Background = darkModeBrush;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
this.CustomMessageBox_Static_Background = darkModeBrush;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
this.MenuItem_SubMenu_Background = darkModeBrush;
|
||||
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
this.ProgressBar_Background = darkModeBrush;
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
this.ScrollViewer_ScrollBar_Background = darkModeBrush;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
this.TabItem_Selected_Background = darkModeBrush;
|
||||
this.TabItem_Static_Background = darkModeBrush;
|
||||
this.TabItem_Static_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
this.TextBox_Static_Background = darkModeBrush;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
<UserControl x:Class="MPF.UserControls.LogOutput"
|
||||
<UserControl x:Class="MPF.UI.Core.UserControls.LogOutput"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:MPF.UserControls"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
228
MPF.UI.Core/UserControls/LogOutput.xaml.cs
Normal file
228
MPF.UI.Core/UserControls/LogOutput.xaml.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using MPF.Core.Data;
|
||||
|
||||
namespace MPF.UI.Core.UserControls
|
||||
{
|
||||
public partial class LogOutput : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Document representing the text
|
||||
/// </summary>
|
||||
internal FlowDocument Document { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Queue of items that need to be logged
|
||||
/// </summary>
|
||||
internal ProcessingQueue<LogLine> LogQueue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Paragraph backing the log
|
||||
/// </summary>
|
||||
private readonly Paragraph _paragraph;
|
||||
|
||||
/// <summary>
|
||||
/// Cached value of the last line written
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private Run lastLine = null;
|
||||
#else
|
||||
private Run? lastLine = null;
|
||||
#endif
|
||||
|
||||
public LogOutput()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Update the internal state
|
||||
Document = new FlowDocument()
|
||||
{
|
||||
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
|
||||
};
|
||||
_paragraph = new Paragraph();
|
||||
Document.Blocks.Add(_paragraph);
|
||||
|
||||
// Setup the processing queue
|
||||
LogQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
|
||||
|
||||
// Add handlers
|
||||
OutputViewer.SizeChanged += OutputViewerSizeChanged;
|
||||
Output.TextChanged += OnTextChanged;
|
||||
ClearButton.Click += OnClearButton;
|
||||
SaveButton.Click += OnSaveButton;
|
||||
|
||||
// Update the internal state
|
||||
Output.Document = Document;
|
||||
}
|
||||
|
||||
#region Logging
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue text to the log with formatting
|
||||
/// </summary>
|
||||
/// <param name="logLevel">LogLevel for the log</param>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void EnqueueLog(LogLevel logLevel, string text)
|
||||
{
|
||||
// Null text gets ignored
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
// Enqueue the text
|
||||
LogQueue.Enqueue(new LogLine(text, logLevel));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log line wrapper
|
||||
/// </summary>
|
||||
internal readonly struct LogLine
|
||||
{
|
||||
public readonly string Text;
|
||||
public readonly LogLevel LogLevel;
|
||||
|
||||
public LogLine(string text, LogLevel logLevel)
|
||||
{
|
||||
this.Text = text;
|
||||
this.LogLevel = logLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the foreground Brush for the current LogLevel
|
||||
/// </summary>
|
||||
/// <returns>Brush representing the color</returns>
|
||||
public Brush GetForegroundColor()
|
||||
{
|
||||
switch (this.LogLevel)
|
||||
{
|
||||
case LogLevel.SECRET:
|
||||
return Brushes.Blue;
|
||||
case LogLevel.ERROR:
|
||||
return Brushes.Red;
|
||||
case LogLevel.VERBOSE:
|
||||
return Brushes.Yellow;
|
||||
case LogLevel.USER:
|
||||
default:
|
||||
return Brushes.White;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a Run object from the current LogLine
|
||||
/// </summary>
|
||||
/// <returns>Run object based on internal values</returns>
|
||||
public Run GenerateRun()
|
||||
{
|
||||
return new Run { Text = this.Text, Foreground = GetForegroundColor() };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the log lines in the queue
|
||||
/// </summary>
|
||||
/// <param name="nextLogLine">LogLine item to process</param>
|
||||
internal void ProcessLogLine(LogLine nextLogLine)
|
||||
{
|
||||
// Null text gets ignored
|
||||
string nextText = nextLogLine.Text;
|
||||
if (nextText == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (nextText.StartsWith("\r"))
|
||||
ReplaceLastLine(nextLogLine);
|
||||
else
|
||||
AppendToTextBox(nextLogLine);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// In the event that something fails horribly, we want to log
|
||||
AppendToTextBox(new LogLine(ex.ToString(), LogLevel.ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append log line to the log text box
|
||||
/// </summary>
|
||||
/// <param name="logLine">LogLine value to append</param>
|
||||
private void AppendToTextBox(LogLine logLine)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
var run = logLine.GenerateRun();
|
||||
_paragraph.Inlines.Add(run);
|
||||
lastLine = run;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace the last line written to the log text box
|
||||
/// </summary>
|
||||
/// <param name="logLine">LogLine value to append</param>
|
||||
private void ReplaceLastLine(LogLine logLine)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
#if NET48
|
||||
if (lastLine == null) lastLine = new Run();
|
||||
#else
|
||||
lastLine ??= new Run();
|
||||
#endif
|
||||
lastLine.Text = logLine.Text;
|
||||
lastLine.Foreground = logLine.GetForegroundColor();
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Clear all inlines of the paragraph
|
||||
/// </summary>
|
||||
private void ClearInlines() => _paragraph.Inlines.Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Save all inlines to console.log
|
||||
/// </summary>
|
||||
private void SaveInlines()
|
||||
{
|
||||
using (var sw = new StreamWriter(File.OpenWrite("console.log")))
|
||||
{
|
||||
foreach (var inline in _paragraph.Inlines)
|
||||
{
|
||||
if (inline is Run run)
|
||||
sw.Write(run.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scroll the current view to the bottom
|
||||
/// </summary>
|
||||
public void ScrollToBottom() => OutputViewer.ScrollToBottom();
|
||||
|
||||
#endregion
|
||||
|
||||
#region EventHandlers
|
||||
|
||||
private void OnClearButton(object sender, EventArgs e)
|
||||
=> ClearInlines();
|
||||
|
||||
private void OnSaveButton(object sender, EventArgs e)
|
||||
=> SaveInlines();
|
||||
|
||||
private void OnTextChanged(object sender, TextChangedEventArgs e)
|
||||
=> ScrollToBottom();
|
||||
|
||||
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
=> ScrollToBottom();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,829 +0,0 @@
|
||||
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 (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.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.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.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.SSHash] = Parent.SSHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = Parent.SSVersion.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
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,17 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
|
||||
xmlns:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:redump="clr-namespace:SabreTools.RedumpLib.Data;assembly=SabreTools.RedumpLib"
|
||||
xmlns:windows="clr-namespace:MPF.UI.Core.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Disc Information" Width="515" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
@@ -74,10 +79,12 @@
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput Label="Title"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.Title, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="AlternativeTitleTextBox" Label="Alternative Title"/>
|
||||
<controls:UserInput x:Name="AlternativeTitleTextBox" Label="Alternative Title"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AlternativeTitle], Mode=TwoWay}"/>
|
||||
<controls:UserInput Label="Foreign Title (Non-Latin)"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.ForeignTitleNonLatin, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="AlternativeForeignTitleTextBox" Label="Alternative Foreign Title"/>
|
||||
<controls:UserInput x:Name="AlternativeForeignTitleTextBox" Label="Alternative Foreign Title"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AlternativeForeignTitle], Mode=TwoWay}"/>
|
||||
<controls:UserInput Label="Disc Number / Letter"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.DiscNumberLetter, Mode=TwoWay}"/>
|
||||
<controls:UserInput Label="Disc Title"
|
||||
@@ -90,8 +97,9 @@
|
||||
</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}" />
|
||||
<ComboBox x:Name="CategoryComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Categories}" SelectedItem="{Binding SubmissionInfo.CommonDiscInfo.Category, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
@@ -101,8 +109,9 @@
|
||||
</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}" />
|
||||
<ComboBox x:Name="RegionComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Regions}" SelectedItem="{Binding SubmissionInfo.CommonDiscInfo.Region, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
@@ -112,7 +121,8 @@
|
||||
</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}"
|
||||
<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>
|
||||
@@ -129,7 +139,8 @@
|
||||
</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}"
|
||||
<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>
|
||||
@@ -164,7 +175,8 @@
|
||||
<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="GenreTextBox" Label="Genre"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Genre], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ProtectionTextBox" Label="Protection" ToolTip="CAUTION: Only edit if you know what you are doing!"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.Protection, Mode=TwoWay}" TextHeight="75" TextWrapping="Wrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
@@ -175,9 +187,11 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label/>
|
||||
<CheckBox Grid.Column="1" x:Name="PostgapTypeCheckBox" Content="Postgap type (Form 2)" Margin="0,4"/>
|
||||
<CheckBox Grid.Column="1" x:Name="PostgapTypeCheckBox" Content="Postgap type (Form 2)" Margin="0,4"
|
||||
IsChecked="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PostgapType], Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
<controls:UserInput x:Name="SeriesTextBox" Label="Series"/>
|
||||
<controls:UserInput x:Name="SeriesTextBox" Label="Series"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Series], Mode=TwoWay}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="0.75*" />
|
||||
@@ -185,7 +199,8 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label/>
|
||||
<CheckBox Grid.Column="1" x:Name="VCDCheckBox" Content="V-CD" Margin="0,4"/>
|
||||
<CheckBox Grid.Column="1" x:Name="VCDCheckBox" Content="V-CD" Margin="0,4"
|
||||
IsChecked="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VCD], Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
@@ -193,42 +208,74 @@
|
||||
<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"/>
|
||||
<controls:UserInput x:Name="BBFCRegistrationNumberTextBox" Label="BBFC Reg. No."
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BBFCRegistrationNumber], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="DiscHologramIDTextBox" Label="Disc Hologram ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DiscHologramID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="DNASDiscIDTextBox" Label="DNAS Disc ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DNASDiscID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ISBNTextBox" Label="ISBN"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ISBN], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ISSNTextBox" Label="ISSN"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ISSN], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="PPNTextBox" Label="PPN"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PPN], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="VFCCodeTextBox" Label="VFC code"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VFCCode], Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Publisher Identifiers" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput x:Name="AcclaimIDTextBox" Label="Acclaim ID"/>
|
||||
<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"/>
|
||||
<controls:UserInput x:Name="AcclaimIDTextBox" Label="Acclaim ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AcclaimID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ActivisionIDTextBox" Label="Activision ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ActivisionID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="BandaiIDTextBox" Label="Bandai ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BandaiID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="CDProjektIDTextBox" Label="CD Projekt ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)CDProjektID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ElectronicArtsIDTextBox" Label="Electronic Arts ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ElectronicArtsID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="FoxInteractiveIDTextBox" Label="Fox Interactive ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)FoxInteractiveID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="GTInteractiveIDTextBox" Label="GT Interactive ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)GTInteractiveID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="JASRACIDTextBox" Label="JASRAC ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)JASRACID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="KingRecordsIDTextBox" Label="King Records ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KingRecordsID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="KoeiIDTextBox" Label="Koei ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KoeiID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="KonamiIDTextBox" Label="Konami ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KonamiID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="LucasArtsIDTextBox" Label="LucasArts ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)LucasArtsID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="MicrosoftIDTextBox" Label="Microsoft ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)MicrosoftID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="NaganoIDTextBox" Label="Nagano ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NaganoID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="NamcoIDTextBox" Label="Namco ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NamcoID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="NipponIchiSoftwareIDTextBox" Label="Nippon Ichi Software ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NipponIchiSoftwareID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="OriginIDTextBox" Label="Origin ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)OriginID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="PonyCanyonIDTextBox" Label="Pony Canyon ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PonyCanyonID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SegaIDTextBox" Label="Sega ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SegaID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SelenIDTextBox" Label="Selen ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SelenID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SierraIDTextBox" Label="Sierra ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SierraID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="TaitoIDTextBox" Label="Taito ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)TaitoID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="UbisoftIDTextBox" Label="Ubisoft ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)UbisoftID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ValveIDTextBox" Label="Valve ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ValveID], Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
@@ -244,31 +291,41 @@
|
||||
|
||||
<!-- Games -->
|
||||
<controls:UserInput x:Name="GamesTextBox" Label="Games" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Games], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="NetYarozeGamesTextBox" Label="Net Yaroze Games" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)NetYarozeGames], Mode=TwoWay}"/>
|
||||
|
||||
<!-- Demos -->
|
||||
<controls:UserInput x:Name="PlayableDemosTextBox" Label="Playable Demos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)PlayableDemos], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="RollingDemosTextBox" Label="Rolling Demos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)RollingDemos], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="TechDemosTextBox" Label="Tech Demos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)TechDemos], Mode=TwoWay}"/>
|
||||
|
||||
<!-- Video -->
|
||||
<controls:UserInput x:Name="GameFootageTextBox" Label="Game Footage" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)GameFootage], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="VideosTextBox" Label="Videos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Videos], Mode=TwoWay}"/>
|
||||
|
||||
<!-- Miscellaneous -->
|
||||
<controls:UserInput x:Name="PatchesTextBox" Label="Patches" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Patches], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SavegamesTextBox" Label="Savegames" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Savegames], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ExtrasTextBox" Label="Extras" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Extras], Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
@@ -338,48 +395,68 @@
|
||||
<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="FullyMatchedID" Label="Fully Matched ID" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.FullyMatchedID, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="PartiallyMatchedIDs" Label="Partially Matched ID(s)" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"/>
|
||||
<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="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.AntiModchip, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="DiscOffset" Label="Disc Offset" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.TracksAndWriteOffsets.OtherWriteOffsets, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="DMIHash" Label="DMI Hash" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DMIHash], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="EDC" Label="EDC" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.EDC.EDC, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ErrorsCount" Label="Error(s) Count" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.ErrorsCount, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="EXEDateBuildDate" Label="EXE/Build Date" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.EXEDateBuildDate, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="Filename" Label="Filename" IsReadOnly="True"
|
||||
TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Filename], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="Header" Label="Header" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.Header, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="InternalName" Label="Internal Names" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="InternalName" Label="Internal Names" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)InternalName], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)InternalSerialName], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="Multisession" Label="Multisession" IsReadOnly="True" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"/>
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Multisession], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.LibCrypt, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="LibCryptData" Label="LibCrypt Data" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.LibCryptData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="PFIHash" Label="PFI Hash" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="PFIHash" Label="PFI Hash" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PFIHash], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="PIC" Label="PIC" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.PIC, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="PVD" Label="PVD" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.PVD, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="RingNonZeroDataStart" Label="Ring Non-Zero Data Start" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)RingNonZeroDataStart], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SecuROMData" Label="SecuROM Data" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.SecuROMData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="SSHash" Label="Security Sector Hash" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="SSHash" Label="Security Sector Hash" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SSHash], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SecuritySectorRanges" Label="Security Sector Ranges" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.SecuritySectorRanges, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="SSVersion" Label="Security Sector Version" IsReadOnly="True"/>
|
||||
<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"/>
|
||||
<controls:UserInput x:Name="SSVersion" Label="Security Sector Version" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SSVersion], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="UniversalHash" Label="Universal Hash (SHA-1)" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)UniversalHash], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="VolumeLabel" Label="Volume Label" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VolumeLabel], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="XeMID" Label="XeMID" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)XeMID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="XMID" Label="XMID" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)XMID], Mode=TwoWay}"/>
|
||||
|
||||
<!-- TODO: Add track/checksum data once there's a reasonable way to do so -->
|
||||
</StackPanel>
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using MPF.Core.Data;
|
||||
using MPF.UI.Core.ViewModels;
|
||||
using RedumpLib.Data;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ViewModels;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
@@ -12,15 +15,388 @@ namespace MPF.UI.Core.Windows
|
||||
/// <summary>
|
||||
/// Read-only access to the current disc information view model
|
||||
/// </summary>
|
||||
public DiscInformationViewModel DiscInformationViewModel => DataContext as DiscInformationViewModel;
|
||||
public DiscInformationViewModel DiscInformationViewModel => DataContext as DiscInformationViewModel ?? new DiscInformationViewModel(new Options(), new SubmissionInfo());
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public DiscInformationWindow(Options options, SubmissionInfo submissionInfo)
|
||||
#else
|
||||
public DiscInformationWindow(Options options, SubmissionInfo? submissionInfo)
|
||||
#endif
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new DiscInformationViewModel(this, options, submissionInfo);
|
||||
DataContext = new DiscInformationViewModel(options, submissionInfo);
|
||||
DiscInformationViewModel.Load();
|
||||
|
||||
// Limit lists, if necessary
|
||||
if (options.EnableRedumpCompatibility)
|
||||
{
|
||||
DiscInformationViewModel.SetRedumpRegions();
|
||||
DiscInformationViewModel.SetRedumpLanguages();
|
||||
}
|
||||
|
||||
// Add handlers
|
||||
AcceptButton.Click += OnAcceptClick;
|
||||
CancelButton.Click += OnCancelClick;
|
||||
RingCodeGuideButton.Click += OnRingCodeGuideClick;
|
||||
|
||||
// Update UI with new values
|
||||
ManipulateFields(options, submissionInfo);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Manipulate fields based on the current disc
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private void ManipulateFields(Options options, SubmissionInfo submissionInfo)
|
||||
#else
|
||||
private void ManipulateFields(Options options, SubmissionInfo? submissionInfo)
|
||||
#endif
|
||||
{
|
||||
// Enable tabs in all fields, if required
|
||||
if (options.EnableTabsInInputFields)
|
||||
EnableTabsInInputFields();
|
||||
|
||||
// Hide read-only fields that don't have values set
|
||||
HideReadOnlyFields(submissionInfo);
|
||||
|
||||
// Different media types mean different fields available
|
||||
UpdateFromDiscType(submissionInfo);
|
||||
|
||||
// Different systems mean different fields available
|
||||
UpdateFromSystemType(submissionInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable tab entry on supported fields
|
||||
/// </summary>
|
||||
/// TODO: See if these can be done by binding
|
||||
private void EnableTabsInInputFields()
|
||||
{
|
||||
// Additional Information
|
||||
CommentsTextBox.Tab = true;
|
||||
|
||||
// Contents
|
||||
GeneralContent.Tab = true;
|
||||
GamesTextBox.Tab = true;
|
||||
NetYarozeGamesTextBox.Tab = true;
|
||||
PlayableDemosTextBox.Tab = true;
|
||||
RollingDemosTextBox.Tab = true;
|
||||
TechDemosTextBox.Tab = true;
|
||||
GameFootageTextBox.Tab = true;
|
||||
VideosTextBox.Tab = true;
|
||||
PatchesTextBox.Tab = true;
|
||||
SavegamesTextBox.Tab = true;
|
||||
ExtrasTextBox.Tab = true;
|
||||
|
||||
// L0
|
||||
L0MasteringRing.Tab = true;
|
||||
L0MasteringSID.Tab = true;
|
||||
L0Toolstamp.Tab = true;
|
||||
L0MouldSID.Tab = true;
|
||||
L0AdditionalMould.Tab = true;
|
||||
|
||||
// L1
|
||||
L1MasteringRing.Tab = true;
|
||||
L1MasteringSID.Tab = true;
|
||||
L1Toolstamp.Tab = true;
|
||||
L1MouldSID.Tab = true;
|
||||
L1AdditionalMould.Tab = true;
|
||||
|
||||
// L2
|
||||
L2MasteringRing.Tab = true;
|
||||
L2MasteringSID.Tab = true;
|
||||
L2Toolstamp.Tab = true;
|
||||
|
||||
// L3
|
||||
L3MasteringRing.Tab = true;
|
||||
L3MasteringSID.Tab = true;
|
||||
L3Toolstamp.Tab = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide any optional, read-only fields if they don't have a value
|
||||
/// </summary>
|
||||
/// TODO: Figure out how to bind the PartiallyMatchedIDs array to a text box
|
||||
/// TODO: Convert visibility to a binding
|
||||
#if NET48
|
||||
private void HideReadOnlyFields(SubmissionInfo submissionInfo)
|
||||
#else
|
||||
private void HideReadOnlyFields(SubmissionInfo? submissionInfo)
|
||||
#endif
|
||||
{
|
||||
// If there's no submission information
|
||||
if (submissionInfo == null)
|
||||
return;
|
||||
|
||||
if (submissionInfo.FullyMatchedID == null)
|
||||
FullyMatchedID.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.PartiallyMatchedIDs == null)
|
||||
PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
PartiallyMatchedIDs.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs);
|
||||
if (submissionInfo.CopyProtection?.AntiModchip == null)
|
||||
AntiModchip.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.TracksAndWriteOffsets?.OtherWriteOffsets == null)
|
||||
DiscOffset.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.Keys?.Contains(SiteCode.DMIHash) != true)
|
||||
DMIHash.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.EDC?.EDC == null)
|
||||
EDC.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.CommonDiscInfo?.ErrorsCount))
|
||||
ErrorsCount.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
|
||||
EXEDateBuildDate.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
|
||||
Filename.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.Header))
|
||||
Header.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
|
||||
InternalName.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalSerialName) != true)
|
||||
InternalSerialName.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Multisession) != true)
|
||||
Multisession.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CopyProtection?.LibCrypt == null)
|
||||
LibCrypt.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.CopyProtection?.LibCryptData))
|
||||
LibCryptData.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
|
||||
PFIHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.PIC))
|
||||
PIC.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.PVD))
|
||||
PVD.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
|
||||
RingNonZeroDataStart.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.CopyProtection?.SecuROMData))
|
||||
SecuROMData.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
|
||||
SSHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.SecuritySectorRanges))
|
||||
SecuritySectorRanges.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
|
||||
SSVersion.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) != true)
|
||||
UniversalHash.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.VolumeLabel) != true)
|
||||
VolumeLabel.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XeMID) != true)
|
||||
XeMID.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
|
||||
XMID.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visible fields and sections based on the media type
|
||||
/// </summary>
|
||||
/// TODO: See if these can be done by binding
|
||||
#if NET48
|
||||
private void UpdateFromDiscType(SubmissionInfo submissionInfo)
|
||||
#else
|
||||
private void UpdateFromDiscType(SubmissionInfo? submissionInfo)
|
||||
#endif
|
||||
{
|
||||
// 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:
|
||||
L0Info.Header = "Data Side";
|
||||
L0MasteringRing.Label = "Mastering Ring";
|
||||
L0MasteringSID.Label = "Mastering SID";
|
||||
L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L0MouldSID.Label = "Mould SID";
|
||||
L0AdditionalMould.Label = "Additional Mould";
|
||||
|
||||
L1Info.Header = "Label Side";
|
||||
L1MasteringRing.Visibility = Visibility.Collapsed;
|
||||
L1MasteringSID.Visibility = Visibility.Collapsed;
|
||||
L1Toolstamp.Visibility = Visibility.Collapsed;
|
||||
L1MouldSID.Label = "Mould SID";
|
||||
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))
|
||||
{
|
||||
L2Info.Visibility = Visibility.Visible;
|
||||
L3Info.Visibility = Visibility.Visible;
|
||||
|
||||
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
L0MasteringRing.Label = "Mastering Ring";
|
||||
L0MasteringSID.Label = "Mastering SID";
|
||||
L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L0MouldSID.Label = "Data Side Mould SID";
|
||||
L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
L1Info.Header = "Layer 1";
|
||||
L1MasteringRing.Label = "Mastering Ring";
|
||||
L1MasteringSID.Label = "Mastering SID";
|
||||
L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L1MouldSID.Label = "Label Side Mould SID";
|
||||
L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
|
||||
L2Info.Header = "Layer 2";
|
||||
L2MasteringRing.Label = "Mastering Ring";
|
||||
L2MasteringSID.Label = "Mastering SID";
|
||||
L2Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
|
||||
L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
|
||||
L3MasteringRing.Label = "Mastering Ring";
|
||||
L3MasteringSID.Label = "Mastering SID";
|
||||
L3Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
}
|
||||
|
||||
// Triple-layer discs
|
||||
else if (submissionInfo?.SizeAndChecksums?.Layerbreak2 != default(long))
|
||||
{
|
||||
L2Info.Visibility = Visibility.Visible;
|
||||
|
||||
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
L0MasteringRing.Label = "Mastering Ring";
|
||||
L0MasteringSID.Label = "Mastering SID";
|
||||
L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L0MouldSID.Label = "Data Side Mould SID";
|
||||
L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
L1Info.Header = "Layer 1";
|
||||
L1MasteringRing.Label = "Mastering Ring";
|
||||
L1MasteringSID.Label = "Mastering SID";
|
||||
L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L1MouldSID.Label = "Label Side Mould SID";
|
||||
L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
|
||||
L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
|
||||
L2MasteringRing.Label = "Mastering Ring";
|
||||
L2MasteringSID.Label = "Mastering SID";
|
||||
L2Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
}
|
||||
|
||||
// Double-layer discs
|
||||
else if (submissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
|
||||
{
|
||||
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
L0MasteringRing.Label = "Mastering Ring";
|
||||
L0MasteringSID.Label = "Mastering SID";
|
||||
L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L0MouldSID.Label = "Data Side Mould SID";
|
||||
L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
|
||||
L1MasteringRing.Label = "Mastering Ring";
|
||||
L1MasteringSID.Label = "Mastering SID";
|
||||
L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L1MouldSID.Label = "Label Side Mould SID";
|
||||
L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
}
|
||||
|
||||
// Single-layer discs
|
||||
else
|
||||
{
|
||||
L0Info.Header = "Data Side";
|
||||
L0MasteringRing.Label = "Mastering Ring";
|
||||
L0MasteringSID.Label = "Mastering SID";
|
||||
L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L0MouldSID.Label = "Mould SID";
|
||||
L0AdditionalMould.Label = "Additional Mould";
|
||||
|
||||
L1Info.Header = "Label Side";
|
||||
L1MasteringRing.Label = "Mastering Ring";
|
||||
L1MasteringSID.Label = "Mastering SID";
|
||||
L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L1MouldSID.Label = "Mould SID";
|
||||
L1AdditionalMould.Label = "Additional Mould";
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// All other media we assume to have no rings
|
||||
default:
|
||||
L0Info.Visibility = Visibility.Collapsed;
|
||||
L1Info.Visibility = Visibility.Collapsed;
|
||||
L2Info.Visibility = Visibility.Collapsed;
|
||||
L3Info.Visibility = Visibility.Collapsed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visible fields and sections based on the system type
|
||||
/// </summary>
|
||||
/// TODO: See if these can be done by binding
|
||||
#if NET48
|
||||
private void UpdateFromSystemType(SubmissionInfo submissionInfo)
|
||||
#else
|
||||
private void UpdateFromSystemType(SubmissionInfo? submissionInfo)
|
||||
#endif
|
||||
{
|
||||
var system = submissionInfo?.CommonDiscInfo?.System;
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
LanguageSelectionGrid.Visibility = Visibility.Visible;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AcceptButton Click event
|
||||
/// </summary>
|
||||
private void OnAcceptClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DiscInformationViewModel.Save();
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CancelButton Click event
|
||||
/// </summary>
|
||||
private void OnCancelClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for RingCodeGuideButton Click event
|
||||
/// </summary>
|
||||
private void OnRingCodeGuideClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var ringCodeGuideWindow = new RingCodeGuideWindow()
|
||||
{
|
||||
Owner = this,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
ringCodeGuideWindow.Show();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
<coreWindows:WindowBase x:Class="MPF.Windows.MainWindow"
|
||||
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UserControls"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows;assembly=MPF.UI.Core"
|
||||
xmlns:viewModels="clr-namespace:MPF.UI.ViewModels"
|
||||
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
|
||||
xmlns:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
|
||||
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
|
||||
mc:Ignorable="d"
|
||||
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Window.DataContext>
|
||||
<viewModels:MainViewModel/>
|
||||
</Window.DataContext>
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
@@ -52,7 +55,8 @@
|
||||
<MenuItem x:Name="OptionsMenuItem" Header="_Options" HorizontalAlignment="Left" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}" />
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"
|
||||
IsEnabled="{Binding OptionsMenuItemEnabled}"/>
|
||||
<MenuItem x:Name="DebugViewMenuItem" Header="_Debug Info Window" HorizontalAlignment="Left" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
@@ -123,7 +127,9 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label x:Name="SystemMediaTypeLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
|
||||
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left" ItemsSource="{Binding Systems}" SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding Systems}" SelectedItem="{Binding Path=CurrentSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding SystemTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ComboBoxItem}">
|
||||
<Style.Triggers>
|
||||
@@ -134,18 +140,21 @@
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right"
|
||||
ItemsSource="{Binding MediaTypes}" SelectedItem="{Binding Path=CurrentMediaType, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding MediaTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="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="OutputPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="Output Path"/>
|
||||
<TextBox x:Name="OutputPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding OutputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding OutputPathTextBoxEnabled}" />
|
||||
<Button x:Name="OutputPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
IsEnabled="{Binding OutputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
|
||||
|
||||
<Label x:Name="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"
|
||||
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"
|
||||
ItemsSource="{Binding Drives}" SelectedItem="{Binding Path=CurrentDrive, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding DriveLetterComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Letter}" />
|
||||
@@ -153,35 +162,49 @@
|
||||
</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"
|
||||
ItemsSource="{Binding DriveSpeeds}" SelectedItem="{Binding Path=DriveSpeed, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding DriveSpeedComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="DumpingProgramLabel" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Content="Dumping Program"/>
|
||||
<ComboBox x:Name="DumpingProgramComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding InternalPrograms}" SelectedItem="{Binding Path=CurrentProgram, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding DumpingProgramComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="ParametersLabel" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" Content="Parameters"/>
|
||||
<TextBox x:Name="ParametersTextBox" Grid.Row="5" Grid.Column="1" Height="22" Width="370" HorizontalAlignment="Left" IsEnabled="False" VerticalContentAlignment="Center" />
|
||||
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right" IsChecked="False" />
|
||||
<TextBox x:Name="ParametersTextBox" Grid.Row="5" Grid.Column="1" Height="22" Width="370" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding Parameters, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding ParametersCheckBoxEnabled}"/>
|
||||
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right"
|
||||
IsChecked="{Binding ParametersCheckBoxEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding EnableParametersCheckBoxEnabled}"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Controls">
|
||||
<UniformGrid Columns="4" Margin="5,5,5,5">
|
||||
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True" Content="Start Dumping"
|
||||
IsEnabled="False" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True"
|
||||
Content="{Binding StartStopButtonText, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding StartStopButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="MediaScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for discs"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
IsEnabled="{Binding MediaScanButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="UpdateVolumeLabel" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Update Label"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
IsEnabled="{Binding UpdateVolumeLabelEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="CopyProtectScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for protection"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
IsEnabled="{Binding CopyProtectScanButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
|
||||
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
|
||||
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Text="Waiting for media..." />
|
||||
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Text="{Binding Status, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<Expander Margin="5,5 5 5" MaxHeight="300" HorizontalAlignment="Stretch" Header="Log Output" x:Name="LogPanel">
|
||||
<Expander Margin="5,5 5 5" MaxHeight="300" HorizontalAlignment="Stretch" Header="Log Output" x:Name="LogPanel"
|
||||
IsExpanded="{Binding LogPanelExpanded}">
|
||||
<controls:LogOutput x:Name="LogOutput"/>
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
467
MPF.UI.Core/Windows/MainWindow.xaml.cs
Normal file
467
MPF.UI.Core/Windows/MainWindow.xaml.cs
Normal file
@@ -0,0 +1,467 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using MPF.Core;
|
||||
using MPF.Core.UI.ViewModels;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using WPFCustomMessageBox;
|
||||
using WinForms = System.Windows.Forms;
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
public partial class MainWindow : WindowBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Read-only access to the current main view model
|
||||
/// </summary>
|
||||
public MainViewModel MainViewModel => DataContext as MainViewModel ?? new MainViewModel();
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MainWindow() => InitializeComponent();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for MainWindow OnContentRendered event
|
||||
/// </summary>
|
||||
protected override void OnContentRendered(EventArgs e)
|
||||
{
|
||||
base.OnContentRendered(e);
|
||||
|
||||
// Disable buttons until we load fully
|
||||
MainViewModel.StartStopButtonEnabled = false;
|
||||
MainViewModel.MediaScanButtonEnabled = false;
|
||||
MainViewModel.UpdateVolumeLabelEnabled = false;
|
||||
MainViewModel.CopyProtectScanButtonEnabled = false;
|
||||
|
||||
// Add the click handlers to the UI
|
||||
AddEventHandlers();
|
||||
|
||||
// Display the debug option in the menu, if necessary
|
||||
if (MainViewModel.Options.ShowDebugViewMenuItem)
|
||||
DebugViewMenuItem.Visibility = Visibility.Visible;
|
||||
|
||||
MainViewModel.Init(LogOutput.EnqueueLog, DisplayUserMessage, ShowDiscInformationWindow);
|
||||
|
||||
// Set the UI color scheme according to the options
|
||||
ApplyTheme();
|
||||
|
||||
// Check for updates, if necessary
|
||||
if (MainViewModel.Options.CheckForUpdatesOnStartup)
|
||||
CheckForUpdates(showIfSame: false);
|
||||
|
||||
// Handle first-run, if necessary
|
||||
if (MainViewModel.Options.FirstRun)
|
||||
{
|
||||
// Show the options window
|
||||
ShowOptionsWindow("Welcome to MPF, Explore the Options");
|
||||
}
|
||||
}
|
||||
|
||||
#region UI Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Add all event handlers
|
||||
/// </summary>
|
||||
public void AddEventHandlers()
|
||||
{
|
||||
// Menu Bar Click
|
||||
AboutMenuItem.Click += AboutClick;
|
||||
AppExitMenuItem.Click += AppExitClick;
|
||||
CheckForUpdatesMenuItem.Click += CheckForUpdatesClick;
|
||||
DebugViewMenuItem.Click += DebugViewClick;
|
||||
OptionsMenuItem.Click += OptionsMenuItemClick;
|
||||
|
||||
// User Area Click
|
||||
CopyProtectScanButton.Click += CopyProtectScanButtonClick;
|
||||
EnableParametersCheckBox.Click += EnableParametersCheckBoxClick;
|
||||
MediaScanButton.Click += MediaScanButtonClick;
|
||||
UpdateVolumeLabel.Click += UpdateVolumeLabelClick;
|
||||
OutputPathBrowseButton.Click += OutputPathBrowseButtonClick;
|
||||
StartStopButton.Click += StartStopButtonClick;
|
||||
|
||||
// User Area SelectionChanged
|
||||
SystemTypeComboBox.SelectionChanged += SystemTypeComboBoxSelectionChanged;
|
||||
MediaTypeComboBox.SelectionChanged += MediaTypeComboBoxSelectionChanged;
|
||||
DriveLetterComboBox.SelectionChanged += DriveLetterComboBoxSelectionChanged;
|
||||
DriveSpeedComboBox.SelectionChanged += DriveSpeedComboBoxSelectionChanged;
|
||||
DumpingProgramComboBox.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
|
||||
|
||||
// User Area TextChanged
|
||||
OutputPathTextBox.TextChanged += OutputPathTextBoxTextChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Browse for an output file path
|
||||
/// </summary>
|
||||
public void BrowseFile()
|
||||
{
|
||||
// Get the current path, if possible
|
||||
string currentPath = MainViewModel.OutputPath;
|
||||
if (string.IsNullOrWhiteSpace(currentPath) && !string.IsNullOrWhiteSpace(MainViewModel.Options.DefaultOutputPath))
|
||||
currentPath = Path.Combine(MainViewModel.Options.DefaultOutputPath, "track.bin");
|
||||
else if (string.IsNullOrWhiteSpace(currentPath))
|
||||
currentPath = "track.bin";
|
||||
if (string.IsNullOrWhiteSpace(currentPath))
|
||||
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "track.bin");
|
||||
|
||||
// Get the full path
|
||||
currentPath = Path.GetFullPath(currentPath);
|
||||
|
||||
// Get the directory
|
||||
var directory = Path.GetDirectoryName(currentPath);
|
||||
|
||||
// Get the filename
|
||||
string filename = Path.GetFileName(currentPath);
|
||||
|
||||
WinForms.FileDialog fileDialog = new WinForms.SaveFileDialog
|
||||
{
|
||||
FileName = filename,
|
||||
InitialDirectory = directory,
|
||||
};
|
||||
WinForms.DialogResult result = fileDialog.ShowDialog();
|
||||
|
||||
if (result == WinForms.DialogResult.OK)
|
||||
{
|
||||
MainViewModel.OutputPath = fileDialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for available updates
|
||||
/// </summary>
|
||||
/// <param name="showIfSame">True to show the box even if it's the same, false to only show if it's different</param>
|
||||
public void CheckForUpdates(bool showIfSame)
|
||||
{
|
||||
(bool different, string message, var url) = MainViewModel.CheckForUpdates();
|
||||
|
||||
// If we have a new version, put it in the clipboard
|
||||
if (different)
|
||||
Clipboard.SetText(url);
|
||||
|
||||
if (showIfSame || different)
|
||||
CustomMessageBox.Show(this, message, "Version Update Check", MessageBoxButton.OK, different ? MessageBoxImage.Exclamation : MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display a user message using a CustomMessageBox
|
||||
/// </summary>
|
||||
/// <param name="title">Title to display to the user</param>
|
||||
/// <param name="message">Message to display to the user</param>
|
||||
/// <param name="optionCount">Number of options to display</param>
|
||||
/// <param name="flag">true for inquiry, false otherwise</param>
|
||||
/// <returns>true for positive, false for negative, null for neutral</returns>
|
||||
public bool? DisplayUserMessage(string title, string message, int optionCount, bool flag)
|
||||
{
|
||||
// Set the correct button style
|
||||
MessageBoxButton button;
|
||||
switch (optionCount)
|
||||
{
|
||||
case 1:
|
||||
button = MessageBoxButton.OK;
|
||||
break;
|
||||
case 2:
|
||||
button = MessageBoxButton.YesNo;
|
||||
break;
|
||||
case 3:
|
||||
button = MessageBoxButton.YesNoCancel;
|
||||
break;
|
||||
|
||||
// This should not happen, but default to "OK"
|
||||
default:
|
||||
button = MessageBoxButton.OK;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the correct icon
|
||||
MessageBoxImage image = flag ? MessageBoxImage.Question : MessageBoxImage.Exclamation;
|
||||
|
||||
// Display and get the result
|
||||
MessageBoxResult result = CustomMessageBox.Show(this, message, title, button, image);
|
||||
switch (result)
|
||||
{
|
||||
case MessageBoxResult.OK:
|
||||
case MessageBoxResult.Yes:
|
||||
return true;
|
||||
|
||||
case MessageBoxResult.No:
|
||||
return false;
|
||||
|
||||
case MessageBoxResult.Cancel:
|
||||
case MessageBoxResult.None:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build a dummy SubmissionInfo and display it for testing
|
||||
/// </summary>
|
||||
public void ShowDebugDiscInfoWindow()
|
||||
{
|
||||
var submissionInfo = MainViewModel.CreateDebugSubmissionInfo();
|
||||
var result = ShowDiscInformationWindow(submissionInfo);
|
||||
InfoTool.ProcessSpecialFields(result.Item2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the disc information window
|
||||
/// </summary>
|
||||
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
|
||||
/// <returns>Dialog open result</returns>
|
||||
#if NET48
|
||||
public (bool?, SubmissionInfo) ShowDiscInformationWindow(SubmissionInfo submissionInfo)
|
||||
#else
|
||||
public (bool?, SubmissionInfo?) ShowDiscInformationWindow(SubmissionInfo? submissionInfo)
|
||||
#endif
|
||||
{
|
||||
if (MainViewModel.Options.ShowDiscEjectReminder)
|
||||
CustomMessageBox.Show(this, "It is now safe to eject the disc", "Eject", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
var discInformationWindow = new DiscInformationWindow(MainViewModel.Options, submissionInfo)
|
||||
{
|
||||
Focusable = true,
|
||||
Owner = this,
|
||||
ShowActivated = true,
|
||||
ShowInTaskbar = true,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
bool? result = discInformationWindow.ShowDialog();
|
||||
|
||||
// Copy back the submission info changes, if necessary
|
||||
if (result == true)
|
||||
#if NET48
|
||||
submissionInfo = discInformationWindow.DiscInformationViewModel.SubmissionInfo.Clone() as SubmissionInfo;
|
||||
#else
|
||||
submissionInfo = (discInformationWindow.DiscInformationViewModel.SubmissionInfo.Clone() as SubmissionInfo)!;
|
||||
#endif
|
||||
|
||||
#if NET48
|
||||
return (result, submissionInfo);
|
||||
#else
|
||||
return (result, submissionInfo!);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the Options window
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public void ShowOptionsWindow(string title = null)
|
||||
#else
|
||||
public void ShowOptionsWindow(string? title = null)
|
||||
#endif
|
||||
{
|
||||
var optionsWindow = new OptionsWindow(MainViewModel.Options)
|
||||
{
|
||||
Focusable = true,
|
||||
Owner = this,
|
||||
ShowActivated = true,
|
||||
ShowInTaskbar = true,
|
||||
Title = title ?? "Options",
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
optionsWindow.Closed += OnOptionsUpdated;
|
||||
optionsWindow.Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the UI color scheme according to the options
|
||||
/// </summary>
|
||||
private void ApplyTheme()
|
||||
{
|
||||
Theme theme;
|
||||
if (MainViewModel.Options.EnableDarkMode)
|
||||
theme = new DarkModeTheme();
|
||||
else
|
||||
theme = new LightModeTheme();
|
||||
|
||||
theme.Apply();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for OptionsWindow OnUpdated event
|
||||
/// </summary>
|
||||
#if NET48
|
||||
public void OnOptionsUpdated(object sender, EventArgs e)
|
||||
#else
|
||||
public void OnOptionsUpdated(object? sender, EventArgs e)
|
||||
#endif
|
||||
{
|
||||
// Get the options window
|
||||
var optionsWindow = (sender as OptionsWindow);
|
||||
if (optionsWindow?.OptionsViewModel == null)
|
||||
return;
|
||||
|
||||
bool savedSettings = optionsWindow.OptionsViewModel.SavedSettings;
|
||||
var options = optionsWindow.OptionsViewModel.Options;
|
||||
MainViewModel.UpdateOptions(savedSettings, options);
|
||||
|
||||
// Set the UI color scheme according to the options
|
||||
ApplyTheme();
|
||||
}
|
||||
|
||||
#region Menu Bar
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AboutMenuItem Click event
|
||||
/// </summary>
|
||||
public void AboutClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string aboutText = MainViewModel.CreateAboutText();
|
||||
CustomMessageBox.Show(this, aboutText, "About", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AppExitMenuItem Click event
|
||||
/// </summary>
|
||||
public void AppExitClick(object sender, RoutedEventArgs e) =>
|
||||
Application.Current.Shutdown();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckForUpdatesMenuItem Click event
|
||||
/// </summary>
|
||||
public void CheckForUpdatesClick(object sender, RoutedEventArgs e)
|
||||
=> CheckForUpdates(showIfSame: true);
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DebugViewMenuItem Click event
|
||||
/// </summary>
|
||||
public void DebugViewClick(object sender, RoutedEventArgs e) =>
|
||||
ShowDebugDiscInfoWindow();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for OptionsMenuItem Click event
|
||||
/// </summary>
|
||||
public void OptionsMenuItemClick(object sender, RoutedEventArgs e) =>
|
||||
ShowOptionsWindow();
|
||||
|
||||
#endregion
|
||||
|
||||
#region User Area
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CopyProtectScanButton Click event
|
||||
/// </summary>
|
||||
public async void CopyProtectScanButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var (output, error) = await MainViewModel.ScanAndShowProtection();
|
||||
|
||||
if (!MainViewModel.LogPanelExpanded)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(output) && string.IsNullOrEmpty(error))
|
||||
CustomMessageBox.Show(this, output, "Detected Protection(s)", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
else
|
||||
CustomMessageBox.Show(this, "An exception occurred, see the log for details", "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DriveLetterComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void DriveLetterComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.InitializeUIValues(removeEventHandlers: true, rescanDrives: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DriveSpeedComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void DriveSpeedComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.EnsureDiscInformation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DumpingProgramComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void DumpingProgramComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.ChangeDumpingProgram();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for EnableParametersCheckBox Click event
|
||||
/// </summary>
|
||||
public void EnableParametersCheckBoxClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.ToggleParameters();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for MediaScanButton Click event
|
||||
/// </summary>
|
||||
public void MediaScanButtonClick(object sender, RoutedEventArgs e) =>
|
||||
MainViewModel.InitializeUIValues(removeEventHandlers: true, rescanDrives: true);
|
||||
|
||||
/// <summary>
|
||||
/// Handler for MediaTypeComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void MediaTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.ChangeMediaType(e.RemovedItems, e.AddedItems);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for OutputPathBrowseButton Click event
|
||||
/// </summary>
|
||||
public void OutputPathBrowseButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BrowseFile();
|
||||
MainViewModel.EnsureDiscInformation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for OutputPathTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void OutputPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.EnsureDiscInformation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for StartStopButton Click event
|
||||
/// </summary>
|
||||
public void StartStopButtonClick(object sender, RoutedEventArgs e) =>
|
||||
MainViewModel.ToggleStartStop();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for SystemTypeComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void SystemTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.ChangeSystem();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for UpdateVolumeLabel Click event
|
||||
/// </summary>
|
||||
public void UpdateVolumeLabelClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
{
|
||||
if (MainViewModel.Options.FastUpdateLabel)
|
||||
MainViewModel.FastUpdateLabel(removeEventHandlers: true);
|
||||
else
|
||||
MainViewModel.InitializeUIValues(removeEventHandlers: true, rescanDrives: false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion // Event Handlers
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,18 @@
|
||||
<coreWindows:WindowBase x:Class="MPF.Windows.OptionsWindow"
|
||||
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.OptionsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:MPF"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows;assembly=MPF.UI.Core"
|
||||
xmlns:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Options" Width="515.132" WindowStyle="None"
|
||||
Width="515.132" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
@@ -29,7 +32,11 @@
|
||||
<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>Options</Bold></TextBlock>
|
||||
<TextBlock>
|
||||
<TextBlock.Inlines>
|
||||
<Run FontWeight="Bold" Text="{Binding Title, Mode=OneWay}"/>
|
||||
</TextBlock.Inlines>
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
<Label.ContextMenu>
|
||||
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
@@ -60,25 +67,84 @@
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
|
||||
<TabControl Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Style="{DynamicResource CustomTabControlStyle}">
|
||||
<TabItem Header="User Interface" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
|
||||
IsChecked="{Binding Options.EnableDarkMode}"
|
||||
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
|
||||
/>
|
||||
<TabItem Header="General" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="User Interface">
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<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"
|
||||
/>
|
||||
</UniformGrid>
|
||||
<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>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Detection">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox x:Name="SkipMediaTypeDetectionCheckBox" VerticalAlignment="Center" Content="Skip Type Detect"
|
||||
IsChecked="{Binding Options.SkipMediaTypeDetection}"
|
||||
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
|
||||
IsChecked="{Binding Options.SkipSystemDetection}"
|
||||
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
|
||||
IsChecked="{Binding Options.IgnoreFixedDrives}"
|
||||
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
|
||||
/>
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
|
||||
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
|
||||
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding Systems}" SelectedItem="{Binding Options.DefaultSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ComboBoxItem}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHeader}" Value="True">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Logging">
|
||||
<UniformGrid Columns="2" Rows="1">
|
||||
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
|
||||
IsChecked="{Binding Options.VerboseLogging}"
|
||||
ToolTip="Display all logging statements" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Auto-Open Log"
|
||||
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
|
||||
ToolTip="Open the log panel when the program launches" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem Header="Paths" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -92,29 +158,35 @@
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Aaru Path" />
|
||||
<TextBox x:Name="AaruPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.AaruPath}" VerticalContentAlignment="Center" />
|
||||
<TextBox x:Name="AaruPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
Text="{Binding Options.AaruPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="AaruPathButton" Grid.Row="0" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DiscImageCreator Path" />
|
||||
<TextBox x:Name="DiscImageCreatorPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DiscImageCreatorPath}" VerticalContentAlignment="Center" />
|
||||
<TextBox x:Name="DiscImageCreatorPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
Text="{Binding Options.DiscImageCreatorPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="DiscImageCreatorPathButton" Grid.Row="1" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DD Path" />
|
||||
<TextBox x:Name="DDPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DDPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="DDPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Redumper Path" />
|
||||
<TextBox x:Name="RedumperPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
Text="{Binding Options.RedumperPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="RedumperPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Dumping Program" />
|
||||
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Dumping Program" />
|
||||
<ComboBox x:Name="InternalProgramComboBox" Grid.Row="3" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding InternalPrograms}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
ItemsSource="{Binding InternalPrograms}" SelectedItem="{Binding Options.InternalProgram, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
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" />
|
||||
<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="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
</Grid>
|
||||
@@ -123,46 +195,13 @@
|
||||
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
|
||||
<UniformGrid Columns="2" Rows="9">
|
||||
<CheckBox VerticalAlignment="Center" Content="Skip Type Detect"
|
||||
IsChecked="{Binding Options.SkipMediaTypeDetection}"
|
||||
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
|
||||
IsChecked="{Binding Options.SkipSystemDetection}"
|
||||
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
|
||||
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left" ItemsSource="{Binding Systems}" SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ComboBoxItem}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHeader}" Value="True">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
|
||||
IsChecked="{Binding Options.IgnoreFixedDrives}"
|
||||
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
|
||||
/>
|
||||
|
||||
<UniformGrid Columns="2" Rows="7">
|
||||
<CheckBox VerticalAlignment="Center" Content="Show Separate Window"
|
||||
IsChecked="{Binding Options.ToolsInSeparateWindow}"
|
||||
ToolTip="Show program output in separate command window instead of in the log. Enable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Protection Scan"
|
||||
IsChecked="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Protection File"
|
||||
<CheckBox VerticalAlignment="Center" Content="Output Protection File"
|
||||
IsChecked="{Binding Options.OutputSeparateProtectionFile}" IsEnabled="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Output protection information to a separate file" Margin="0,4,0,0"
|
||||
/>
|
||||
@@ -177,6 +216,11 @@
|
||||
ToolTip="Enable showing the disc information output after dumping" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Pull All Information"
|
||||
IsChecked="{Binding Options.PullAllInformation}"
|
||||
ToolTip="Enable pulling all comment and content data 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"
|
||||
@@ -197,6 +241,11 @@
|
||||
ToolTip="Enable adding placeholder text in the output for required and optional fields" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Add Filename Suffix"
|
||||
IsChecked="{Binding Options.AddFilenameSuffix}"
|
||||
ToolTip="Enable appending the output filename to the generated files" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Output Submission JSON"
|
||||
IsChecked="{Binding Options.OutputSubmissionJSON}"
|
||||
ToolTip="Enable outputting a compressed JSON version of the submission info" Margin="0,4"
|
||||
@@ -211,9 +260,38 @@
|
||||
IsChecked="{Binding Options.CompressLogFiles}"
|
||||
ToolTip="Compress output log files to reduce space" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Delete Unnecessary Files"
|
||||
IsChecked="{Binding Options.DeleteUnnecessaryFiles}"
|
||||
ToolTip="Delete unnecesary output files to reduce space" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Protection">
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Protection Scan"
|
||||
IsChecked="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Scan Archive Contents"
|
||||
IsChecked="{Binding Options.ScanArchivesForProtection}"
|
||||
ToolTip="Enable scanning archive contents during protection scanning (may drastically increase scanning time but is more accurate)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Executable Packers"
|
||||
IsChecked="{Binding Options.ScanPackersForProtection}"
|
||||
ToolTip="Include executable packers in outputted protections" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
|
||||
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
|
||||
ToolTip="Include debug information during protection scans" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Default Speeds" Grid.ColumnSpan="2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -226,160 +304,157 @@
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="CD-ROM" />
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="CD" />
|
||||
<Slider x:Name="DumpSpeedCDSlider" Grid.Row="0" Grid.Column="1" Minimum="1" Maximum="72" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static local:Constants.SpeedsForCDAsCollection}}"
|
||||
Ticks="{Binding Source={x:Static core:Constants.SpeedsForCDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedCD}" />
|
||||
<TextBox x:Name="DumpSpeedCDTextBox" Grid.Row="0" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedCDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="DVD-ROM" />
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="DVD" />
|
||||
<Slider x:Name="DumpSpeedDVDSlider" Grid.Row="1" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static local:Constants.SpeedsForDVDAsCollection}}"
|
||||
Ticks="{Binding Source={x:Static core:Constants.SpeedsForDVDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedDVD}" />
|
||||
<TextBox x:Name="DumpSpeedDVDTextBox" Grid.Row="1" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="BD-ROM" />
|
||||
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static local:Constants.SpeedsForBDAsCollection}}"
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="HD-DVD" />
|
||||
<Slider x:Name="DumpSpeedHDDVDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static core:Constants.SpeedsForHDDVDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedHDDVD}" />
|
||||
<TextBox x:Name="DumpSpeedHDDVDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedHDDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="BD" />
|
||||
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="3" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static core:Constants.SpeedsForBDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedBD}" />
|
||||
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="3" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedBDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Protection Scanning" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<TabItem Header="Programs" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Scan Archive Contents"
|
||||
IsChecked="{Binding Options.ScanArchivesForProtection}"
|
||||
ToolTip="Enable scanning archive contents during protection scanning (may drastically increase scanning time but is more accurate)" Margin="0,4"
|
||||
/>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Aaru">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.AaruEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Executable Packers"
|
||||
IsChecked="{Binding Options.ScanPackersForProtection}"
|
||||
ToolTip="Include executable packers in outputted protections" Margin="0,4"
|
||||
/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.AaruEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
|
||||
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
|
||||
ToolTip="Include debug information during protection scans" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
<CheckBox VerticalAlignment="Center" Content="Force Dumping"
|
||||
IsChecked="{Binding Options.AaruForceDumping}"
|
||||
ToolTip="Enable forcing dump even if there are issues" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Strip Personal Data"
|
||||
IsChecked="{Binding Options.AaruStripPersonalData}"
|
||||
ToolTip="Enable stripping of personally identifiable information from metadata" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.AaruRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="DiscImageCreator">
|
||||
<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"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Paranoid Mode"
|
||||
IsChecked="{Binding Options.DICParanoidMode}"
|
||||
ToolTip="Enable pedantic and super-safe flags" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Use CMI Flag"
|
||||
IsChecked="{Binding Options.DICUseCMIFlag}"
|
||||
ToolTip="Enable the CMI flag for supported disc types (DVD/HD-DVD only)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Reset After Dump"
|
||||
IsChecked="{Binding Options.DICResetDriveAfterDump}"
|
||||
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
|
||||
/>
|
||||
|
||||
<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 [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>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redumper">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.RedumperEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.RedumperEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable BE Drive Reading"
|
||||
IsChecked="{Binding Options.RedumperUseBEReading}"
|
||||
ToolTip="Enable setting drive read method to BE_CDDA by default" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Set Generic Drive Type"
|
||||
IsChecked="{Binding Options.RedumperUseGenericDriveType}"
|
||||
ToolTip="Enable setting drive type to Generic by default" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.RedumperRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Aaru" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.AaruEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.AaruEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Force Dumping"
|
||||
IsChecked="{Binding Options.AaruForceDumping}"
|
||||
ToolTip="Enable forcing dump even if there are issues" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Strip Personal Data"
|
||||
IsChecked="{Binding Options.AaruStripPersonalData}"
|
||||
ToolTip="Enable stripping of personally identifiable information from metadata" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.AaruRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="DiscImageCreator" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<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"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Paranoid Mode"
|
||||
IsChecked="{Binding Options.DICParanoidMode}"
|
||||
ToolTip="Enable pedantic and super-safe flags" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Use CMI Flag"
|
||||
IsChecked="{Binding Options.DICUseCMIFlag}"
|
||||
ToolTip="Enable the CMI flag for supported disc types (DVD/HD-DVD only)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Reset After Dump"
|
||||
IsChecked="{Binding Options.DICResetDriveAfterDump}"
|
||||
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
|
||||
/>
|
||||
|
||||
<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 [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="Logging" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
|
||||
IsChecked="{Binding Options.VerboseLogging}"
|
||||
ToolTip="Display all logging statements" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Auto-Open Log"
|
||||
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
|
||||
ToolTip="Open the log panel when the program launches" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Log Formatting"
|
||||
IsChecked="{Binding Options.EnableLogFormatting}"
|
||||
ToolTip="Format log lines written to the log, including overwriting previous lines on match. Disable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Progress Processing"
|
||||
IsChecked="{Binding Options.EnableProgressProcessing}" IsEnabled="{Binding Options.EnableLogFormatting}"
|
||||
ToolTip="Process lines written to the log to update the progress bar. Disable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Login Info" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump Credentials">
|
||||
<UniformGrid Columns="5" Rows="1">
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump">
|
||||
<UniformGrid Columns="5">
|
||||
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Username" />
|
||||
<TextBox x:Name="RedumpUsernameTextBox" Height="22" HorizontalAlignment="Stretch"
|
||||
Text="{Binding Options.RedumpUsername}" />
|
||||
@@ -391,6 +466,12 @@
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<Label>
|
||||
<Label.Content>
|
||||
<TextBlock TextWrapping="Wrap"><Bold Foreground="Red">WARNING:</Bold> If you choose to enable validation and information retrieval, you are responsible for ensuring that all data populated matches your actual media. Some information may be marked to check for validity as a reminder, but all information should be subject to the same scrutiny.</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
@@ -398,12 +479,14 @@
|
||||
<!-- Accept / Cancel -->
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
<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}" />
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user