mirror of
https://github.com/SabreTools/MPF.git
synced 2026-02-04 05:35:52 +00:00
Compare commits
484 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
c4ef14ea3c | ||
|
|
03668bd6af | ||
|
|
2d90a63ca7 | ||
|
|
11e6e37331 | ||
|
|
b229a2d59e | ||
|
|
c61af9316f | ||
|
|
02e3040e1b | ||
|
|
60bbe29435 | ||
|
|
3274ea08aa | ||
|
|
97a61dea32 | ||
|
|
6cccf20b03 | ||
|
|
0a7e17ed00 | ||
|
|
706f75c0eb | ||
|
|
022e87c4bb | ||
|
|
8b29ac7e47 | ||
|
|
4d5b8baf6f | ||
|
|
e199e5a08c | ||
|
|
6cc2351bf7 | ||
|
|
c391dbd3c8 | ||
|
|
709d980b67 | ||
|
|
0903855d5c | ||
|
|
6ad2505bf8 | ||
|
|
5db8756639 | ||
|
|
5d176408a2 | ||
|
|
ab0b569798 | ||
|
|
ee76d49e56 | ||
|
|
c75d2dcae2 | ||
|
|
7c411d36db | ||
|
|
ca767cf576 | ||
|
|
b57e0bb97e | ||
|
|
5f059253a4 | ||
|
|
e0f8443653 | ||
|
|
4c23a4bbf3 | ||
|
|
0c1486bbce | ||
|
|
6f41c9a331 | ||
|
|
2879dd29d6 | ||
|
|
42e1ef45b4 | ||
|
|
19493fdf0c | ||
|
|
5870ad0673 | ||
|
|
cbd2850d1b | ||
|
|
e7c36c104a | ||
|
|
960840d9ba | ||
|
|
b8ac1bc9d4 | ||
|
|
89edf9a8f6 | ||
|
|
eb8db0b311 | ||
|
|
b10cf8b78a | ||
|
|
1dd5c0d6d0 | ||
|
|
a1e00d23a4 | ||
|
|
1a9d38dd0e | ||
|
|
2f3e7d105d | ||
|
|
a72b3c32b1 | ||
|
|
a479b16ae2 | ||
|
|
a9e7b6f5b3 | ||
|
|
60605d7d00 | ||
|
|
9dc7f726e9 | ||
|
|
46134032d6 | ||
|
|
9847c8f351 | ||
|
|
7b506586cd | ||
|
|
40bbd422b7 | ||
|
|
285fd69ab4 | ||
|
|
b436e19bbb | ||
|
|
bf6e079289 | ||
|
|
b623866b6e | ||
|
|
df7046723c | ||
|
|
3cd3047790 | ||
|
|
abd60612c5 | ||
|
|
02292acee1 | ||
|
|
c923899898 | ||
|
|
fcdd2ad036 | ||
|
|
02d3af0ac1 | ||
|
|
0516bccece | ||
|
|
2d6389d54d | ||
|
|
60d5a117b5 | ||
|
|
9b7e2e35f5 | ||
|
|
ddfb383f23 | ||
|
|
a1707486f4 | ||
|
|
451101ec67 | ||
|
|
83c583b7e6 | ||
|
|
33cb3d3c97 | ||
|
|
3d98f345c1 | ||
|
|
de8ce5c110 | ||
|
|
fd9ed77316 | ||
|
|
d974b73cce | ||
|
|
da8a67fcad | ||
|
|
471bc60ed7 | ||
|
|
fef46be34c | ||
|
|
9a92dbdedb | ||
|
|
d6be0a4154 | ||
|
|
d3ae372903 | ||
|
|
7621ef1a13 | ||
|
|
148fdd0590 | ||
|
|
232256310a | ||
|
|
5e938791ef | ||
|
|
08405dd9b4 | ||
|
|
512c637ea3 | ||
|
|
909ebd72a1 | ||
|
|
123136e90e | ||
|
|
299d25af27 | ||
|
|
9eee2f6444 | ||
|
|
2afb10b73b | ||
|
|
1b016e5915 | ||
|
|
1c403e1748 | ||
|
|
a372a133ca | ||
|
|
08a7df504b | ||
|
|
39f9d9a86d | ||
|
|
bdd5af65ce | ||
|
|
92d7d2ab91 | ||
|
|
8d4d7ce449 | ||
|
|
d2d650cace | ||
|
|
772cefd700 | ||
|
|
e20350160b | ||
|
|
08f4e91b27 | ||
|
|
021237bc38 | ||
|
|
78c36db2f9 | ||
|
|
7fd2562cb5 | ||
|
|
15a3be2e66 | ||
|
|
9b6a540ec6 | ||
|
|
2b51085bc2 | ||
|
|
822134070b | ||
|
|
26cd874779 | ||
|
|
9f21b68541 | ||
|
|
e49d95663b | ||
|
|
2c70392ada | ||
|
|
1b2248b1e7 | ||
|
|
a322dc6353 | ||
|
|
7c78ae47c6 | ||
|
|
a14dec1b7e | ||
|
|
53b5862697 | ||
|
|
83d379c7b5 | ||
|
|
be33db8339 | ||
|
|
97e9924a0b | ||
|
|
7daab55639 | ||
|
|
9d70b7469a | ||
|
|
b5504902c4 | ||
|
|
f5e82ccd75 | ||
|
|
f4c4c21a10 | ||
|
|
661d2440f2 | ||
|
|
f4af8097f6 | ||
|
|
e1822905e7 | ||
|
|
e5154dad5b | ||
|
|
30f8932039 | ||
|
|
c4f0792c77 | ||
|
|
79c7f13ff9 | ||
|
|
dbeeb0c69b | ||
|
|
58c53ff5e2 | ||
|
|
87c441887a | ||
|
|
8ee4dab239 | ||
|
|
e01ebf8d8e | ||
|
|
e92bcd378c | ||
|
|
80156e73d1 | ||
|
|
97803cd860 | ||
|
|
9a5feee095 | ||
|
|
84410056bd | ||
|
|
acd0e41703 | ||
|
|
c7dfb9dca7 | ||
|
|
0efd82bd59 | ||
|
|
426ceff451 | ||
|
|
f2be7ed34c | ||
|
|
5dcc783b95 | ||
|
|
d1c641e934 | ||
|
|
59be63785d | ||
|
|
0bf85ec729 | ||
|
|
b54c2dc254 | ||
|
|
021fcd0641 | ||
|
|
f2dadae7a3 | ||
|
|
7ce7df2625 | ||
|
|
09fcd384ab | ||
|
|
5f8625a384 | ||
|
|
621011af7a | ||
|
|
a0b81941d1 | ||
|
|
e735335773 | ||
|
|
7b62572a56 | ||
|
|
0f921c926b | ||
|
|
4d8a4d23c0 | ||
|
|
9d7eaa46fd | ||
|
|
cc9664f7d6 | ||
|
|
573b3e9d1c | ||
|
|
9808694d89 | ||
|
|
d70d8f5b6e | ||
|
|
b75c2d80bf | ||
|
|
aa747ff651 | ||
|
|
bcbf5daf0d | ||
|
|
aee1c05a45 | ||
|
|
0bb96a8dd3 | ||
|
|
1e1d2c7b63 | ||
|
|
f12375cddc | ||
|
|
ed26e6611a | ||
|
|
98f77eca07 | ||
|
|
1b2b560f8f | ||
|
|
b49cc0c9bd | ||
|
|
4ba58ea861 | ||
|
|
3f52a20c90 | ||
|
|
580089d06e | ||
|
|
1397ab0fa6 |
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -10,7 +10,7 @@ assignees: mnadareski
|
||||
**Before You Submit**
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
|
||||
- .NET Core 3.1 and .NET 5.0 have known limitations, so make sure that what you're asking for isn't already in another build.
|
||||
- .NET 6.0 has known limitations, so make sure that what you're asking for isn't already in another build.
|
||||
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
|
||||
|
||||
If none of those apply, then continue...
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/informational.md
vendored
2
.github/ISSUE_TEMPLATE/informational.md
vendored
@@ -10,7 +10,7 @@ assignees: mnadareski
|
||||
**Before You Submit**
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
|
||||
- .NET Core 3.1 and .NET 5.0 have known limitations, so make sure that what you're giving information on isn't already in another build.
|
||||
- .NET 6.0 has known limitations, so make sure that what you're giving information on isn't already in another build.
|
||||
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
|
||||
|
||||
If none of those apply, then continue...
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/issue-report.md
vendored
5
.github/ISSUE_TEMPLATE/issue-report.md
vendored
@@ -10,7 +10,7 @@ assignees: mnadareski
|
||||
**Before You Submit**
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the issue has already been addressed.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
|
||||
- .NET Core 3.1 and .NET 5.0 have known issues, please try using another build to reproduce the error
|
||||
- .NET 6.0 has known issues, please try using another build to reproduce the error
|
||||
- Check multiple discs to help narrow down the issue
|
||||
- Check the Options to see if changing any of those affects your issue.
|
||||
|
||||
@@ -25,9 +25,8 @@ What version are you using?
|
||||
**Build**
|
||||
What runtime version are you using?
|
||||
|
||||
- [ ] .NET Framework 4.7.2 running on (Operating System)
|
||||
- [ ] .NET Framework 4.8 running on (Operating System)
|
||||
- [ ] .NET Core 3.1 running on (Operating System)
|
||||
- [ ] .NET 6.0 running on (Operating System)
|
||||
|
||||
**Describe the issue**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "MPF.Library/Aaru/CICMMetadata"]
|
||||
path = CICMMetadata
|
||||
url = https://github.com/claunia/CICMMetadata
|
||||
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
|
||||
}
|
||||
15
.vscode/tasks.json
vendored
15
.vscode/tasks.json
vendored
@@ -3,25 +3,22 @@
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"command": "msbuild",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/MPF/MPF.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
"${workspaceFolder}/MPF.sln",
|
||||
"-property:RuntimeIdentifiers=win7-x64"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"command": "msbuild",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/MPF/MPF.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
"-target:Publish",
|
||||
"-property:RuntimeIdentifiers=win7-x64"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
474
CHANGELIST.md
474
CHANGELIST.md
@@ -1,4 +1,468 @@
|
||||
### 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
|
||||
- Make FillFromRedump private again
|
||||
- Fix DVD layer finding corner case
|
||||
- Fix failing module tests
|
||||
- Add option to limit region and language selections
|
||||
- Cap X360 directory check to 500MB
|
||||
- Assign normalized path to parameters
|
||||
- Move path normalization to better place
|
||||
- Specifically include Unsafe Nuget package
|
||||
- Update Nuget packages to newest stable
|
||||
- Add Xbox One system detection
|
||||
- Update to DIC 20220301
|
||||
- Force internal drive refresh
|
||||
- Add single drive refresh button (IcySon55)
|
||||
- Fix "missing" option in window
|
||||
- Track last used drive on refresh
|
||||
- Clean up pre-dump validation
|
||||
- Update to Aaru v5.3.1 LTS
|
||||
- Add upper case `<TAB>` processing
|
||||
- Reorder event handers
|
||||
- Handle sanitized protections edge case
|
||||
- Update Aaru Nuget package
|
||||
- Return faster on empty protection sets
|
||||
- Remove redundant check around volume label
|
||||
- Fix tabs in Games and Videos boxes
|
||||
- Make fully and partially matching IDs more apparent
|
||||
- Add write offset as read-only field
|
||||
- Explicitly clear list, just in case
|
||||
- Add both fully and partially matching to info file
|
||||
- Fix submission info clone
|
||||
- Clear out fully matched IDs from the partial list
|
||||
- Refine copy protection section showing
|
||||
- Update Nuget packages
|
||||
- Normalize newlines in comments and contents
|
||||
- Increase JSON accuracy for disc types
|
||||
- Further separate out protection scan outputs
|
||||
- Compress JSON for artifacts alone
|
||||
- Even stricter output for copy protection section
|
||||
- Report dictionary to InfoTool
|
||||
- Even even stricter copy protection output
|
||||
- Disable PVD creation for Aaru
|
||||
- Explicitly sanitize '?' from path
|
||||
- Combine cases in protection scan
|
||||
- Convert status label to TextBlock
|
||||
- Add option for copy protection file output
|
||||
- Make protection field user-editable
|
||||
- Add warning to tooltip
|
||||
- Create core UI library
|
||||
- Rename MPF.GUI to MPF.UI
|
||||
- Add multisession pseudo-tag
|
||||
- Add multisession helper method skeleton
|
||||
- Move and update options loader; clean up Check
|
||||
- Move helper methods around
|
||||
- Consolidate Redump login testing
|
||||
- Separate common arguments to new helper
|
||||
- Parse and format CD multisession data
|
||||
- Convert triple space to tab
|
||||
- Fix clone issue with copy protection
|
||||
- Be more picky about multisession
|
||||
- Sanitize whitespace around tabs
|
||||
- Convert internal libraries to .NET Standard 2.0
|
||||
- Update AppVeyor to VS2022
|
||||
- Update Nuget packages
|
||||
- Remove needless csproj constants
|
||||
- Update copyright date to 2022
|
||||
- Revert AppVeyor to VS2019 for now
|
||||
- Add size-based media type detection for non-Framework
|
||||
- Gate ManagmentObject use further
|
||||
- Use built-in NETFRAMEWORK directive
|
||||
- Update to BurnOutSharp 2.1.0
|
||||
- .NET 6.0 and Cleanup
|
||||
- Remove .NET Core 3.1 from test project for now
|
||||
- Remove .NET Core 3.1 entirely
|
||||
- Add filesystem logging for .NET 6
|
||||
- Avoid whitespace changes for PVD, Header, and Cuesheet
|
||||
- Sync to newest CICM
|
||||
- Update solution file for VS2022
|
||||
- Add Aaru as a submodule for .NET 6
|
||||
- Multisession is multi-line
|
||||
- Use Aaru for media type retrival (.NET 6)
|
||||
- Fix CD-R multisession info
|
||||
- Better get drive list (.NET 6)
|
||||
- Add optical media support method (.NET 6)
|
||||
- Simplify IsOptical (.NET 6)
|
||||
- Reorganize GetMediaType
|
||||
- Possibly fix tab regex replacement
|
||||
- Add PIC.bin to log zip
|
||||
- Organize projects in solution
|
||||
- Implement Drive.Create for safety
|
||||
- Fix incomplete system name detection
|
||||
- Add Sharp X68k detection
|
||||
- Add Bandai Playdia QIS detection
|
||||
- Framework for XBONE filenames
|
||||
- Add files for XBONE
|
||||
- Update to DIC 20220707
|
||||
- Fix .bin file paths; update internal filename generation
|
||||
- Disable nonstandard BD-ROM sizes
|
||||
- Trim leading file paths for XBONE
|
||||
- Give .NET 6 priority for web calls
|
||||
- Update to BurnOutSharp 2.3.0 (nw)
|
||||
- Add Mattel Fisher-Price iXL detection
|
||||
- Update to BurnOutSharp 2.3.1 (nw)
|
||||
- Fix serial parsing for Dreamcast
|
||||
- Fix missing parenthesis
|
||||
- Add internal name for Cleanrip outputs
|
||||
- Fix psxt001z namespace
|
||||
- Fix missing assignment
|
||||
- Update to new constructor
|
||||
- Update PSX content check
|
||||
- Update Aaru submodule to latest devel
|
||||
- Add Sony Electronic Book system
|
||||
- Verify GD-ROM outputs, finally
|
||||
- Electronic not Electric
|
||||
- Fix ringcode guide images
|
||||
- Add skeleton for Redumper
|
||||
- Download Redumper with AppVeyor
|
||||
- Add Redumper constants
|
||||
- Add Redumper parameter values
|
||||
- Add Redumper command support and reset
|
||||
- Add Redumper dumping command
|
||||
- Add Redumper command validation
|
||||
- Add Redumper command generation
|
||||
- Create Redumper extensions class
|
||||
- Minor Redumper cleanup
|
||||
- Add important Redumper note
|
||||
- Update to DIC 20220909
|
||||
- Possibly fix PIC parsing
|
||||
- Add PS3 folder/file checks for detection
|
||||
- Use default directory for folder browsing, if possible
|
||||
- Add unused command filename parser
|
||||
- Add initial framework for reporting dumping program
|
||||
- Report specific DIC version, if possible
|
||||
- Add dumping info section skeleton
|
||||
- Update Aaru submodules
|
||||
- Fix missing info reference change
|
||||
- Enable separated protection info output by default
|
||||
- Try adding MPF logs to zip
|
||||
- Create MPF log helper and filter for deletion
|
||||
- Fix missing info in Aaru
|
||||
- Add hardware info to DIC and Aaru
|
||||
- Fix hardware info
|
||||
- Add specialized CDS/SafeDisc filter
|
||||
- Add unused article formatter
|
||||
- Add language filtering to formatter
|
||||
- Add multi-language helper for filter
|
||||
- Update Nuget packages
|
||||
- Remove deprecated protection setting
|
||||
- Update BurnOutSharp to 2.3.4
|
||||
- Add compression result reason to log
|
||||
- Add System.Memory package to MPF.Library
|
||||
- Add CodePages package to MPF.Library
|
||||
- Remove extraneous packages
|
||||
- Change location of dumping info
|
||||
- Add MS ZipFile package to MPF.Library
|
||||
- Add + to positive offsets
|
||||
- Disable layerbreak generation for BD
|
||||
- Disable XGD version reporting
|
||||
- Disable XGD layerbreak reporting
|
||||
- Disable XGD1 PVD reporting
|
||||
- Put Redump limitations behind existing flag
|
||||
- Add framework for reported disc type
|
||||
- Add disc type parsing for Aaru and DIC
|
||||
- Fix multiple DiscType for DIC
|
||||
- Fix multiple DiscType* for DIC
|
||||
- Add logging to !submissionInfo writing failure
|
||||
- Add logging to !submissionInfo formatting failure
|
||||
- Update issue templates to be more accurate
|
||||
- Fix NRE with offsets
|
||||
- Fix readonly Filename info display
|
||||
- Fix layerbreak-based checks
|
||||
- Add PS4 serial finding (tjanas)
|
||||
- Add unused notification method
|
||||
- Move to unused Chime class
|
||||
- Add PS5 serial finding (tjanas)
|
||||
- Fix offset formatting (fuzz6001)
|
||||
|
||||
### 2.3 (2022-02-05)
|
||||
|
||||
- Start overhauling Redump information pulling, again
|
||||
- Add internal structure for special site codes
|
||||
- Add new tabs for special site information
|
||||
@@ -83,6 +547,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
|
||||
@@ -134,6 +599,7 @@
|
||||
- Add safety around volume labels
|
||||
|
||||
### 2.1 (2021-07-22)
|
||||
|
||||
- Enum, no more
|
||||
- Sony works backward
|
||||
- Add experimental dark mode
|
||||
@@ -163,6 +629,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
|
||||
@@ -217,6 +684,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
|
||||
@@ -241,6 +709,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
|
||||
@@ -248,6 +717,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
|
||||
@@ -407,7 +877,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
|
||||
@@ -420,4 +890,4 @@
|
||||
|
||||
### 1.01d (2018-05-18)
|
||||
|
||||
-Combine IBM PC-CD options, misc fixes.
|
||||
-Combine IBM PC-CD options, misc fixes.
|
||||
|
||||
Submodule CICMMetadata deleted from 7944bca8e6
@@ -1,18 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Title>MPF Check</Title>
|
||||
<AssemblyName>MPF.Check</AssemblyName>
|
||||
<Description>Validator for various dumping programs</Description>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.3</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<Version>2.6.5</Version>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
@@ -28,10 +26,16 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.0" GeneratePathProperty="true">
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -41,12 +45,4 @@
|
||||
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj">
|
||||
<Project>{51ab0928-13f9-44bf-a407-b6957a43a056}</Project>
|
||||
<Name>MPF.Library</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,13 +1,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BurnOutSharp;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.Library;
|
||||
using RedumpLib.Data;
|
||||
using RedumpLib.Web;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace MPF.Check
|
||||
{
|
||||
@@ -15,125 +14,21 @@ namespace MPF.Check
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Help options
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
|
||||
{
|
||||
DisplayHelp();
|
||||
// Try processing the standalone arguments
|
||||
if (ProcessStandaloneArguments(args))
|
||||
return;
|
||||
}
|
||||
|
||||
// List options
|
||||
if (args[0] == "-lm" || args[0] == "--listmedia")
|
||||
{
|
||||
ListMediaTypes();
|
||||
Console.ReadLine();
|
||||
// Try processing the common arguments
|
||||
(bool success, MediaType mediaType, RedumpSystem? knownSystem) = ProcessCommonArguments(args);
|
||||
if (!success)
|
||||
return;
|
||||
}
|
||||
else if (args[0] == "-lp" || args[0] == "--listprograms")
|
||||
{
|
||||
ListPrograms();
|
||||
Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
else if (args[0] == "-ls" || args[0] == "--listsystems")
|
||||
{
|
||||
ListSystems();
|
||||
Console.ReadLine();
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal operation check
|
||||
if (args.Length < 3)
|
||||
{
|
||||
DisplayHelp("Invalid number of arguments");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the MediaType
|
||||
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
|
||||
if (mediaType == MediaType.NONE)
|
||||
{
|
||||
DisplayHelp($"{args[0]} is not a recognized media type");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the RedumpSystem
|
||||
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
|
||||
if (knownSystem == null)
|
||||
{
|
||||
DisplayHelp($"{args[1]} is not a recognized system");
|
||||
return;
|
||||
}
|
||||
|
||||
// Default values
|
||||
string username = null, password = null;
|
||||
string internalProgram = "DiscImageCreator";
|
||||
string path = string.Empty;
|
||||
bool scan = false, compress = false, json = false;
|
||||
|
||||
// Loop through and process options
|
||||
int startIndex = 2;
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
(Core.Data.Options options, string path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
|
||||
if (options.InternalProgram == InternalProgram.NONE)
|
||||
{
|
||||
// Redump login
|
||||
if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
{
|
||||
string[] credentials = args[startIndex].Split('=')[1].Split(';');
|
||||
username = credentials[0];
|
||||
password = credentials[1];
|
||||
}
|
||||
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
|
||||
{
|
||||
username = args[startIndex + 1];
|
||||
password = args[startIndex + 2];
|
||||
startIndex += 2;
|
||||
}
|
||||
|
||||
// Use specific program
|
||||
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
internalProgram = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
internalProgram = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Use a device path for physical checks
|
||||
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
|
||||
{
|
||||
path = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
|
||||
{
|
||||
path = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Scan for protection (requires device path)
|
||||
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
|
||||
{
|
||||
scan = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
|
||||
{
|
||||
json = true;
|
||||
}
|
||||
|
||||
// Compress log and extraneous files
|
||||
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
|
||||
{
|
||||
compress = true;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
DisplayHelp("A program name needs to be provided");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make new Progress objects
|
||||
@@ -142,20 +37,14 @@ namespace MPF.Check
|
||||
var protectionProgress = new Progress<ProtectionProgress>();
|
||||
protectionProgress.ProgressChanged += ProgressUpdated;
|
||||
|
||||
// If credentials are invalid, alert the user
|
||||
if (!string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
using (RedumpWebClient wc = new RedumpWebClient())
|
||||
{
|
||||
bool? loggedIn = wc.Login(username, password);
|
||||
if (loggedIn == true)
|
||||
Console.WriteLine("Redump username and password accepted!");
|
||||
else if (loggedIn == false)
|
||||
Console.WriteLine("Redump username and password denied!");
|
||||
else
|
||||
Console.WriteLine("An error occurred validating your crendentials!");
|
||||
}
|
||||
}
|
||||
// Validate the supplied credentials
|
||||
#if NET48
|
||||
(bool? _, string message) = RedumpWebClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword);
|
||||
#else
|
||||
(bool? _, string message) = RedumpHttpClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
Console.WriteLine(message);
|
||||
|
||||
// Loop through all the rest of the args
|
||||
for (int i = startIndex; i < args.Length; i++)
|
||||
@@ -171,24 +60,11 @@ namespace MPF.Check
|
||||
string filepath = Path.GetFullPath(args[i].Trim('"'));
|
||||
|
||||
// Now populate an environment
|
||||
var options = new Options
|
||||
{
|
||||
InternalProgram = EnumConverter.ToInternalProgram(internalProgram),
|
||||
ScanForProtection = scan && !string.IsNullOrWhiteSpace(path),
|
||||
PromptForDiscInformation = false,
|
||||
ShowDiscEjectReminder = false,
|
||||
OutputSubmissionJSON = json,
|
||||
CompressLogFiles = compress,
|
||||
|
||||
RedumpUsername = username,
|
||||
RedumpPassword = password,
|
||||
};
|
||||
|
||||
Drive drive = null;
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
drive = new Drive(null, new DriveInfo(path));
|
||||
drive = Drive.Create(null, path);
|
||||
|
||||
var env = new DumpEnvironment(options, "", filepath, drive, knownSystem, mediaType, null);
|
||||
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, internalProgram: null, parameters: null);
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
@@ -214,61 +90,94 @@ namespace MPF.Check
|
||||
Console.WriteLine("-ls, --listsystems List supported system types");
|
||||
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Check Options:");
|
||||
Console.WriteLine("-c, --credentials <user> <pw> Redump username and password");
|
||||
Console.WriteLine("-u, --use <program> Dumping program output type");
|
||||
Console.WriteLine("-p, --path <drivepath> Physical drive path for additional checks");
|
||||
Console.WriteLine("-s, --scan Enable copy protection scan (requires --path)");
|
||||
Console.WriteLine("-j, --json Enable submission JSON output");
|
||||
Console.WriteLine("-z, --zip Enable log file compression");
|
||||
var supportedArguments = OptionsLoader.PrintSupportedArguments();
|
||||
foreach (string argument in supportedArguments)
|
||||
{
|
||||
Console.WriteLine(argument);
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all media types with their short usable names
|
||||
/// Process common arguments for all functionality
|
||||
/// </summary>
|
||||
private static void ListMediaTypes()
|
||||
/// <returns>True if all arguments pass, false otherwise</returns>
|
||||
private static (bool, MediaType, RedumpSystem?) ProcessCommonArguments(string[] args)
|
||||
{
|
||||
Console.WriteLine("Supported Media Types:");
|
||||
foreach (var val in Enum.GetValues(typeof(MediaType)))
|
||||
// All other use requires at least 3 arguments
|
||||
if (args.Length < 3)
|
||||
{
|
||||
if (((MediaType)val) == MediaType.NONE)
|
||||
continue;
|
||||
|
||||
Console.WriteLine($"{((MediaType?)val).ShortName()} - {((MediaType?)val).LongName()}");
|
||||
DisplayHelp("Invalid number of arguments");
|
||||
return (false, MediaType.NONE, null);
|
||||
}
|
||||
|
||||
// Check the MediaType
|
||||
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
|
||||
if (mediaType == MediaType.NONE)
|
||||
{
|
||||
DisplayHelp($"{args[0]} is not a recognized media type");
|
||||
return (false, MediaType.NONE, null);
|
||||
}
|
||||
|
||||
// Check the RedumpSystem
|
||||
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
|
||||
if (knownSystem == null)
|
||||
{
|
||||
DisplayHelp($"{args[1]} is not a recognized system");
|
||||
return (false, MediaType.NONE, null);
|
||||
}
|
||||
|
||||
return (true, mediaType, knownSystem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all programs with their short usable names
|
||||
/// Process any standalone arguments for the program
|
||||
/// </summary>
|
||||
private static void ListPrograms()
|
||||
/// <returns>True if one of the arguments was processed, false otherwise</returns>
|
||||
private static bool ProcessStandaloneArguments(string[] args)
|
||||
{
|
||||
Console.WriteLine("Supported Programs:");
|
||||
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
|
||||
// Help options
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
|
||||
{
|
||||
if (((InternalProgram)val) == InternalProgram.NONE)
|
||||
continue;
|
||||
|
||||
Console.WriteLine($"{((InternalProgram?)val).LongName()}");
|
||||
DisplayHelp();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all systems with their short usable names
|
||||
/// </summary>
|
||||
private static void ListSystems()
|
||||
{
|
||||
Console.WriteLine("Supported Known Systems:");
|
||||
var knownSystems = Enum.GetValues(typeof(RedumpSystem))
|
||||
.OfType<RedumpSystem?>()
|
||||
.Where(s => s != null && !s.IsMarker() && s.GetCategory() != SystemCategory.NONE)
|
||||
.OrderBy(s => s.LongName() ?? string.Empty);
|
||||
|
||||
foreach (var val in knownSystems)
|
||||
// List options
|
||||
if (args[0] == "-lm" || args[0] == "--listmedia")
|
||||
{
|
||||
Console.WriteLine($"{val.ShortName()} - {val.LongName()}");
|
||||
Console.WriteLine("Supported Media Types:");
|
||||
foreach (string mediaType in Extensions.ListMediaTypes())
|
||||
{
|
||||
Console.WriteLine(mediaType);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-lp" || args[0] == "--listprograms")
|
||||
{
|
||||
Console.WriteLine("Supported Programs:");
|
||||
foreach (string program in EnumExtensions.ListPrograms())
|
||||
{
|
||||
Console.WriteLine(program);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-ls" || args[0] == "--listsystems")
|
||||
{
|
||||
Console.WriteLine("Supported Systems:");
|
||||
foreach (string system in Extensions.ListSystems())
|
||||
{
|
||||
Console.WriteLine(system);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#endif
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Converters
|
||||
{
|
||||
@@ -34,7 +34,7 @@ namespace MPF.Core.Converters
|
||||
}
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Convert IMAPI physical media type to a MediaType
|
||||
/// </summary>
|
||||
@@ -98,8 +98,7 @@ namespace MPF.Core.Converters
|
||||
|
||||
if (!LongNameMethods.TryGetValue(sourceType, out MethodInfo method))
|
||||
{
|
||||
|
||||
method = typeof(RedumpLib.Data.Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
|
||||
method = typeof(SabreTools.RedumpLib.Data.Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
|
||||
if (method == null)
|
||||
method = typeof(EnumConverter).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
|
||||
|
||||
@@ -131,10 +130,10 @@ namespace MPF.Core.Converters
|
||||
|
||||
case InternalProgram.Aaru:
|
||||
return "Aaru";
|
||||
case InternalProgram.DD:
|
||||
return "dd";
|
||||
case InternalProgram.DiscImageCreator:
|
||||
return "DiscImageCreator";
|
||||
case InternalProgram.Redumper:
|
||||
return "redumper";
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -181,8 +180,9 @@ namespace MPF.Core.Converters
|
||||
case "dicreator":
|
||||
case "discimagecreator":
|
||||
return InternalProgram.DiscImageCreator;
|
||||
case "dd":
|
||||
return InternalProgram.DD;
|
||||
case "rd":
|
||||
case "redumper":
|
||||
return InternalProgram.Redumper;
|
||||
|
||||
// Verification support only
|
||||
case "cleanrip":
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
@@ -19,6 +19,7 @@ namespace MPF.Core.Data
|
||||
// Private lists of known drive speed ranges
|
||||
private static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
private static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
|
||||
private static IReadOnlyList<int> Unknown { get; } = new List<int> { 1 };
|
||||
|
||||
@@ -35,10 +36,11 @@ namespace MPF.Core.Data
|
||||
case MediaType.GDROM:
|
||||
return CD;
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return DVD;
|
||||
case MediaType.HDDVD:
|
||||
return HDDVD;
|
||||
case MediaType.BluRay:
|
||||
return BD;
|
||||
default:
|
||||
@@ -82,13 +84,20 @@ 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";
|
||||
public const string ReportedDiscType = "Reported Disc Type";
|
||||
public const string PVDField = "Primary Volume Descriptor (PVD)";
|
||||
public const string DATField = "DAT";
|
||||
public const string SizeField = "Size";
|
||||
public const string CRC32Field = "CRC32";
|
||||
public const string MD5Field = "MD5";
|
||||
public const string SHA1Field = "SHA1";
|
||||
public const string MatchingIDsField = "Matching IDs";
|
||||
public const string FullyMatchingIDField = "Fully Matching ID";
|
||||
public const string PartiallyMatchingIDsField = "Partially Matching IDs";
|
||||
public const string ErrorCountField = "Error Count";
|
||||
public const string CuesheetField = "Cuesheet";
|
||||
public const string SubIntentionField = "SubIntention Data (SecuROM/LibCrypt)";
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management;
|
||||
using Microsoft.Management.Infrastructure;
|
||||
using Microsoft.Management.Infrastructure.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Utilities;
|
||||
using RedumpLib.Data;
|
||||
#if NET_FRAMEWORK
|
||||
using SabreTools.RedumpLib.Data;
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#else
|
||||
using Aaru.CommonTypes.Enums;
|
||||
using AaruDevices = Aaru.Devices;
|
||||
#endif
|
||||
|
||||
namespace MPF.Core.Data
|
||||
@@ -18,8 +16,14 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Represents information for a single drive
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TODO: This needs to be less Windows-centric. Devices do not always have a single letter that can be used.
|
||||
/// TODO: Can the Aaru models be used instead of the ones I've created here?
|
||||
/// </remarks>
|
||||
public class Drive
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Represents drive type
|
||||
/// </summary>
|
||||
@@ -28,41 +32,32 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Drive partition format
|
||||
/// </summary>
|
||||
public string DriveFormat => driveInfo?.DriveFormat;
|
||||
|
||||
/// <summary>
|
||||
/// Windows drive letter
|
||||
/// </summary>
|
||||
public char Letter => driveInfo?.Name[0] ?? '\0';
|
||||
public string DriveFormat { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Windows drive path
|
||||
/// </summary>
|
||||
public string Name => driveInfo?.Name;
|
||||
public string Name { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Represents if Windows has marked the drive as active
|
||||
/// </summary>
|
||||
public bool MarkedActive => driveInfo?.IsReady ?? false;
|
||||
public bool MarkedActive { get; private set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the total size of the drive
|
||||
/// </summary>
|
||||
public long TotalSize { get; private set; } = default;
|
||||
|
||||
/// <summary>
|
||||
/// Media label as read by Windows
|
||||
/// </summary>
|
||||
/// <remarks>The try/catch is needed because Windows will throw an exception if the drive is not marked as active</remarks>
|
||||
public string VolumeLabel
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return driveInfo?.VolumeLabel;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
public string VolumeLabel { get; private set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Derived Fields
|
||||
|
||||
/// <summary>
|
||||
/// Media label as read by Windows, formatted to avoid odd outputs
|
||||
@@ -72,12 +67,12 @@ namespace MPF.Core.Data
|
||||
get
|
||||
{
|
||||
string volumeLabel = Template.DiscNotDetected;
|
||||
if (driveInfo.IsReady)
|
||||
if (this.MarkedActive)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(driveInfo.VolumeLabel))
|
||||
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
|
||||
volumeLabel = "track";
|
||||
else
|
||||
volumeLabel = driveInfo.VolumeLabel;
|
||||
volumeLabel = this.VolumeLabel;
|
||||
}
|
||||
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
@@ -88,80 +83,94 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DriveInfo object representing the drive, if possible
|
||||
/// Windows drive letter
|
||||
/// </summary>
|
||||
private readonly DriveInfo driveInfo;
|
||||
public char Letter => this.Name == null || this.Name.Length == 0 ? '\0' : this.Name[0];
|
||||
|
||||
public Drive(InternalDriveType? driveType, DriveInfo driveInfo)
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Protected constructor
|
||||
/// </summary>
|
||||
protected Drive() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new Drive object from a drive type and device path
|
||||
/// </summary>
|
||||
/// <param name="driveType">InternalDriveType value representing the drive type</param>
|
||||
/// <param name="devicePath">Path to the device according to the local machine</param>
|
||||
public static Drive Create(InternalDriveType? driveType, string devicePath)
|
||||
{
|
||||
this.InternalDriveType = driveType;
|
||||
this.driveInfo = driveInfo;
|
||||
// Create a new, empty drive object
|
||||
var drive = new Drive()
|
||||
{
|
||||
InternalDriveType = driveType,
|
||||
};
|
||||
|
||||
// If we have an invalid device path, return null
|
||||
if (string.IsNullOrWhiteSpace(devicePath))
|
||||
return null;
|
||||
|
||||
// Sanitize a Windows-formatted long device path
|
||||
if (devicePath.StartsWith("\\\\.\\"))
|
||||
devicePath = devicePath.Substring("\\\\.\\".Length);
|
||||
|
||||
// Create and validate the drive info object
|
||||
var driveInfo = new DriveInfo(devicePath);
|
||||
if (driveInfo == null || driveInfo == default)
|
||||
return null;
|
||||
|
||||
// Fill in the rest of the data
|
||||
drive.PopulateFromDriveInfo(driveInfo);
|
||||
|
||||
return drive;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate all fields from a DriveInfo object
|
||||
/// </summary>
|
||||
/// <param name="driveInfo">DriveInfo object to populate from</param>
|
||||
private void PopulateFromDriveInfo(DriveInfo driveInfo)
|
||||
{
|
||||
// If we have an invalid DriveInfo, just return
|
||||
if (driveInfo == null || driveInfo == default)
|
||||
return;
|
||||
|
||||
// Populate the data fields
|
||||
this.Name = driveInfo.Name;
|
||||
this.MarkedActive = driveInfo.IsReady;
|
||||
if (this.MarkedActive)
|
||||
{
|
||||
this.DriveFormat = driveInfo.DriveFormat;
|
||||
this.TotalSize = driveInfo.TotalSize;
|
||||
this.VolumeLabel = driveInfo.VolumeLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.DriveFormat = string.Empty;
|
||||
this.TotalSize = default;
|
||||
this.VolumeLabel = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
#region Public Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Create a list of active drives matched to their volume labels
|
||||
/// </summary>
|
||||
/// <param name="ignoreFixedDrives">Ture to ignore fixed drives from population, false otherwise</param>
|
||||
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
|
||||
/// <returns>Active drives, matched to labels, if possible</returns>
|
||||
/// <remarks>
|
||||
/// https://stackoverflow.com/questions/3060796/how-to-distinguish-between-usb-and-floppy-devices?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
|
||||
/// https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx
|
||||
/// </remarks>
|
||||
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
|
||||
{
|
||||
var desiredDriveTypes = new List<DriveType>() { DriveType.CDRom };
|
||||
if (!ignoreFixedDrives)
|
||||
{
|
||||
desiredDriveTypes.Add(DriveType.Fixed);
|
||||
desiredDriveTypes.Add(DriveType.Removable);
|
||||
}
|
||||
|
||||
// Get all supported drive types
|
||||
var drives = DriveInfo.GetDrives()
|
||||
.Where(d => desiredDriveTypes.Contains(d.DriveType))
|
||||
.Select(d => new Drive(EnumConverter.ToInternalDriveType(d.DriveType), d))
|
||||
.ToList();
|
||||
|
||||
// TODO: Management searcher stuff is not supported on other platforms
|
||||
// Get the floppy drives and set the flag from removable
|
||||
try
|
||||
{
|
||||
ManagementObjectSearcher searcher =
|
||||
new ManagementObjectSearcher("root\\CIMV2",
|
||||
"SELECT * FROM Win32_LogicalDisk");
|
||||
|
||||
var collection = searcher.Get();
|
||||
foreach (ManagementObject queryObj in collection)
|
||||
{
|
||||
uint? mediaType = (uint?)queryObj["MediaType"];
|
||||
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
|
||||
{
|
||||
char devId = queryObj["DeviceID"].ToString()[0];
|
||||
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
|
||||
// Order the drives by drive letter
|
||||
drives = drives.OrderBy(i => i.Letter).ToList();
|
||||
|
||||
var drives = GetDriveList(ignoreFixedDrives);
|
||||
drives = drives?.OrderBy(i => i.Letter)?.ToList();
|
||||
return drives;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current media type from drive letter
|
||||
/// </summary>
|
||||
/// <param name="drive"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This may eventually be replaced by Aaru.Devices being able to be about 10x more accurate.
|
||||
/// This will also end up making it so that IMAPI2 is no longer necessary. Unfortunately, that
|
||||
/// will only work for .NET Core 3.1 and beyond.
|
||||
/// </remarks>
|
||||
public (MediaType?, string) GetMediaType()
|
||||
{
|
||||
// Take care of the non-optical stuff first
|
||||
@@ -172,22 +181,25 @@ namespace MPF.Core.Data
|
||||
return (MediaType.HardDisk, null);
|
||||
else if (this.InternalDriveType == Data.InternalDriveType.Removable)
|
||||
return (MediaType.FlashDrive, null);
|
||||
#if NET6_0_OR_GREATER
|
||||
else
|
||||
return GetMediaTypeFromSize();
|
||||
#endif
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
// Get the current drive information
|
||||
string deviceId = null;
|
||||
bool loaded = false;
|
||||
try
|
||||
{
|
||||
// Get the device ID first
|
||||
var searcher = new ManagementObjectSearcher(
|
||||
"root\\CIMV2",
|
||||
$"SELECT * FROM Win32_CDROMDrive WHERE Id = '{this.Letter}:\'");
|
||||
CimSession session = CimSession.Create(null);
|
||||
var collection = session.QueryInstances("root\\CIMV2", "WQL", $"SELECT * FROM Win32_CDROMDrive WHERE Id = '{this.Letter}:\'");
|
||||
|
||||
foreach (ManagementObject queryObj in searcher.Get())
|
||||
foreach (CimInstance instance in collection)
|
||||
{
|
||||
deviceId = (string)queryObj["DeviceID"];
|
||||
loaded = (bool)queryObj["MediaLoaded"];
|
||||
CimKeyedCollection<CimProperty> properties = instance.CimInstanceProperties;
|
||||
deviceId = (string)properties["DeviceID"]?.Value;
|
||||
loaded = (bool)properties["MediaLoaded"]?.Value;
|
||||
}
|
||||
|
||||
// If we got no valid device, we don't care and just return
|
||||
@@ -196,6 +208,8 @@ namespace MPF.Core.Data
|
||||
else if (!loaded)
|
||||
return (null, "Device is not reporting media loaded");
|
||||
|
||||
#if NETFRAMEWORK
|
||||
|
||||
MsftDiscMaster2 discMaster = new MsftDiscMaster2();
|
||||
deviceId = deviceId.ToLower().Replace('\\', '#').Replace('/', '#');
|
||||
string id = null;
|
||||
@@ -223,31 +237,17 @@ namespace MPF.Core.Data
|
||||
|
||||
var media = dataWriter.CurrentPhysicalMediaType;
|
||||
return (media.IMAPIToMediaType(), null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (null, ex.Message);
|
||||
}
|
||||
|
||||
#else
|
||||
try
|
||||
{
|
||||
var device = new AaruDevices.Device(this.Name);
|
||||
if (device.Error)
|
||||
return (null, "Could not open device");
|
||||
else if (device.Type != DeviceType.ATAPI && device.Type != DeviceType.SCSI)
|
||||
return (null, "Device does not support media type detection");
|
||||
|
||||
// TODO: In order to get the disc type, Aaru.Core will need to be included as a
|
||||
// package. Unfortunately, it currently has a conflict with one of the required libraries:
|
||||
// System.Text.Encoding.CodePages (BOS uses >= 5.0.0, DotNetZip uses >= 4.5.0 && < 5.0.0)
|
||||
return (null, "IMAPI2 recorder not supported");
|
||||
|
||||
#endif
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (null, ex.Message);
|
||||
}
|
||||
|
||||
return (null, "Media detection only supported on .NET Framework");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -273,19 +273,51 @@ namespace MPF.Core.Data
|
||||
if (systemFromLabel != null)
|
||||
return systemFromLabel;
|
||||
|
||||
// Get a list of files for quicker checking
|
||||
#region Consoles
|
||||
|
||||
// Bandai Playdia Quick Interactive System
|
||||
try
|
||||
{
|
||||
List<string> files = Directory.EnumerateFiles(drivePath, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
|
||||
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
|
||||
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return RedumpSystem.BandaiPlaydiaQuickInteractiveSystem;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Mattel Fisher-Price iXL
|
||||
if (File.Exists(Path.Combine(drivePath, "iXL", "iXLUpdater.exe")))
|
||||
{
|
||||
return RedumpSystem.MattelFisherPriceiXL;
|
||||
}
|
||||
|
||||
// Microsoft Xbox 360
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "$SystemUpdate"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any())
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any()
|
||||
&& this.TotalSize <= 500_000_000)
|
||||
{
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Microsoft Xbox One
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "MSXC"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "MSXC")).Any())
|
||||
{
|
||||
return RedumpSystem.MicrosoftXboxOne;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Sega Dreamcast
|
||||
if (File.Exists(Path.Combine(drivePath, "IP.BIN")))
|
||||
{
|
||||
@@ -330,6 +362,35 @@ namespace MPF.Core.Data
|
||||
return RedumpSystem.SonyPlayStation;
|
||||
}
|
||||
|
||||
// Sony PlayStation 3
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "PS3_GAME"))
|
||||
|| Directory.Exists(Path.Combine(drivePath, "PS3_UPDATE"))
|
||||
|| File.Exists(Path.Combine(drivePath, "PS3_DISC.SFB")))
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Sony PlayStation 4
|
||||
// There are more possible paths that could be checked.
|
||||
// There are some entries that can be found on most PS4 discs:
|
||||
// "/app/GAME_SERIAL/app.pkg"
|
||||
// "/bd/param.sfo"
|
||||
// "/license/rif"
|
||||
// There are also extra files that can be found on some discs:
|
||||
// "/patch/GAME_SERIAL/patch.pkg" can be found in Redump entry 66816.
|
||||
// Originally on disc as "/patch/CUSA11302/patch.pkg".
|
||||
// Is used as an on-disc update for the base game app without needing to get update from the internet.
|
||||
// "/addcont/GAME_SERIAL/CONTENT_ID/ac.pkg" can be found in Redump entry 97619.
|
||||
// Originally on disc as "/addcont/CUSA00288/FFXIVEXPS400001A/ac.pkg".
|
||||
if (File.Exists(Path.Combine(drivePath, "PS4", "UPDATE", "PS4UPDATE.PUP")))
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation4;
|
||||
}
|
||||
|
||||
// V.Tech V.Flash / V.Smile Pro
|
||||
if (File.Exists(Path.Combine(drivePath, "0SYSTEM")))
|
||||
{
|
||||
@@ -338,6 +399,16 @@ namespace MPF.Core.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region Computers
|
||||
|
||||
// Sharp X68000
|
||||
if (File.Exists(Path.Combine(drivePath, "COMMAND.X")))
|
||||
{
|
||||
return RedumpSystem.SharpX68000;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Video Formats
|
||||
|
||||
// BD-Video
|
||||
@@ -424,6 +495,10 @@ namespace MPF.Core.Data
|
||||
//if (this.VolumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
// return RedumpSystem.MicrosoftXbox360;
|
||||
|
||||
// Sega Mega-CD / Sega-CD
|
||||
if (this.VolumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SegaMegaCDSegaCD;
|
||||
|
||||
// Sony PlayStation 3
|
||||
if (this.VolumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
@@ -448,7 +523,7 @@ namespace MPF.Core.Data
|
||||
public byte[] ReadSector(long num, int size = 2048)
|
||||
{
|
||||
// Missing drive leter is not supported
|
||||
if (string.IsNullOrEmpty(this.driveInfo?.Name))
|
||||
if (string.IsNullOrEmpty(this.Name))
|
||||
return null;
|
||||
|
||||
// We don't support negative sectors
|
||||
@@ -480,5 +555,96 @@ namespace MPF.Core.Data
|
||||
fs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the current drive information based on path
|
||||
/// </summary>
|
||||
public void RefreshDrive()
|
||||
{
|
||||
var driveInfo = DriveInfo.GetDrives().FirstOrDefault(d => d?.Name == this.Name);
|
||||
this.PopulateFromDriveInfo(driveInfo);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Get the media type for a device path based on size
|
||||
/// </summary>
|
||||
/// <returns>MediaType, null on error</returns>
|
||||
private (MediaType?, string) GetMediaTypeFromSize()
|
||||
{
|
||||
if (this.TotalSize >= 0 && this.TotalSize < 800_000_000 && this.DriveFormat == "CDFS")
|
||||
return (MediaType.CDROM, null);
|
||||
else if (this.TotalSize >= 400_000_000 && this.TotalSize <= 8_540_000_000 && this.DriveFormat == "UDF")
|
||||
return (MediaType.DVD, null);
|
||||
else
|
||||
return (MediaType.BluRay, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all current attached Drives
|
||||
/// </summary>
|
||||
/// <param name="ignoreFixedDrives">True to ignore fixed drives from population, false otherwise</param>
|
||||
/// <returns>List of drives, null on error</returns>
|
||||
/// <remarks>
|
||||
/// https://stackoverflow.com/questions/3060796/how-to-distinguish-between-usb-and-floppy-devices?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
|
||||
/// https://msdn.microsoft.com/en-us/library/aa394173(v=vs.85).aspx
|
||||
/// </remarks>
|
||||
private static List<Drive> GetDriveList(bool ignoreFixedDrives)
|
||||
{
|
||||
var desiredDriveTypes = new List<DriveType>() { DriveType.CDRom };
|
||||
if (!ignoreFixedDrives)
|
||||
{
|
||||
desiredDriveTypes.Add(DriveType.Fixed);
|
||||
desiredDriveTypes.Add(DriveType.Removable);
|
||||
}
|
||||
|
||||
// TODO: Reduce reliance on `DriveInfo`
|
||||
// https://github.com/aaru-dps/Aaru/blob/5164a154e2145941472f2ee0aeb2eff3338ecbb3/Aaru.Devices/Windows/ListDevices.cs#L66
|
||||
|
||||
// Create an output drive list
|
||||
List<Drive> drives = new List<Drive>();
|
||||
|
||||
// Get all standard supported drive types
|
||||
try
|
||||
{
|
||||
drives = DriveInfo.GetDrives()
|
||||
.Where(d => desiredDriveTypes.Contains(d.DriveType))
|
||||
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name))
|
||||
.ToList();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return drives;
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
CimKeyedCollection<CimProperty> properties = instance.CimInstanceProperties;
|
||||
uint? mediaType = properties["MediaType"]?.Value as uint?;
|
||||
if (mediaType != null && ((mediaType > 0 && mediaType < 11) || (mediaType > 12 && mediaType < 22)))
|
||||
{
|
||||
char devId = (properties["Caption"].Value as string)[0];
|
||||
drives.ForEach(d => { if (d.Letter == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
|
||||
return drives;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
|
||||
// Dumping support
|
||||
Aaru,
|
||||
DD,
|
||||
DiscImageCreator,
|
||||
Redumper,
|
||||
|
||||
// Verification support only
|
||||
CleanRip,
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
public class Options : IDictionary<string, string>, ICloneable
|
||||
public class Options : IDictionary<string, string>
|
||||
{
|
||||
private readonly Dictionary<string, string> _settings;
|
||||
private Dictionary<string, string> _settings;
|
||||
|
||||
#region Internal Program
|
||||
|
||||
@@ -31,12 +31,12 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Path to dd for Windows
|
||||
/// Path to Redumper
|
||||
/// </summary>
|
||||
public string DDPath
|
||||
public string RedumperPath
|
||||
{
|
||||
get { return GetStringSetting(_settings, "DDPath", "Programs\\DD\\dd.exe"); }
|
||||
set { _settings["DDPath"] = value; }
|
||||
get { return GetStringSetting(_settings, "RedumperPath", "Programs\\Redumper\\redumper.exe"); }
|
||||
set { _settings["RedumperPath"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -78,6 +78,15 @@ namespace MPF.Core.Data
|
||||
set { _settings["CheckForUpdatesOnStartup"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fast update label - Skips disc checks and updates path only
|
||||
/// </summary>
|
||||
public bool FastUpdateLabel
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "FastUpdateLabel", false); }
|
||||
set { _settings["FastUpdateLabel"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default output path for dumps
|
||||
/// </summary>
|
||||
@@ -123,7 +132,7 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedCD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedCD", 72); }
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedCD", 24); }
|
||||
set { _settings["PreferredDumpSpeedCD"] = value.ToString(); }
|
||||
}
|
||||
|
||||
@@ -132,16 +141,25 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedDVD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedDVD", 24); }
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedDVD", 16); }
|
||||
set { _settings["PreferredDumpSpeedDVD"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default HD-DVD dumping speed
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedHDDVD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedHDDVD", 8); }
|
||||
set { _settings["PreferredDumpSpeedHDDVD"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default BD dumping speed
|
||||
/// </summary>
|
||||
public int PreferredDumpSpeedBD
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedBD", 16); }
|
||||
get { return GetInt32Setting(_settings, "PreferredDumpSpeedBD", 8); }
|
||||
set { _settings["PreferredDumpSpeedBD"] = value.ToString(); }
|
||||
}
|
||||
|
||||
@@ -198,6 +216,24 @@ namespace MPF.Core.Data
|
||||
|
||||
#region DiscImageCreator
|
||||
|
||||
/// <summary>
|
||||
/// Enable multi-sector read flag by default
|
||||
/// </summary>
|
||||
public bool DICMultiSectorRead
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "DICMultiSectorRead", false); }
|
||||
set { _settings["DICMultiSectorRead"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Include a default multi-sector read value
|
||||
/// </summary>
|
||||
public int DICMultiSectorReadValue
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "DICMultiSectorReadValue", 0); }
|
||||
set { _settings["DICMultiSectorReadValue"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable overly-secure dumping flags by default
|
||||
/// </summary>
|
||||
@@ -230,6 +266,15 @@ namespace MPF.Core.Data
|
||||
set { _settings["DICRereadCount"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default number of DVD/HD-DVD/BD rereads
|
||||
/// </summary>
|
||||
public int DICDVDRereadCount
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "DICDVDRereadCount", 10); }
|
||||
set { _settings["DICDVDRereadCount"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset drive after dumping (useful for older drives)
|
||||
/// </summary>
|
||||
@@ -250,6 +295,37 @@ namespace MPF.Core.Data
|
||||
|
||||
#endregion
|
||||
|
||||
#region Redumper
|
||||
|
||||
/// <summary>
|
||||
/// Enable debug output while dumping by default
|
||||
/// </summary>
|
||||
public bool RedumperEnableDebug
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "RedumperEnableDebug", false); }
|
||||
set { _settings["RedumperEnableDebug"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable verbose output while dumping by default
|
||||
/// </summary>
|
||||
public bool RedumperEnableVerbose
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "RedumperEnableVerbose", false); }
|
||||
set { _settings["RedumperEnableVerbose"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default number of rereads
|
||||
/// </summary>
|
||||
public int RedumperRereadCount
|
||||
{
|
||||
get { return GetInt32Setting(_settings, "RedumperRereadCount", 20); }
|
||||
set { _settings["RedumperRereadCount"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Extra Dumping Options
|
||||
|
||||
/// <summary>
|
||||
@@ -261,6 +337,15 @@ namespace MPF.Core.Data
|
||||
set { _settings["ScanForProtection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output all found protections to a separate file in the directory
|
||||
/// </summary>
|
||||
public bool OutputSeparateProtectionFile
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "OutputSeparateProtectionFile", true); }
|
||||
set { _settings["OutputSeparateProtectionFile"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add placeholder values in the submission info
|
||||
/// </summary>
|
||||
@@ -288,6 +373,15 @@ namespace MPF.Core.Data
|
||||
set { _settings["EnableTabsInInputFields"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Limit outputs to Redump-supported values only
|
||||
/// </summary>
|
||||
public bool EnableRedumpCompatibility
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "EnableRedumpCompatibility", true); }
|
||||
set { _settings["EnableRedumpCompatibility"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show disc eject reminder before the disc information window is shown
|
||||
/// </summary>
|
||||
@@ -395,15 +489,6 @@ namespace MPF.Core.Data
|
||||
set { _settings["ScanPackersForProtection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force scanning all files for protection
|
||||
/// </summary>
|
||||
public bool ForceScanningForProtection
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "ForceScanningForProtection", false); }
|
||||
set { _settings["ForceScanningForProtection"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Include debug information with scan results
|
||||
/// </summary>
|
||||
@@ -435,30 +520,6 @@ namespace MPF.Core.Data
|
||||
set { _settings["OpenLogWindowAtStartup"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable fancy formatting of log statements
|
||||
/// Disables EnableProgressProcessing if disabled
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is mainly for outputting redirected console outputs. Not many
|
||||
/// other bits of the logs include any specially handled outputs.
|
||||
/// </remarks>
|
||||
public bool EnableLogFormatting
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "EnableLogFormatting", false); }
|
||||
set { _settings["EnableLogFormatting"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable progress bar updating based on log text
|
||||
/// Disabled if EnableLogFormatting is disabled
|
||||
/// </summary>
|
||||
public bool EnableProgressProcessing
|
||||
{
|
||||
get { return GetBooleanSetting(_settings, "EnableProgressProcessing", false); }
|
||||
set { _settings["EnableProgressProcessing"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Redump Login Information
|
||||
@@ -493,11 +554,21 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a clone of the object
|
||||
/// Constructor taking an existing Options object
|
||||
/// </summary>
|
||||
public object Clone()
|
||||
/// <param name="source"></param>
|
||||
public Options(Options source)
|
||||
{
|
||||
return new Options(new Dictionary<string, string>(_settings));
|
||||
_settings = new Dictionary<string, string>(source._settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set all fields from an existing Options object
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public void SetFromExisting(Options source)
|
||||
{
|
||||
_settings = new Dictionary<string, string>(source._settings);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
@@ -1,32 +1,10 @@
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information specific to an XGD disc
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// XGD1 XMID Format Information:
|
||||
///
|
||||
/// AABBBCCD
|
||||
/// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details)
|
||||
/// - BBB => Game ID
|
||||
/// - CC => Version number
|
||||
/// - D => Region identifier (see GetRegion for details)
|
||||
///
|
||||
/// XGD2/3 XeMID Format Information:
|
||||
///
|
||||
/// AABCCCDDEFFGHH(IIIIIIII)
|
||||
/// - AA => The two-ASCII-character publisher identifier (see GetPublisher for details)
|
||||
/// - B => Platform identifier; 2 indicates Xbox 360.
|
||||
/// - CCC => Game ID
|
||||
/// - DD => SKU number (unique per SKU of a title)
|
||||
/// - E => Region identifier (see GetRegion for details)
|
||||
/// - FF => Base version; usually starts at 01 (can be 1 or 2 characters)
|
||||
/// - G => Media type identifier (see GetMediaSubtype for details)
|
||||
/// - HH => Disc number stored in [disc number][total discs] format
|
||||
/// - IIIIIIII => 8-hex-digit certification submission identifier; usually on test discs only
|
||||
/// </remarks>
|
||||
public class XgdInfo
|
||||
{
|
||||
#region Fields
|
||||
@@ -39,75 +17,17 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Raw XMID/XeMID string that all other information is derived from
|
||||
/// </summary>
|
||||
public string XMID { get; private set; }
|
||||
public string RawXMID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 2-character publisher identifier
|
||||
/// XGD1 XMID
|
||||
/// </summary>
|
||||
public string PublisherIdentifier { get; private set; }
|
||||
public SabreTools.Models.Xbox.XMID XMID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Platform disc is made for, 2 indicates Xbox 360
|
||||
/// XGD2/3 XeMID
|
||||
/// </summary>
|
||||
public char? PlatformIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Game ID
|
||||
/// </summary>
|
||||
public string GameID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// For XGD1: Internal version number
|
||||
/// For XGD2/3: Title-specific SKU
|
||||
/// </summary>
|
||||
public string SKU { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Region identifier character
|
||||
/// </summary>
|
||||
public char RegionIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Base version of executables, usually starts at 01
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// TODO: Check if this is always 2 characters for XGD2/3
|
||||
/// </remarks>
|
||||
public string BaseVersion { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Media subtype identifier
|
||||
/// </summary>
|
||||
public char MediaSubtypeIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Disc number stored in [disc number][total discs] format
|
||||
/// </summary>
|
||||
public string DiscNumberIdentifier { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 8-hex-digit certification submission identifier; usually on test discs only
|
||||
/// </summary>
|
||||
public string CertificationSubmissionIdentifier { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Auto-Generated Information
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable name derived from the publisher identifier
|
||||
/// </summary>
|
||||
public string PublisherName => GetPublisher(this.PublisherIdentifier);
|
||||
|
||||
/// <summary>
|
||||
/// Internally represented region
|
||||
/// </summary>
|
||||
public Region? InternalRegion => GetRegion(this.RegionIdentifier);
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable subtype derived from the media identifier
|
||||
/// </summary>
|
||||
public string MediaSubtype => GetMediaSubtype(this.MediaSubtypeIdentifier);
|
||||
public SabreTools.Models.Xbox.XeMID XeMID { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -115,24 +35,23 @@ namespace MPF.Core.Data
|
||||
/// Populate a set of XGD information from a Master ID (XMID/XeMID) string
|
||||
/// </summary>
|
||||
/// <param name="xmid">XMID/XeMID string representing the DMI information</param>
|
||||
/// <param name="validate">True if value validation should be performed, false otherwise</param>
|
||||
public XgdInfo(string xmid, bool validate = false)
|
||||
public XgdInfo(string xmid)
|
||||
{
|
||||
this.Initialized = false;
|
||||
if (string.IsNullOrWhiteSpace(xmid))
|
||||
return;
|
||||
|
||||
this.XMID = xmid.TrimEnd('\0');
|
||||
if (string.IsNullOrWhiteSpace(this.XMID))
|
||||
this.RawXMID = xmid.TrimEnd('\0');
|
||||
if (string.IsNullOrWhiteSpace(this.RawXMID))
|
||||
return;
|
||||
|
||||
// XGD1 information is 8 characters
|
||||
if (this.XMID.Length == 8)
|
||||
this.Initialized = ParseXGD1XMID(this.XMID, validate);
|
||||
if (this.RawXMID.Length == 8)
|
||||
this.Initialized = ParseXGD1XMID(this.RawXMID);
|
||||
|
||||
// XGD2/3 information is semi-variable length
|
||||
else if (this.XMID.Length == 13 || this.XMID.Length == 14 || this.XMID.Length == 21 || this.XMID.Length == 22)
|
||||
this.Initialized = ParseXGD23XeMID(this.XMID, validate);
|
||||
else if (this.RawXMID.Length == 13 || this.RawXMID.Length == 14 || this.RawXMID.Length == 21 || this.RawXMID.Length == 22)
|
||||
this.Initialized = ParseXGD23XeMID(this.RawXMID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -147,12 +66,12 @@ namespace MPF.Core.Data
|
||||
try
|
||||
{
|
||||
// XGD1 doesn't use PlatformIdentifier
|
||||
if (this.PlatformIdentifier == null)
|
||||
return $"{this.PublisherIdentifier}-{this.GameID}";
|
||||
if (XMID != null)
|
||||
return $"{XMID.PublisherIdentifier}-{XMID.GameID}";
|
||||
|
||||
// XGD2/3 uses a specific identifier
|
||||
else if (this.PlatformIdentifier == '2')
|
||||
return $"{this.PublisherIdentifier}-{this.PlatformIdentifier}{this.GameID}";
|
||||
else if (XeMID?.PlatformIdentifier == '2')
|
||||
return $"{XeMID.PublisherIdentifier}-{XeMID.PlatformIdentifier}{XeMID.GameID}";
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -175,12 +94,12 @@ namespace MPF.Core.Data
|
||||
try
|
||||
{
|
||||
// XGD1 doesn't use PlatformIdentifier
|
||||
if (this.PlatformIdentifier == null)
|
||||
return $"1.{this.SKU}";
|
||||
if (XMID != null)
|
||||
return $"1.{XMID.VersionNumber}";
|
||||
|
||||
// XGD2/3 uses a specific identifier
|
||||
else if (this.PlatformIdentifier == '2')
|
||||
return $"1.{this.SKU}";
|
||||
else if (XeMID?.PlatformIdentifier == '2')
|
||||
return $"1.{XeMID.SKU}";
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -193,217 +112,55 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Parse an XGD1 XMID string
|
||||
/// </summary>
|
||||
/// <param name="xmid">XMID string to attempt to parse</param>
|
||||
/// <param name="validate">True if value validation should be performed, false otherwise</param>
|
||||
/// <param name="rawXmid">XMID string to attempt to parse</param>
|
||||
/// <returns>True if the XMID could be parsed, false otherwise</returns>
|
||||
private bool ParseXGD1XMID(string xmid, bool validate)
|
||||
private bool ParseXGD1XMID(string rawXmid)
|
||||
{
|
||||
if (xmid == null || xmid.Length != 8)
|
||||
return false;
|
||||
try
|
||||
{
|
||||
var xmid = new SabreTools.Serialization.Files.XMID().Deserialize(rawXmid);
|
||||
if (xmid == null)
|
||||
return false;
|
||||
|
||||
this.PublisherIdentifier = xmid.Substring(0, 2);
|
||||
if (validate && string.IsNullOrEmpty(this.PublisherName))
|
||||
this.XMID = xmid;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
|
||||
this.GameID = xmid.Substring(2, 3);
|
||||
this.SKU = xmid.Substring(5, 2);
|
||||
this.RegionIdentifier = xmid[7];
|
||||
if (validate && this.InternalRegion == null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an XGD2/3 XeMID string
|
||||
/// </summary>
|
||||
/// <param name="xemid">XeMID string to attempt to parse</param>
|
||||
/// <param name="validate">True if value validation should be performed, false otherwise</param>
|
||||
/// <param name="rawXemid">XeMID string to attempt to parse</param>
|
||||
/// <returns>True if the XeMID could be parsed, false otherwise</returns>
|
||||
private bool ParseXGD23XeMID(string xemid, bool validate)
|
||||
private bool ParseXGD23XeMID(string rawXemid)
|
||||
{
|
||||
if (xemid == null
|
||||
|| (xemid.Length != 13 && xemid.Length != 14
|
||||
&& xemid.Length != 21 && xemid.Length != 22))
|
||||
return false;
|
||||
|
||||
this.PublisherIdentifier = xemid.Substring(0, 2);
|
||||
if (validate && string.IsNullOrEmpty(this.PublisherName))
|
||||
return false;
|
||||
|
||||
this.PlatformIdentifier = xemid[2];
|
||||
if (validate && this.PlatformIdentifier != '2')
|
||||
return false;
|
||||
|
||||
this.GameID = xemid.Substring(3, 3);
|
||||
this.SKU = xemid.Substring(6, 2);
|
||||
this.RegionIdentifier = xemid[8];
|
||||
if (validate && this.InternalRegion == null)
|
||||
return false;
|
||||
|
||||
if (xemid.Length == 13 || xemid.Length == 21)
|
||||
try
|
||||
{
|
||||
this.BaseVersion = xemid.Substring(9, 1);
|
||||
this.MediaSubtypeIdentifier = xemid[10];
|
||||
if (validate && string.IsNullOrEmpty(this.MediaSubtype))
|
||||
var xemid = new SabreTools.Serialization.Files.XeMID().Deserialize(rawXemid);
|
||||
if (xemid == null)
|
||||
return false;
|
||||
|
||||
this.DiscNumberIdentifier = xemid.Substring(11, 2);
|
||||
this.XeMID = xemid;
|
||||
return true;
|
||||
}
|
||||
else if (xemid.Length == 14 || xemid.Length == 22)
|
||||
catch
|
||||
{
|
||||
this.BaseVersion = xemid.Substring(9, 2);
|
||||
this.MediaSubtypeIdentifier = xemid[11];
|
||||
if (validate && string.IsNullOrEmpty(this.MediaSubtype))
|
||||
return false;
|
||||
|
||||
this.DiscNumberIdentifier = xemid.Substring(12, 2);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xemid.Length == 21)
|
||||
this.CertificationSubmissionIdentifier = xemid.Substring(13);
|
||||
else if (xemid.Length == 22)
|
||||
this.CertificationSubmissionIdentifier = xemid.Substring(14);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Determine the XGD type based on the XGD2/3 media type identifier character
|
||||
/// </summary>
|
||||
/// <param name="mediaTypeIdentifier">Character denoting the media type</param>
|
||||
/// <returns>Media subtype as a string, if possible</returns>
|
||||
private static string GetMediaSubtype(char mediaTypeIdentifier)
|
||||
{
|
||||
switch (mediaTypeIdentifier)
|
||||
{
|
||||
case 'F': return "XGD3";
|
||||
case 'X': return "XGD2";
|
||||
case 'Z': return "Games on Demand / Marketplace Demo";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the full name of the publisher from the 2-character identifier
|
||||
/// </summary>
|
||||
/// <param name="publisherIdentifier">Case-sensitive 2-character identifier</param>
|
||||
/// <returns>Publisher name, if possible</returns>
|
||||
/// <see cref="https://xboxdevwiki.net/Xbe#Title_ID"/>
|
||||
private static string GetPublisher(string publisherIdentifier)
|
||||
{
|
||||
switch (publisherIdentifier)
|
||||
{
|
||||
case "AC": return "Acclaim Entertainment";
|
||||
case "AH": return "ARUSH Entertainment";
|
||||
case "AQ": return "Aqua System";
|
||||
case "AS": return "ASK";
|
||||
case "AT": return "Atlus";
|
||||
case "AV": return "Activision";
|
||||
case "AY": return "Aspyr Media";
|
||||
case "BA": return "Bandai";
|
||||
case "BL": return "Black Box";
|
||||
case "BM": return "BAM! Entertainment";
|
||||
case "BR": return "Broccoli Co.";
|
||||
case "BS": return "Bethesda Softworks";
|
||||
case "BU": return "Bunkasha Co.";
|
||||
case "BV": return "Buena Vista Games";
|
||||
case "BW": return "BBC Multimedia";
|
||||
case "BZ": return "Blizzard";
|
||||
case "CC": return "Capcom";
|
||||
case "CK": return "Kemco Corporation"; // TODO: Confirm
|
||||
case "CM": return "Codemasters";
|
||||
case "CV": return "Crave Entertainment";
|
||||
case "DC": return "DreamCatcher Interactive";
|
||||
case "DX": return "Davilex";
|
||||
case "EA": return "Electronic Arts (EA)";
|
||||
case "EC": return "Encore inc";
|
||||
case "EL": return "Enlight Software";
|
||||
case "EM": return "Empire Interactive";
|
||||
case "ES": return "Eidos Interactive";
|
||||
case "FI": return "Fox Interactive";
|
||||
case "FS": return "From Software";
|
||||
case "GE": return "Genki Co.";
|
||||
case "GV": return "Groove Games";
|
||||
case "HE": return "Tru Blu (Entertainment division of Home Entertainment Suppliers)";
|
||||
case "HP": return "Hip games";
|
||||
case "HU": return "Hudson Soft";
|
||||
case "HW": return "Highwaystar";
|
||||
case "IA": return "Mad Catz Interactive";
|
||||
case "IF": return "Idea Factory";
|
||||
case "IG": return "Infogrames";
|
||||
case "IL": return "Interlex Corporation";
|
||||
case "IM": return "Imagine Media";
|
||||
case "IO": return "Ignition Entertainment";
|
||||
case "IP": return "Interplay Entertainment";
|
||||
case "IX": return "InXile Entertainment"; // TODO: Confirm
|
||||
case "JA": return "Jaleco";
|
||||
case "JW": return "JoWooD";
|
||||
case "KB": return "Kemco"; // TODO: Confirm
|
||||
case "KI": return "Kids Station Inc."; // TODO: Confirm
|
||||
case "KN": return "Konami";
|
||||
case "KO": return "KOEI";
|
||||
case "KU": return "Kobi and / or GAE (formerly Global A Entertainment)"; // TODO: Confirm
|
||||
case "LA": return "LucasArts";
|
||||
case "LS": return "Black Bean Games (publishing arm of Leader S.p.A.)";
|
||||
case "MD": return "Metro3D";
|
||||
case "ME": return "Medix";
|
||||
case "MI": return "Microïds";
|
||||
case "MJ": return "Majesco Entertainment";
|
||||
case "MM": return "Myelin Media";
|
||||
case "MP": return "MediaQuest"; // TODO: Confirm
|
||||
case "MS": return "Microsoft Game Studios";
|
||||
case "MW": return "Midway Games";
|
||||
case "MX": return "Empire Interactive"; // TODO: Confirm
|
||||
case "NK": return "NewKidCo";
|
||||
case "NL": return "NovaLogic";
|
||||
case "NM": return "Namco";
|
||||
case "OX": return "Oxygen Interactive";
|
||||
case "PC": return "Playlogic Entertainment";
|
||||
case "PL": return "Phantagram Co., Ltd.";
|
||||
case "RA": return "Rage";
|
||||
case "SA": return "Sammy";
|
||||
case "SC": return "SCi Games";
|
||||
case "SE": return "SEGA";
|
||||
case "SN": return "SNK";
|
||||
case "SS": return "Simon & Schuster";
|
||||
case "SU": return "Success Corporation";
|
||||
case "SW": return "Swing! Deutschland";
|
||||
case "TA": return "Takara";
|
||||
case "TC": return "Tecmo";
|
||||
case "TD": return "The 3DO Company (or just 3DO)";
|
||||
case "TK": return "Takuyo";
|
||||
case "TM": return "TDK Mediactive";
|
||||
case "TQ": return "THQ";
|
||||
case "TS": return "Titus Interactive";
|
||||
case "TT": return "Take-Two Interactive Software";
|
||||
case "US": return "Ubisoft";
|
||||
case "VC": return "Victor Interactive Software";
|
||||
case "VN": return "Vivendi Universal (just took Interplays publishing rights)"; // TODO: Confirm
|
||||
case "VU": return "Vivendi Universal Games";
|
||||
case "VV": return "Vivendi Universal Games"; // TODO: Confirm
|
||||
case "WE": return "Wanadoo Edition";
|
||||
case "WR": return "Warner Bros. Interactive Entertainment"; // TODO: Confirm
|
||||
case "XI": return "XPEC Entertainment and Idea Factory";
|
||||
case "XK": return "Xbox kiosk disk?"; // TODO: Confirm
|
||||
case "XL": return "Xbox special bundled or live demo disk?"; // TODO: Confirm
|
||||
case "XM": return "Evolved Games"; // TODO: Confirm
|
||||
case "XP": return "XPEC Entertainment";
|
||||
case "XR": return "Panorama";
|
||||
case "YB": return "YBM Sisa (South-Korea)";
|
||||
case "ZD": return "Zushi Games (formerly Zoo Digital Publishing)";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the region based on the XGD serial character
|
||||
/// </summary>
|
||||
/// <param name="region">Character denoting the region</param>
|
||||
/// <returns>Region, if possible</returns>
|
||||
private static Region? GetRegion(char region)
|
||||
public static Region? GetRegion(char region)
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.3</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<Version>2.6.5</Version>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@@ -20,10 +18,6 @@
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<DefineConstants>NET_FRAMEWORK</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<COMReference Include="IMAPI2">
|
||||
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
|
||||
@@ -46,20 +40,13 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="System.Management" Version="6.0.0-rc.1.21451.13" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'!='net48'">
|
||||
<PackageReference Include="Aaru.Devices" Version="5.3.0-rc2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.Management" />
|
||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="2.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.1.2" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.1.2" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
396
MPF.Core/Utilities/Chime.cs
Normal file
396
MPF.Core/Utilities/Chime.cs
Normal file
@@ -0,0 +1,396 @@
|
||||
using System;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Methods to deal with outputting tones to the PC speaker
|
||||
/// </summary>
|
||||
public class Chime
|
||||
{
|
||||
/// <summary>
|
||||
/// Standard duration to play a single tone
|
||||
/// </summary>
|
||||
private const int standardDurationMs = 200;
|
||||
|
||||
#region Octave 0
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(0)
|
||||
/// </summary>
|
||||
private const int noteC0 = 16; // 16.35
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(0)
|
||||
/// </summary>
|
||||
private const int noteD0 = 18; // 18.35
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(0)
|
||||
/// </summary>
|
||||
private const int noteE0 = 21; // 20.60
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(0)
|
||||
/// </summary>
|
||||
private const int noteF0 = 22; // 21.83
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(0)
|
||||
/// </summary>
|
||||
private const int noteG0 = 25; // 24.50
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(0)
|
||||
/// </summary>
|
||||
private const int noteA0 = 28; // 27.50
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(0)
|
||||
/// </summary>
|
||||
private const int noteB0 = 31; // 30.87
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 1
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(1)
|
||||
/// </summary>
|
||||
private const int noteC1 = 33; // 32.70
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(1)
|
||||
/// </summary>
|
||||
private const int noteD1 = 37; // 36.71
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(1)
|
||||
/// </summary>
|
||||
private const int noteE1 = 41; // 41.20
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(1)
|
||||
/// </summary>
|
||||
private const int noteF1 = 44; // 43.65
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(1)
|
||||
/// </summary>
|
||||
private const int noteG1 = 49; // 49.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(1)
|
||||
/// </summary>
|
||||
private const int noteA1 = 55; // 55.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(1)
|
||||
/// </summary>
|
||||
private const int noteB1 = 62; // 61.74
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 2
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(2)
|
||||
/// </summary>
|
||||
private const int noteC2 = 65; // 65.41
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(2)
|
||||
/// </summary>
|
||||
private const int noteD2 = 73; // 73.42
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(2)
|
||||
/// </summary>
|
||||
private const int noteE2 = 82; // 82.41
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(2)
|
||||
/// </summary>
|
||||
private const int noteF2 = 87; // 87.31
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(2)
|
||||
/// </summary>
|
||||
private const int noteG2 = 98; // 98.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(2)
|
||||
/// </summary>
|
||||
private const int noteA2 = 110; // 110.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(2)
|
||||
/// </summary>
|
||||
private const int noteB2 = 123; // 123.47
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 3
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(3)
|
||||
/// </summary>
|
||||
private const int noteC3 = 131; // 130.81
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(3)
|
||||
/// </summary>
|
||||
private const int noteD3 = 147; // 146.83
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(3)
|
||||
/// </summary>
|
||||
private const int noteE3 = 165; // 164.81
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(3)
|
||||
/// </summary>
|
||||
private const int noteF3 = 175; // 174.61
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(3)
|
||||
/// </summary>
|
||||
private const int noteG3 = 196; // 196.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(3)
|
||||
/// </summary>
|
||||
private const int noteA3 = 220; // 220.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(3)
|
||||
/// </summary>
|
||||
private const int noteB3 = 247; // 246.94
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 4
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(4)
|
||||
/// </summary>
|
||||
private const int noteC4 = 262; // 261.63
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(4)
|
||||
/// </summary>
|
||||
private const int noteD4 = 294; // 293.66
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(4)
|
||||
/// </summary>
|
||||
private const int noteE4 = 330; // 329.63
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(4)
|
||||
/// </summary>
|
||||
private const int noteF4 = 349; // 349.23
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(4)
|
||||
/// </summary>
|
||||
private const int noteG4 = 392; // 392.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(4)
|
||||
/// </summary>
|
||||
private const int noteA4 = 440; // 440.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(4)
|
||||
/// </summary>
|
||||
private const int noteB4 = 494; // 493.88
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 5
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(5)
|
||||
/// </summary>
|
||||
private const int noteC5 = 523; // 523.25
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(5)
|
||||
/// </summary>
|
||||
private const int noteD5 = 587; // 587.33
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(5)
|
||||
/// </summary>
|
||||
private const int noteE5 = 659; // 659.25
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(5)
|
||||
/// </summary>
|
||||
private const int noteF5 = 698; // 698.46
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(5)
|
||||
/// </summary>
|
||||
private const int noteG5 = 783; // 783.99
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(5)
|
||||
/// </summary>
|
||||
private const int noteA5 = 880; // 880.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(5)
|
||||
/// </summary>
|
||||
private const int noteB5 = 988; // 987.77
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 6
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(6)
|
||||
/// </summary>
|
||||
private const int noteC6 = 1047; // 1046.50
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(6)
|
||||
/// </summary>
|
||||
private const int noteD6 = 1175; // 1174.66
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(6)
|
||||
/// </summary>
|
||||
private const int noteE6 = 1319; // 1318.51
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(6)
|
||||
/// </summary>
|
||||
private const int noteF6 = 1397; // 1396.91
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(6)
|
||||
/// </summary>
|
||||
private const int noteG6 = 1568; // 1567.98
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(6)
|
||||
/// </summary>
|
||||
private const int noteA6 = 1760; // 1760.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(6)
|
||||
/// </summary>
|
||||
private const int noteB6 = 1976; // 1975.53
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 7
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(7)
|
||||
/// </summary>
|
||||
private const int noteC7 = 2093; // 2093.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(7)
|
||||
/// </summary>
|
||||
private const int noteD7 = 2349; // 2349.32
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(7)
|
||||
/// </summary>
|
||||
private const int noteE7 = 2637; // 2637.02
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(7)
|
||||
/// </summary>
|
||||
private const int noteF7 = 2794; // 2793.83
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(7)
|
||||
/// </summary>
|
||||
private const int noteG7 = 3136; // 3135.96
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(7)
|
||||
/// </summary>
|
||||
private const int noteA7 = 3520; // 3520.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(7)
|
||||
/// </summary>
|
||||
private const int noteB7 = 3951; // 3951.07
|
||||
|
||||
#endregion
|
||||
|
||||
#region Octave 8
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing C(8)
|
||||
/// </summary>
|
||||
private const int noteC8 = 4186; // 4186.01
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing D(8)
|
||||
/// </summary>
|
||||
private const int noteD8 = 4699; // 4698.63
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing E(8)
|
||||
/// </summary>
|
||||
private const int noteE8 = 5274; // 5274.04
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing F(8)
|
||||
/// </summary>
|
||||
private const int noteF8 = 5588; // 5587.65
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing G(8)
|
||||
/// </summary>
|
||||
private const int noteG8 = 6272; // 6271.93
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing A(8)
|
||||
/// </summary>
|
||||
private const int noteA8 = 7040; // 7040.00
|
||||
|
||||
/// <summary>
|
||||
/// Frequency representing B(8)
|
||||
/// </summary>
|
||||
private const int noteB8 = 7902; // 7902.13
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Output a series of beeps for completion, similar to DiscImageCreator
|
||||
/// </summary>
|
||||
/// <param name="success">True if the upward series should play, false otherwise</param>
|
||||
public static void StandardCompletion(bool success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
Console.Beep(noteC4, standardDurationMs);
|
||||
Console.Beep(noteD4, standardDurationMs);
|
||||
Console.Beep(noteE4, standardDurationMs);
|
||||
Console.Beep(noteF4, standardDurationMs);
|
||||
Console.Beep(noteG4, standardDurationMs);
|
||||
Console.Beep(noteA4, standardDurationMs);
|
||||
Console.Beep(noteB4, standardDurationMs);
|
||||
Console.Beep(noteC5, standardDurationMs);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.Beep(noteC5, standardDurationMs);
|
||||
Console.Beep(noteB4, standardDurationMs);
|
||||
Console.Beep(noteA4, standardDurationMs);
|
||||
Console.Beep(noteG4, standardDurationMs);
|
||||
Console.Beep(noteF4, standardDurationMs);
|
||||
Console.Beep(noteE4, standardDurationMs);
|
||||
Console.Beep(noteD4, standardDurationMs);
|
||||
Console.Beep(noteC4, standardDurationMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
using RedumpLib.Data;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
@@ -68,6 +72,7 @@ namespace MPF.Core.Utilities
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
//case RedumpSystem.SonyPlayStation5:
|
||||
case RedumpSystem.SonyPlayStationPortable:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@@ -90,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:
|
||||
@@ -102,26 +109,6 @@ namespace MPF.Core.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a system is a marker value
|
||||
/// </summary>
|
||||
/// <param name="system">RedumpSystem value to check</param>
|
||||
/// <returns>True if the system is a marker value, false otherwise</returns>
|
||||
public static bool IsMarker(this RedumpSystem? system)
|
||||
{
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.MarkerArcadeEnd:
|
||||
case RedumpSystem.MarkerComputerEnd:
|
||||
case RedumpSystem.MarkerDiscBasedConsoleEnd:
|
||||
// case RedumpSystem.MarkerOtherConsoleEnd:
|
||||
case RedumpSystem.MarkerOtherEnd:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a system is considered XGD
|
||||
/// </summary>
|
||||
@@ -140,5 +127,23 @@ namespace MPF.Core.Utilities
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all programs with their short usable names
|
||||
/// </summary>
|
||||
public static List<string> ListPrograms()
|
||||
{
|
||||
var programs = new List<string>();
|
||||
|
||||
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
|
||||
{
|
||||
if (((InternalProgram)val) == InternalProgram.NONE)
|
||||
continue;
|
||||
|
||||
programs.Add($"{((InternalProgram?)val).LongName()}");
|
||||
}
|
||||
|
||||
return programs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
178
MPF.Core/Utilities/OptionsLoader.cs
Normal file
178
MPF.Core/Utilities/OptionsLoader.cs
Normal file
@@ -0,0 +1,178 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
public static class OptionsLoader
|
||||
{
|
||||
#region Arguments
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from application arguments
|
||||
/// </summary>
|
||||
public static (Options, string, int) LoadFromArguments(string[] args, int startIndex = 0)
|
||||
{
|
||||
// Create the output values with defaults
|
||||
var options = new Options()
|
||||
{
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
OutputSubmissionJSON = false,
|
||||
CompressLogFiles = false,
|
||||
};
|
||||
|
||||
string parsedPath = null;
|
||||
|
||||
// These values require multiple parts to be active
|
||||
bool scan = false, protectFile = false;
|
||||
|
||||
// If we have no arguments, just return
|
||||
if (args == null || args.Length == 0)
|
||||
return (options, null, 0);
|
||||
|
||||
// If we have an invalid start index, just return
|
||||
if (startIndex < 0 || startIndex >= args.Length)
|
||||
return (options, null, startIndex);
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
{
|
||||
// Redump login
|
||||
if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
{
|
||||
string[] credentials = args[startIndex].Split('=')[1].Split(';');
|
||||
options.RedumpUsername = credentials[0];
|
||||
options.RedumpPassword = credentials[1];
|
||||
}
|
||||
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
|
||||
{
|
||||
options.RedumpUsername = args[startIndex + 1];
|
||||
options.RedumpPassword = args[startIndex + 2];
|
||||
startIndex += 2;
|
||||
}
|
||||
|
||||
// Use specific program
|
||||
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
string internalProgram = args[startIndex].Split('=')[1];
|
||||
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
string internalProgram = args[startIndex + 1];
|
||||
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Use a device path for physical checks
|
||||
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
|
||||
{
|
||||
parsedPath = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
|
||||
{
|
||||
parsedPath = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Scan for protection (requires device path)
|
||||
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
|
||||
{
|
||||
scan = true;
|
||||
}
|
||||
|
||||
// Output protection to separate file (requires scan for protection)
|
||||
else if (args[startIndex].Equals("-f") || args[startIndex].Equals("--protect-file"))
|
||||
{
|
||||
protectFile = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
|
||||
{
|
||||
options.OutputSubmissionJSON = true;
|
||||
}
|
||||
|
||||
// Compress log and extraneous files
|
||||
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
|
||||
{
|
||||
options.CompressLogFiles = true;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now deal with the complex options
|
||||
options.ScanForProtection = scan && !string.IsNullOrWhiteSpace(parsedPath);
|
||||
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrWhiteSpace(parsedPath);
|
||||
|
||||
return (options, parsedPath, startIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a list of supported arguments and descriptions
|
||||
/// </summary>
|
||||
public static List<string> PrintSupportedArguments()
|
||||
{
|
||||
var supportedArguments = new List<string>();
|
||||
|
||||
supportedArguments.Add("-u, --use <program> Dumping program output type [REQUIRED]");
|
||||
supportedArguments.Add("-c, --credentials <user> <pw> Redump username and password");
|
||||
supportedArguments.Add("-p, --path <drivepath> Physical drive path for additional checks");
|
||||
supportedArguments.Add("-s, --scan Enable copy protection scan (requires --path)");
|
||||
supportedArguments.Add("-f, --protect-file Output protection to separate file (requires --scan)");
|
||||
supportedArguments.Add("-j, --json Enable submission JSON output");
|
||||
supportedArguments.Add("-z, --zip Enable log file compression");
|
||||
|
||||
return supportedArguments;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Configuration
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from the application configuration
|
||||
/// </summary>
|
||||
public static Options LoadFromConfig()
|
||||
{
|
||||
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||||
|
||||
var settings = configFile.AppSettings.Settings;
|
||||
var dict = new Dictionary<string, string>();
|
||||
|
||||
foreach (string key in settings.AllKeys)
|
||||
{
|
||||
dict[key] = settings[key]?.Value ?? string.Empty;
|
||||
}
|
||||
|
||||
return new Options(dict);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current set of options to the application configuration
|
||||
/// </summary>
|
||||
public static void SaveToConfig(Options options)
|
||||
{
|
||||
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||||
|
||||
// Loop through all settings in Options and save them, overwriting existing settings
|
||||
foreach (var kvp in options)
|
||||
{
|
||||
configFile.AppSettings.Settings.Remove(kvp.Key);
|
||||
configFile.AppSettings.Settings.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
configFile.Save(ConfigurationSaveMode.Modified);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using MPF.Core.Data;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
@@ -162,8 +161,15 @@ namespace MPF.Core.Utilities
|
||||
/// </summary>
|
||||
public static string GetCurrentVersion()
|
||||
{
|
||||
var assemblyVersion = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
|
||||
return assemblyVersion.InformationalVersion;
|
||||
try
|
||||
{
|
||||
var assemblyVersion = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
|
||||
return assemblyVersion.InformationalVersion;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return ex.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -171,7 +177,8 @@ namespace MPF.Core.Utilities
|
||||
/// </summary>
|
||||
private static (string tag, string url) GetRemoteVersionAndUrl()
|
||||
{
|
||||
using (WebClient wc = new WebClient())
|
||||
#if NETFRAMEWORK
|
||||
using (System.Net.WebClient wc = new System.Net.WebClient())
|
||||
{
|
||||
wc.Headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0";
|
||||
|
||||
@@ -184,6 +191,21 @@ namespace MPF.Core.Utilities
|
||||
|
||||
return (latestTag, releaseUrl);
|
||||
}
|
||||
#else
|
||||
using (System.Net.Http.HttpClient hc = new System.Net.Http.HttpClient())
|
||||
{
|
||||
// TODO: Figure out a better way than having this hardcoded...
|
||||
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
|
||||
var message = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
|
||||
message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
|
||||
string latestReleaseJsonString = hc.Send(message)?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
var latestReleaseJson = JObject.Parse(latestReleaseJsonString);
|
||||
string latestTag = latestReleaseJson["tag_name"].ToString();
|
||||
string releaseUrl = latestReleaseJson["html_url"].ToString();
|
||||
|
||||
return (latestTag, releaseUrl);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,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,30 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.3</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="Unclassified.NetRevisionTask" Version="0.4.1">
|
||||
<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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using BurnOutSharp;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.Modules;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Library
|
||||
{
|
||||
@@ -19,14 +19,9 @@ namespace MPF.Library
|
||||
#region Output paths
|
||||
|
||||
/// <summary>
|
||||
/// Base output directory to write files to
|
||||
/// Base output file path to write files to
|
||||
/// </summary>
|
||||
public string OutputDirectory { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Base output filename for output
|
||||
/// </summary>
|
||||
public string OutputFilename { get; private set; }
|
||||
public string OutputPath { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -47,10 +42,15 @@ namespace MPF.Library
|
||||
/// </summary>
|
||||
public MediaType? Type { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected dumping program
|
||||
/// </summary>
|
||||
public InternalProgram InternalProgram { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Options object representing user-defined options
|
||||
/// </summary>
|
||||
public Options Options { get; private set; }
|
||||
public Core.Data.Options Options { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parameters object representing what to send to the internal program
|
||||
@@ -87,30 +87,31 @@ namespace MPF.Library
|
||||
/// Constructor for a full DumpEnvironment object from user information
|
||||
/// </summary>
|
||||
/// <param name="options"></param>
|
||||
/// <param name="outputDirectory"></param>
|
||||
/// <param name="outputFilename"></param>
|
||||
/// <param name="outputPath"></param>
|
||||
/// <param name="drive"></param>
|
||||
/// <param name="system"></param>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="internalProgram"></param>
|
||||
/// <param name="parameters"></param>
|
||||
public DumpEnvironment(Options options,
|
||||
string outputDirectory,
|
||||
string outputFilename,
|
||||
public DumpEnvironment(Core.Data.Options options,
|
||||
string outputPath,
|
||||
Drive drive,
|
||||
RedumpSystem? system,
|
||||
MediaType? type,
|
||||
InternalProgram? internalProgram,
|
||||
string parameters)
|
||||
{
|
||||
// Set options object
|
||||
this.Options = options;
|
||||
|
||||
// Output paths
|
||||
(this.OutputDirectory, this.OutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename);
|
||||
this.OutputPath = InfoTool.NormalizeOutputPaths(outputPath, true);
|
||||
|
||||
// UI information
|
||||
this.Drive = drive;
|
||||
this.System = system ?? options.DefaultSystem;
|
||||
this.Type = type ?? MediaType.NONE;
|
||||
this.InternalProgram = internalProgram ?? options.InternalProgram;
|
||||
|
||||
// Dumping program
|
||||
SetParameters(parameters);
|
||||
@@ -127,17 +128,32 @@ namespace MPF.Library
|
||||
if (this.Parameters.InternalProgram != InternalProgram.DiscImageCreator)
|
||||
return;
|
||||
|
||||
// Replace all instances in the output directory
|
||||
this.OutputDirectory = this.OutputDirectory.Replace('.', '_');
|
||||
try
|
||||
{
|
||||
// Normalize the output path
|
||||
string outputPath = InfoTool.NormalizeOutputPaths(this.OutputPath, true);
|
||||
|
||||
// Currently, only periods in directories matter
|
||||
// Leave the following code commented in case filename handling breaks again
|
||||
// Replace all instances in the output directory
|
||||
string outputDirectory = Path.GetDirectoryName(outputPath);
|
||||
outputDirectory = outputDirectory.Replace(".", "_");
|
||||
|
||||
// Replace all instances in the output filename, except the extension
|
||||
//string tempFilename = Path.GetFileNameWithoutExtension(this.OutputFilename)
|
||||
// .Replace('.', '_');
|
||||
//string tempExtension = Path.GetExtension(this.OutputFilename)?.TrimStart('.');
|
||||
//this.OutputFilename = $"{tempFilename}.{tempExtension}";
|
||||
// Replace all instances in the output filename
|
||||
string outputFilename = Path.GetFileNameWithoutExtension(outputPath);
|
||||
outputFilename = outputFilename.Replace(".", "_");
|
||||
|
||||
// Get the extension for recreating the path
|
||||
string outputExtension = Path.GetExtension(outputPath).TrimStart('.');
|
||||
|
||||
// Rebuild the output path
|
||||
if (!string.IsNullOrWhiteSpace(outputExtension))
|
||||
this.OutputPath = Path.Combine(outputDirectory, $"{outputFilename}.{outputExtension}");
|
||||
else
|
||||
this.OutputPath = Path.Combine(outputDirectory, outputFilename);
|
||||
|
||||
// Assign the path to the filename as well for dumping
|
||||
((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = this.OutputPath;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -146,21 +162,21 @@ namespace MPF.Library
|
||||
/// <param name="parameters">String representation of the parameters</param>
|
||||
public void SetParameters(string parameters)
|
||||
{
|
||||
switch (Options.InternalProgram)
|
||||
switch (this.InternalProgram)
|
||||
{
|
||||
// Dumping support
|
||||
case InternalProgram.Aaru:
|
||||
this.Parameters = new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath };
|
||||
break;
|
||||
|
||||
case InternalProgram.DD:
|
||||
this.Parameters = new Modules.DD.Parameters(parameters) { ExecutablePath = Options.DDPath };
|
||||
break;
|
||||
|
||||
case InternalProgram.DiscImageCreator:
|
||||
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
|
||||
break;
|
||||
|
||||
case InternalProgram.Redumper:
|
||||
this.Parameters = new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath };
|
||||
break;
|
||||
|
||||
// Verification support only
|
||||
case InternalProgram.CleanRip:
|
||||
this.Parameters = new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null };
|
||||
@@ -200,24 +216,23 @@ namespace MPF.Library
|
||||
return null;
|
||||
|
||||
// Set the proper parameters
|
||||
string filename = OutputDirectory + Path.DirectorySeparatorChar + OutputFilename;
|
||||
switch (Options.InternalProgram)
|
||||
switch (this.InternalProgram)
|
||||
{
|
||||
case InternalProgram.Aaru:
|
||||
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
case InternalProgram.DD:
|
||||
Parameters = new Modules.DD.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
case InternalProgram.DiscImageCreator:
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
case InternalProgram.Redumper:
|
||||
Parameters = new Modules.Redumper.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
default:
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, filename, driveSpeed, Options);
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -268,10 +283,10 @@ namespace MPF.Library
|
||||
}
|
||||
|
||||
// Execute internal tool
|
||||
progress?.Report(Result.Success($"Executing {Options.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
|
||||
Directory.CreateDirectory(OutputDirectory);
|
||||
progress?.Report(Result.Success($"Executing {this.InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));
|
||||
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
|
||||
progress?.Report(Result.Success($"{Options.InternalProgram} has finished!"));
|
||||
progress?.Report(Result.Success($"{this.InternalProgram} has finished!"));
|
||||
|
||||
// Execute additional tools
|
||||
progress?.Report(Result.Success("Running any additional tools... see log for output!"));
|
||||
@@ -302,8 +317,12 @@ namespace MPF.Library
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Gathering submission information... please wait!"));
|
||||
|
||||
// Get the output directory and filename separately
|
||||
string outputDirectory = Path.GetDirectoryName(this.OutputPath);
|
||||
string outputFilename = Path.GetFileName(this.OutputPath);
|
||||
|
||||
// Check to make sure that the output had all the correct files
|
||||
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(this.OutputDirectory, this.OutputFilename, this.Parameters, false);
|
||||
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, this.Parameters, false);
|
||||
if (!foundFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
|
||||
@@ -313,8 +332,7 @@ namespace MPF.Library
|
||||
// Extract the information from the output files
|
||||
resultProgress?.Report(Result.Success("Extracting output information from output files..."));
|
||||
SubmissionInfo submissionInfo = await InfoTool.ExtractOutputInformation(
|
||||
this.OutputDirectory,
|
||||
this.OutputFilename,
|
||||
this.OutputPath,
|
||||
this.Drive,
|
||||
this.System,
|
||||
this.Type,
|
||||
@@ -332,7 +350,7 @@ namespace MPF.Library
|
||||
}
|
||||
|
||||
// Reset the drive automatically if configured to
|
||||
if (Options.InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
|
||||
if (this.InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Resetting drive {Drive.Letter}"));
|
||||
await ResetDrive();
|
||||
@@ -359,37 +377,51 @@ namespace MPF.Library
|
||||
|
||||
// Format the information for the text output
|
||||
resultProgress?.Report(Result.Success("Formatting information..."));
|
||||
List<string> formattedValues = InfoTool.FormatOutputData(submissionInfo);
|
||||
resultProgress?.Report(Result.Success("Formatting complete!"));
|
||||
(List<string> formattedValues, string formatResult) = InfoTool.FormatOutputData(submissionInfo, this.Options);
|
||||
if (formattedValues == null)
|
||||
resultProgress?.Report(Result.Success(formatResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(formatResult));
|
||||
|
||||
// Write the text output
|
||||
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
|
||||
bool success = InfoTool.WriteOutputData(this.OutputDirectory, formattedValues);
|
||||
if (success)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, formattedValues);
|
||||
if (txtSuccess)
|
||||
resultProgress?.Report(Result.Success(txtResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Writing could not complete!"));
|
||||
resultProgress?.Report(Result.Failure(txtResult));
|
||||
|
||||
// Write the JSON output, if required
|
||||
if (Options.OutputSubmissionJSON)
|
||||
// Write the copy protection output
|
||||
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.json.gz..."));
|
||||
success = InfoTool.WriteOutputData(this.OutputDirectory, submissionInfo);
|
||||
if (success)
|
||||
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, submissionInfo);
|
||||
if (scanSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Writing could not complete!"));
|
||||
}
|
||||
|
||||
// Conpress the logs, if required
|
||||
// Write the JSON output, if required
|
||||
if (Options.OutputSubmissionJSON)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Writing information to !submissionInfo.json{(Options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
|
||||
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, submissionInfo, Options.IncludeArtifacts);
|
||||
if (jsonSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Writing could not complete!"));
|
||||
}
|
||||
|
||||
// Compress the logs, if required
|
||||
if (Options.CompressLogFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Compressing log files..."));
|
||||
success = InfoTool.CompressLogFiles(this.OutputDirectory, this.OutputFilename, this.Parameters);
|
||||
if (success)
|
||||
resultProgress?.Report(Result.Success("Compression complete!"));
|
||||
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, outputFilename, this.Parameters);
|
||||
if (compressSuccess)
|
||||
resultProgress?.Report(Result.Success(compressResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Compression could not complete!"));
|
||||
resultProgress?.Report(Result.Failure(compressResult));
|
||||
}
|
||||
|
||||
resultProgress?.Report(Result.Success("Submission information process complete!"));
|
||||
@@ -466,12 +498,11 @@ namespace MPF.Library
|
||||
return Result.Failure("Error! Current configuration is not supported!");
|
||||
|
||||
// Fix the output paths, just in case
|
||||
(OutputDirectory, OutputFilename) = InfoTool.NormalizeOutputPaths(OutputDirectory, OutputFilename);
|
||||
this.OutputPath = InfoTool.NormalizeOutputPaths(this.OutputPath, true);
|
||||
|
||||
// Validate that the output path isn't on the dumping drive
|
||||
string fullOutputPath = Path.GetFullPath(Path.Combine(OutputDirectory, OutputFilename));
|
||||
if (fullOutputPath[0] == Drive.Letter)
|
||||
return Result.Failure($"Error! Cannot output to same drive that is being dumped!");
|
||||
if (this.OutputPath[0] == Drive.Letter)
|
||||
return Result.Failure("Error! Cannot output to same drive that is being dumped!");
|
||||
|
||||
// Validate that the required program exists
|
||||
if (!File.Exists(Parameters.ExecutablePath))
|
||||
@@ -480,7 +511,7 @@ namespace MPF.Library
|
||||
// Validate that the dumping drive doesn't contain the executable
|
||||
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath);
|
||||
if (fullExecutablePath[0] == Drive.Letter)
|
||||
return Result.Failure("$Error! Cannot dump same drive that executable resides on!");
|
||||
return Result.Failure("Error! Cannot dump same drive that executable resides on!");
|
||||
|
||||
// Validate that the current configuration is supported
|
||||
return Tools.GetSupportStatus(System, Type);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<AssemblyName>MPF.Library</AssemblyName>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.3</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<Version>2.6.5</Version>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@@ -21,23 +19,21 @@
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<DefineConstants>NET_FRAMEWORK</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.0" GeneratePathProperty="true">
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -4,58 +4,84 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using BinaryObjectScanner.Protection;
|
||||
using BurnOutSharp;
|
||||
using BurnOutSharp.External.psxt001z;
|
||||
using BurnOutSharp.ProtectionType;
|
||||
using MPF.Core.Data;
|
||||
using psxt001z;
|
||||
|
||||
namespace MPF.Library
|
||||
{
|
||||
public static class Protection
|
||||
{
|
||||
/// <summary>
|
||||
/// Run protection scan on a given dump environment
|
||||
/// Run protection scan on a given path
|
||||
/// </summary>
|
||||
/// <param name="path">Path to scan for protection</param>
|
||||
/// <param name="options">Options object that determines what to scan</param>
|
||||
/// <param name="progress">Optional progress callback</param>
|
||||
/// <returns>TCopy protection detected in the envirionment, if any</returns>
|
||||
public static async Task<(bool, string)> RunProtectionScanOnPath(string path, Options options, IProgress<ProtectionProgress> progress = null)
|
||||
/// <returns>Set of all detected copy protections with an optional error string</returns>
|
||||
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Core.Data.Options options, IProgress<ProtectionProgress> progress = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var found = await Task.Run(() =>
|
||||
{
|
||||
var scanner = new Scanner(progress)
|
||||
{
|
||||
IncludeDebug = options.IncludeDebugProtectionInformation,
|
||||
ScanAllFiles = options.ForceScanningForProtection,
|
||||
ScanArchives = options.ScanArchivesForProtection,
|
||||
ScanPackers = options.ScanPackersForProtection,
|
||||
};
|
||||
var scanner = new Scanner(
|
||||
options.ScanArchivesForProtection,
|
||||
scanContents: true, // Hardcoded value to avoid issues
|
||||
scanGameEngines: false, // Hardcoded value to avoid issues
|
||||
options.ScanPackersForProtection,
|
||||
scanPaths: true, // Hardcoded value to avoid issues
|
||||
options.IncludeDebugProtectionInformation,
|
||||
progress);
|
||||
|
||||
return scanner.GetProtections(path);
|
||||
});
|
||||
|
||||
if (found == null || found.Count() == 0)
|
||||
return (true, "None found");
|
||||
// If nothing was returned, return
|
||||
if (found == null || !found.Any())
|
||||
return (null, null);
|
||||
|
||||
// Get an ordered list of distinct found protections
|
||||
var orderedDistinctProtections = found
|
||||
// Filter out any empty protections
|
||||
var filteredProtections = found
|
||||
.Where(kvp => kvp.Value != null && kvp.Value.Any())
|
||||
.SelectMany(kvp => kvp.Value)
|
||||
.Distinct()
|
||||
.OrderBy(p => p);
|
||||
.ToDictionary(
|
||||
kvp => kvp.Key,
|
||||
kvp => kvp.Value.OrderBy(s => s).ToList());
|
||||
|
||||
// Sanitize and join protections for writing
|
||||
string protections = SanitizeFoundProtections(orderedDistinctProtections);
|
||||
return (true, protections);
|
||||
// Return the filtered set of protections
|
||||
return (filteredProtections, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, ex.ToString());
|
||||
return (null, ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format found protections to a deduplicated, ordered string
|
||||
/// </summary>
|
||||
/// <param name="protections">Dictionary of file to list of protection mappings</param>
|
||||
/// <returns>Detected protections, if any</returns>
|
||||
public static string FormatProtections(Dictionary<string, List<string>> protections)
|
||||
{
|
||||
// If the filtered list is empty in some way, return
|
||||
if (protections == null || !protections.Any())
|
||||
return "None found";
|
||||
|
||||
// Get an ordered list of distinct found protections
|
||||
var orderedDistinctProtections = protections
|
||||
.SelectMany(kvp => kvp.Value)
|
||||
.Distinct()
|
||||
.OrderBy(p => p);
|
||||
|
||||
// Sanitize and join protections for writing
|
||||
string protectionString = SanitizeFoundProtections(orderedDistinctProtections);
|
||||
if (string.IsNullOrWhiteSpace(protectionString))
|
||||
return "None found";
|
||||
|
||||
return protectionString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the existence of an anti-modchip string from a PlayStation disc, if possible
|
||||
/// </summary>
|
||||
@@ -73,7 +99,7 @@ namespace MPF.Library
|
||||
try
|
||||
{
|
||||
byte[] fileContent = File.ReadAllBytes(file);
|
||||
string protection = antiModchip.CheckContents(file, fileContent, false, null, null);
|
||||
string protection = antiModchip.CheckContents(file, fileContent, false);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
return true;
|
||||
}
|
||||
@@ -177,80 +203,68 @@ namespace MPF.Library
|
||||
// SafeCast
|
||||
// TODO: Figure this one out
|
||||
|
||||
// Cactus Data Shield / SafeDisc
|
||||
if (foundProtections.Any(p => p == "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)"))
|
||||
{
|
||||
foundProtections = foundProtections
|
||||
.Where(p => p != "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)");
|
||||
|
||||
if (foundProtections.Any(p => !p.StartsWith("SafeDisc")))
|
||||
foundProtections = foundProtections.Append("Cactus Data Shield 300");
|
||||
}
|
||||
|
||||
// SafeDisc
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.Aaru
|
||||
{
|
||||
|
||||
@@ -7,9 +7,10 @@ using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml.Serialization;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.CueSheets;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.Models.CueSheets;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Schemas;
|
||||
|
||||
namespace MPF.Modules.Aaru
|
||||
@@ -191,22 +192,53 @@ namespace MPF.Modules.Aaru
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
// TODO: Fill in submission info specifics for Aaru
|
||||
string outputDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
// TODO: Determine if there's an Aaru version anywhere
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + ".cicm.xml")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// Deserialize the sidecar, if possible
|
||||
var sidecar = GenerateSidecar(basePath + ".cicm.xml");
|
||||
|
||||
// Fill in the hardware data
|
||||
if (GetHardwareInfo(sidecar, out string manufacturer, out string model, out string firmware))
|
||||
{
|
||||
info.DumpingInfo.Manufacturer = manufacturer;
|
||||
info.DumpingInfo.Model = model;
|
||||
info.DumpingInfo.Firmware = firmware;
|
||||
}
|
||||
|
||||
// Fill in the disc type data
|
||||
if (GetDiscType(sidecar, out string discType, out string discSubType))
|
||||
{
|
||||
string fullDiscType = string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
|
||||
fullDiscType = $"{discType} ({discSubType})";
|
||||
else if (!string.IsNullOrWhiteSpace(discType) && string.IsNullOrWhiteSpace(discSubType))
|
||||
fullDiscType = discType;
|
||||
else if (string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
|
||||
fullDiscType = discSubType;
|
||||
|
||||
info.DumpingInfo.ReportedDiscType = fullDiscType;
|
||||
}
|
||||
|
||||
// Get the Datafile information
|
||||
Datafile datafile = GenerateDatafile(sidecar, basePath);
|
||||
|
||||
// Fill in the hash data
|
||||
info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(sidecar, basePath);
|
||||
info.TracksAndWriteOffsets.ClrMameProData = GenerateDatfile(datafile);
|
||||
|
||||
switch (this.Type)
|
||||
{
|
||||
// TODO: Can this do GD-ROM?
|
||||
case MediaType.CDROM:
|
||||
// TODO: Can this do GD-ROM?
|
||||
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
// TODO: Re-enable once PVD generation / finding is fixed
|
||||
// Generate / obtain the PVD
|
||||
//info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
|
||||
long errorCount = -1;
|
||||
if (File.Exists(basePath + ".resume.xml"))
|
||||
@@ -214,9 +246,9 @@ namespace MPF.Modules.Aaru
|
||||
|
||||
info.CommonDiscInfo.ErrorsCount = (errorCount == -1 ? "Error retrieving error count" : errorCount.ToString());
|
||||
|
||||
info.TracksAndWriteOffsets.Cuesheet = GenerateCuesheet(sidecar, basePath) ?? "";
|
||||
info.TracksAndWriteOffsets.Cuesheet = GenerateCuesheet(sidecar, basePath) ?? string.Empty;
|
||||
|
||||
string cdWriteOffset = GetWriteOffset(sidecar) ?? "";
|
||||
string cdWriteOffset = GetWriteOffset(sidecar) ?? string.Empty;
|
||||
info.CommonDiscInfo.RingWriteOffset = cdWriteOffset;
|
||||
info.TracksAndWriteOffsets.OtherWriteOffsets = cdWriteOffset;
|
||||
break;
|
||||
@@ -225,7 +257,7 @@ namespace MPF.Modules.Aaru
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
// Get the individual hash data, as per internal
|
||||
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out string crc32, out string md5, out string sha1))
|
||||
if (GetISOHashValues(datafile, out long size, out string crc32, out string md5, out string sha1))
|
||||
{
|
||||
info.SizeAndChecksums.Size = size;
|
||||
info.SizeAndChecksums.CRC32 = crc32;
|
||||
@@ -233,22 +265,25 @@ namespace MPF.Modules.Aaru
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
}
|
||||
|
||||
// TODO: Re-enable once PVD generation / finding is fixed
|
||||
// Generate / obtain the PVD
|
||||
//info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
|
||||
// Deal with the layerbreak
|
||||
string layerbreak = null;
|
||||
if (this.Type == MediaType.DVD)
|
||||
layerbreak = GetLayerbreak(sidecar) ?? "";
|
||||
layerbreak = GetLayerbreak(sidecar) ?? string.Empty;
|
||||
else if (this.Type == MediaType.BluRay)
|
||||
layerbreak = info.SizeAndChecksums.Size > 25_025_314_816 ? "25025314816" : null;
|
||||
|
||||
// If we have a single-layer disc
|
||||
if (string.IsNullOrWhiteSpace(layerbreak))
|
||||
{
|
||||
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
// Currently no-op
|
||||
}
|
||||
// If we have a dual-layer disc
|
||||
else
|
||||
{
|
||||
info.Extras.PVD = GeneratePVD(sidecar) ?? "Disc has no PVD";
|
||||
info.SizeAndChecksums.Layerbreak = Int64.Parse(layerbreak);
|
||||
}
|
||||
|
||||
@@ -269,7 +304,7 @@ namespace MPF.Modules.Aaru
|
||||
|
||||
case RedumpSystem.DVDAudio:
|
||||
case RedumpSystem.DVDVideo:
|
||||
info.CopyProtection.Protection = GetDVDProtection(sidecar) ?? "";
|
||||
info.CopyProtection.Protection = GetDVDProtection(sidecar) ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiPython2:
|
||||
@@ -281,7 +316,7 @@ namespace MPF.Modules.Aaru
|
||||
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXbox:
|
||||
@@ -291,13 +326,13 @@ namespace MPF.Modules.Aaru
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd1PFIHash;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd1SSHash;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd1SSVer;
|
||||
info.Extras.SecuritySectorRanges = ss ?? "";
|
||||
info.Extras.SecuritySectorRanges = ss ?? string.Empty;
|
||||
}
|
||||
|
||||
if (GetXboxDMIInfo(sidecar, out string serial, out string version, out Region? region))
|
||||
{
|
||||
info.CommonDiscInfo.Serial = serial ?? "";
|
||||
info.VersionAndEditions.Version = version ?? "";
|
||||
info.CommonDiscInfo.Serial = serial ?? string.Empty;
|
||||
info.VersionAndEditions.Version = version ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = region;
|
||||
}
|
||||
|
||||
@@ -310,13 +345,13 @@ namespace MPF.Modules.Aaru
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = xgd23PFIHash;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = xgd23SSHash;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = xgd23SSVer;
|
||||
info.Extras.SecuritySectorRanges = ss360 ?? "";
|
||||
info.Extras.SecuritySectorRanges = ss360 ?? string.Empty;
|
||||
}
|
||||
|
||||
if (GetXbox360DMIInfo(sidecar, out string serial360, out string version360, out Region? region360))
|
||||
{
|
||||
info.CommonDiscInfo.Serial = serial360 ?? "";
|
||||
info.VersionAndEditions.Version = version360 ?? "";
|
||||
info.CommonDiscInfo.Serial = serial360 ?? string.Empty;
|
||||
info.VersionAndEditions.Version = version360 ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = region360;
|
||||
}
|
||||
break;
|
||||
@@ -341,15 +376,22 @@ namespace MPF.Modules.Aaru
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? "";
|
||||
info.VersionAndEditions.Version = GetPlayStation2Version(drive?.Letter) ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
info.VersionAndEditions.Version = GetPlayStation3Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation3Serial(drive?.Letter) ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? "";
|
||||
info.VersionAndEditions.Version = GetPlayStation4Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation4Serial(drive?.Letter) ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
|
||||
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = GetPlayStation5Serial(drive?.Letter) ?? string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1396,6 +1438,8 @@ namespace MPF.Modules.Aaru
|
||||
case MediaType.CDROM:
|
||||
if (File.Exists($"{basePath}.cicm.xml"))
|
||||
logFiles.Add($"{basePath}.cicm.xml");
|
||||
if (File.Exists($"{basePath}.error.log"))
|
||||
logFiles.Add($"{basePath}.error.log");
|
||||
if (File.Exists($"{basePath}.ibg"))
|
||||
logFiles.Add($"{basePath}.ibg");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
@@ -1414,6 +1458,8 @@ namespace MPF.Modules.Aaru
|
||||
case MediaType.BluRay:
|
||||
if (File.Exists($"{basePath}.cicm.xml"))
|
||||
logFiles.Add($"{basePath}.cicm.xml");
|
||||
if (File.Exists($"{basePath}.error.log"))
|
||||
logFiles.Add($"{basePath}.error.log");
|
||||
if (File.Exists($"{basePath}.ibg"))
|
||||
logFiles.Add($"{basePath}.ibg");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
@@ -1782,7 +1828,7 @@ namespace MPF.Modules.Aaru
|
||||
|
||||
// Speed
|
||||
byteValue = ProcessInt8Parameter(parts, null, FlagStrings.SpeedLong, ref i);
|
||||
if (byteValue == null && byteValue != SByte.MinValue)
|
||||
if (byteValue != null && byteValue != SByte.MinValue)
|
||||
SpeedValue = byteValue;
|
||||
|
||||
#endregion
|
||||
@@ -2395,10 +2441,10 @@ namespace MPF.Modules.Aaru
|
||||
|
||||
// Required variables
|
||||
uint totalTracks = 0;
|
||||
CueSheet cueSheet = new CueSheet
|
||||
var cueFiles = new List<CueFile>();
|
||||
var cueSheet = new CueSheet
|
||||
{
|
||||
Performer = string.Join(", ", cicmSidecar.Performer ?? new string[0]),
|
||||
Files = new List<CueFile>(),
|
||||
};
|
||||
|
||||
// Only care about OpticalDisc types
|
||||
@@ -2436,13 +2482,13 @@ namespace MPF.Modules.Aaru
|
||||
{
|
||||
FileName = GenerateTrackName(basePath, (int)totalTracks, cueTrack.Number, opticalDisc.DiscType),
|
||||
FileType = CueFileType.BINARY,
|
||||
Tracks = new List<CueTrack>(),
|
||||
};
|
||||
|
||||
// Add index data
|
||||
var cueTracks = new List<CueTrack>();
|
||||
if (track.Indexes != null && track.Indexes.Length > 0)
|
||||
{
|
||||
cueTrack.Indices = new List<CueIndex>();
|
||||
var cueIndicies = new List<CueIndex>();
|
||||
|
||||
// Loop through each index
|
||||
foreach (TrackIndexType trackIndex in track.Indexes)
|
||||
@@ -2456,36 +2502,60 @@ namespace MPF.Modules.Aaru
|
||||
|
||||
// Pregap information
|
||||
if (trackIndex.Value < 0)
|
||||
cueTrack.PreGap = new PreGap(timeString);
|
||||
{
|
||||
string[] timeStringSplit = timeString.Split(':');
|
||||
cueTrack.PreGap = new PreGap
|
||||
{
|
||||
Minutes = int.Parse(timeStringSplit[0]),
|
||||
Seconds = int.Parse(timeStringSplit[1]),
|
||||
Frames = int.Parse(timeStringSplit[2]),
|
||||
};
|
||||
}
|
||||
|
||||
// Individual indexes
|
||||
else
|
||||
cueTrack.Indices.Add(new CueIndex(trackIndex.index.ToString(), timeString));
|
||||
{
|
||||
string[] timeStringSplit = timeString.Split(':');
|
||||
cueIndicies.Add(new CueIndex
|
||||
{
|
||||
Index = trackIndex.index,
|
||||
Minutes = int.Parse(timeStringSplit[0]),
|
||||
Seconds = int.Parse(timeStringSplit[1]),
|
||||
Frames = int.Parse(timeStringSplit[2]),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
cueTrack.Indices = cueIndicies.ToArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default if index data missing from sidecar
|
||||
cueTrack.Indices = new List<CueIndex>()
|
||||
cueTrack.Indices = new CueIndex[]
|
||||
{
|
||||
new CueIndex("01", "00:00:00"),
|
||||
new CueIndex
|
||||
{
|
||||
Index = 1,
|
||||
Minutes = 0,
|
||||
Seconds = 0,
|
||||
Frames = 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Add the track to the file
|
||||
cueFile.Tracks.Add(cueTrack);
|
||||
cueTracks.Add(cueTrack);
|
||||
|
||||
// Add the file to the cuesheet
|
||||
cueSheet.Files.Add(cueFile);
|
||||
cueFiles.Add(cueFile);
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a cuesheet to write out, do so
|
||||
cueSheet.Files = cueFiles.ToArray();
|
||||
if (cueSheet != null && cueSheet != default)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
cueSheet.Write(ms);
|
||||
ms.Position = 0;
|
||||
var ms = new SabreTools.Serialization.Streams.CueSheet().Serialize(cueSheet);
|
||||
using (var sr = new StreamReader(ms))
|
||||
{
|
||||
return sr.ReadToEnd();
|
||||
@@ -2603,6 +2673,119 @@ namespace MPF.Modules.Aaru
|
||||
return datfile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a CMP XML datfile string based on CICM sidecar data
|
||||
/// </summary>
|
||||
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
|
||||
/// <param name="basePath">Base path for determining file names</param>
|
||||
/// <returns>Datafile containing the hash information, null on error</returns>
|
||||
private static Datafile GenerateDatafile(CICMMetadataType cicmSidecar, string basePath)
|
||||
{
|
||||
// If the object is null, we can't get information from it
|
||||
if (cicmSidecar == null)
|
||||
return null;
|
||||
|
||||
// Required variables
|
||||
Datafile datafile = new Datafile();
|
||||
List<Rom> roms = new List<Rom>();
|
||||
|
||||
// Process OpticalDisc, if possible
|
||||
if (cicmSidecar.OpticalDisc != null && cicmSidecar.OpticalDisc.Length > 0)
|
||||
{
|
||||
// Loop through each OpticalDisc in the metadata
|
||||
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
|
||||
{
|
||||
// Only capture the first total track count
|
||||
uint totalTracks = 0;
|
||||
if (opticalDisc.Tracks != null && opticalDisc.Tracks.Length > 0)
|
||||
totalTracks = opticalDisc.Tracks[0];
|
||||
|
||||
// If there are no tracks, we can't get a datfile
|
||||
if (opticalDisc.Track == null || opticalDisc.Track.Length == 0)
|
||||
continue;
|
||||
|
||||
// Loop through each track
|
||||
foreach (TrackType track in opticalDisc.Track)
|
||||
{
|
||||
uint trackNumber = track.Sequence?.TrackNumber ?? 0;
|
||||
ulong size = track.Size;
|
||||
string crc32 = string.Empty;
|
||||
string md5 = string.Empty;
|
||||
string sha1 = string.Empty;
|
||||
|
||||
// If we don't have any checksums, we can't get a DAT for this track
|
||||
if (track.Checksums == null || track.Checksums.Length == 0)
|
||||
continue;
|
||||
|
||||
// Extract only relevant checksums
|
||||
foreach (ChecksumType checksum in track.Checksums)
|
||||
{
|
||||
switch (checksum.type)
|
||||
{
|
||||
case ChecksumTypeType.crc32:
|
||||
crc32 = checksum.Value;
|
||||
break;
|
||||
case ChecksumTypeType.md5:
|
||||
md5 = checksum.Value;
|
||||
break;
|
||||
case ChecksumTypeType.sha1:
|
||||
sha1 = checksum.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build the track datfile data and append
|
||||
string trackName = GenerateTrackName(basePath, (int)totalTracks, (int)trackNumber, opticalDisc.DiscType);
|
||||
roms.Add(new Rom { Name = trackName, Size = size.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process BlockMedia, if possible
|
||||
if (cicmSidecar.BlockMedia != null && cicmSidecar.BlockMedia.Length > 0)
|
||||
{
|
||||
// Loop through each BlockMedia in the metadata
|
||||
foreach (BlockMediaType blockMedia in cicmSidecar.BlockMedia)
|
||||
{
|
||||
ulong size = blockMedia.Size;
|
||||
string crc32 = string.Empty;
|
||||
string md5 = string.Empty;
|
||||
string sha1 = string.Empty;
|
||||
|
||||
// If we don't have any checksums, we can't get a DAT for this track
|
||||
if (blockMedia.Checksums == null || blockMedia.Checksums.Length == 0)
|
||||
continue;
|
||||
|
||||
// Extract only relevant checksums
|
||||
foreach (ChecksumType checksum in blockMedia.Checksums)
|
||||
{
|
||||
switch (checksum.type)
|
||||
{
|
||||
case ChecksumTypeType.crc32:
|
||||
crc32 = checksum.Value;
|
||||
break;
|
||||
case ChecksumTypeType.md5:
|
||||
md5 = checksum.Value;
|
||||
break;
|
||||
case ChecksumTypeType.sha1:
|
||||
sha1 = checksum.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Build the track datfile data and append
|
||||
string trackName = $"{basePath}.bin";
|
||||
roms.Add(new Rom { Name = trackName, Size = size.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1 });
|
||||
}
|
||||
}
|
||||
|
||||
// Assign the roms to a new game
|
||||
datafile.Games = new Game[1];
|
||||
datafile.Games[0] = new Game { Roms = roms.ToArray() };
|
||||
|
||||
return datafile;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a track name based on current path and tracks
|
||||
/// </summary>
|
||||
@@ -2858,6 +3041,37 @@ namespace MPF.Modules.Aaru
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get reported disc type information, if possible
|
||||
/// </summary>
|
||||
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
|
||||
/// <returns>True if disc type info was set, false otherwise</returns>
|
||||
private static bool GetDiscType(CICMMetadataType cicmSidecar, out string discType, out string discSubType)
|
||||
{
|
||||
// Set the default values
|
||||
discType = null; discSubType = null;
|
||||
|
||||
// If the object is null, we can't get information from it
|
||||
if (cicmSidecar == null)
|
||||
return false;
|
||||
|
||||
// Only care about OpticalDisc types
|
||||
if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0)
|
||||
return false;
|
||||
|
||||
// Find and return the hardware info, if possible
|
||||
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
|
||||
{
|
||||
// Store the first instance of each value
|
||||
if (string.IsNullOrEmpty(discType) && !string.IsNullOrEmpty(opticalDisc.DiscType))
|
||||
discType = opticalDisc.DiscType;
|
||||
if (string.IsNullOrEmpty(discSubType) && !string.IsNullOrEmpty(opticalDisc.DiscSubType))
|
||||
discSubType = opticalDisc.DiscSubType;
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(discType) || !string.IsNullOrEmpty(discSubType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the DVD protection information, if possible
|
||||
/// </summary>
|
||||
@@ -2951,6 +3165,50 @@ namespace MPF.Modules.Aaru
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hardware information, if possible
|
||||
/// </summary>
|
||||
/// <param name="cicmSidecar">CICM Sidecar data generated by Aaru</param>
|
||||
/// <returns>True if hardware info was set, false otherwise</returns>
|
||||
private static bool GetHardwareInfo(CICMMetadataType cicmSidecar, out string manufacturer, out string model, out string firmware)
|
||||
{
|
||||
// Set the default values
|
||||
manufacturer = null; model = null; firmware = null;
|
||||
|
||||
// If the object is null, we can't get information from it
|
||||
if (cicmSidecar == null)
|
||||
return false;
|
||||
|
||||
// Only care about OpticalDisc types
|
||||
if (cicmSidecar.OpticalDisc == null || cicmSidecar.OpticalDisc.Length == 0)
|
||||
return false;
|
||||
|
||||
// Find and return the hardware info, if possible
|
||||
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
|
||||
{
|
||||
// If there's no hardware information, skip
|
||||
if (opticalDisc.DumpHardwareArray == null || !opticalDisc.DumpHardwareArray.Any())
|
||||
continue;
|
||||
|
||||
foreach (DumpHardwareType hardware in opticalDisc.DumpHardwareArray)
|
||||
{
|
||||
// If the hardware information is invalid, skip
|
||||
if (hardware == null)
|
||||
continue;
|
||||
|
||||
// Store the first instance of each value
|
||||
if (string.IsNullOrEmpty(manufacturer) && !string.IsNullOrEmpty(hardware.Manufacturer))
|
||||
manufacturer = hardware.Manufacturer;
|
||||
if (string.IsNullOrEmpty(model) && !string.IsNullOrEmpty(hardware.Model))
|
||||
model = hardware.Model;
|
||||
if (string.IsNullOrEmpty(firmware) && !string.IsNullOrEmpty(hardware.Firmware))
|
||||
firmware = hardware.Firmware;
|
||||
}
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(manufacturer) || !string.IsNullOrEmpty(model) || !string.IsNullOrEmpty(firmware);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the layerbreak from the input file, if possible
|
||||
/// </summary>
|
||||
|
||||
7391
MPF.Modules/Aaru/cicm.cs
Normal file
7391
MPF.Modules/Aaru/cicm.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
@@ -6,10 +6,14 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Schema;
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Hashing;
|
||||
using MPF.Core.Utilities;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.Models.PIC;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules
|
||||
{
|
||||
@@ -153,10 +157,11 @@ namespace MPF.Modules
|
||||
/// Generate a SubmissionInfo for the output files
|
||||
/// </summary>
|
||||
/// <param name="submissionInfo">Base submission info to fill in specifics for</param>
|
||||
/// <param name="options">Options object representing user-defined options</param>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <param name="drive">Drive representing the disc to get information from</param>
|
||||
/// <param name="includeArtifacts">True to include output files as encoded artifacts, false otherwise</param>
|
||||
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, string basePath, Drive drive, bool includeArtifacts);
|
||||
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, Options options, string basePath, Drive drive, bool includeArtifacts);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -241,7 +246,7 @@ namespace MPF.Modules
|
||||
/// </summary>
|
||||
/// <param name="parameters">String possibly representing parameters</param>
|
||||
/// <returns>True if the parameters were set correctly, false otherwise</returns>
|
||||
protected virtual bool ValidateAndSetParameters(string parameters) => true;
|
||||
protected virtual bool ValidateAndSetParameters(string parameters) => !string.IsNullOrWhiteSpace(parameters);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -343,7 +348,7 @@ namespace MPF.Modules
|
||||
return BitConverter.ToString(bytes).Replace("-", string.Empty);
|
||||
}
|
||||
|
||||
return string.Join("\n", File.ReadAllLines(filename));
|
||||
return File.ReadAllText(filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -621,6 +626,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (sbyte)(sbyte.Parse(value) * factor);
|
||||
}
|
||||
@@ -697,6 +703,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (short)(short.Parse(value) * factor);
|
||||
}
|
||||
@@ -773,6 +780,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (int)(int.Parse(value) * factor);
|
||||
}
|
||||
@@ -849,6 +857,7 @@ namespace MPF.Modules
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return long.Parse(value) * factor;
|
||||
}
|
||||
@@ -931,6 +940,84 @@ namespace MPF.Modules
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a byte parameter
|
||||
/// </summary>
|
||||
/// <param name="parts">List of parts to be referenced</param>
|
||||
/// <param name="flagString">Flag string, if available</param>
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>Byte value if success, Byte.MinValue if skipped, null on error/returns>
|
||||
protected byte? ProcessUInt8Parameter(List<string> parts, string flagString, ref int i, bool missingAllowed = false)
|
||||
=> ProcessUInt8Parameter(parts, null, flagString, ref i, missingAllowed);
|
||||
|
||||
/// <summary>
|
||||
/// Process a byte parameter
|
||||
/// </summary>
|
||||
/// <param name="parts">List of parts to be referenced</param>
|
||||
/// <param name="shortFlagString">Short flag string, if available</param>
|
||||
/// <param name="longFlagString">Long flag string, if available</param>
|
||||
/// <param name="i">Reference to the position in the parts</param>
|
||||
/// <param name="missingAllowed">True if missing values are allowed, false otherwise</param>
|
||||
/// <returns>Byte value if success, Byte.MinValue if skipped, null on error/returns>
|
||||
protected byte? ProcessUInt8Parameter(List<string> parts, string shortFlagString, string longFlagString, ref int i, bool missingAllowed = false)
|
||||
{
|
||||
if (parts == null)
|
||||
return null;
|
||||
|
||||
if (parts[i] == shortFlagString || parts[i] == longFlagString)
|
||||
{
|
||||
if (!IsFlagSupported(longFlagString))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (!DoesExist(parts, i + 1))
|
||||
{
|
||||
if (missingAllowed)
|
||||
this[longFlagString] = true;
|
||||
|
||||
return null;
|
||||
}
|
||||
else if (IsFlagSupported(parts[i + 1]))
|
||||
{
|
||||
if (missingAllowed)
|
||||
this[longFlagString] = true;
|
||||
|
||||
return null;
|
||||
}
|
||||
else if (!IsValidInt8(parts[i + 1]))
|
||||
{
|
||||
if (missingAllowed)
|
||||
this[longFlagString] = true;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
this[longFlagString] = true;
|
||||
i++;
|
||||
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return (byte)(byte.Parse(value) * factor);
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
if (!IsFlagSupported(longFlagString))
|
||||
return null;
|
||||
|
||||
string[] commandParts = parts[i].Split('=');
|
||||
if (commandParts.Length != 2)
|
||||
return null;
|
||||
|
||||
string valuePart = commandParts[1];
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (byte)(byte.Parse(value) * factor);
|
||||
}
|
||||
|
||||
return Byte.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get yhe trimmed value and multiplication factor from a value
|
||||
/// </summary>
|
||||
@@ -997,6 +1084,102 @@ namespace MPF.Modules
|
||||
|
||||
#region Common Information Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Generate the proper datfile from the input Datafile, if possible
|
||||
/// </summary>
|
||||
/// <param name="datafile">.dat file location</param>
|
||||
/// <returns>Relevant pieces of the datfile, null on error</returns>
|
||||
protected static string GenerateDatfile(Datafile datafile)
|
||||
{
|
||||
// If we don't have a valid datafile, we can't do anything
|
||||
if (datafile?.Games == null || datafile.Games.Length == 0 || datafile.Games[0]?.Roms == null || datafile.Games[0].Roms.Length == 0)
|
||||
return null;
|
||||
|
||||
// Otherwise, reconstruct the hash data with only the required info
|
||||
try
|
||||
{
|
||||
var roms = datafile.Games[0].Roms;
|
||||
|
||||
string datString = string.Empty;
|
||||
for (int i = 0; i < roms.Length; i++)
|
||||
{
|
||||
var rom = roms[i];
|
||||
datString += $"<rom name=\"{rom.Name}\" size=\"{rom.Size}\" crc=\"{rom.Crc}\" md5=\"{rom.Md5}\" sha1=\"{rom.Sha1}\" />\n";
|
||||
}
|
||||
|
||||
datString.TrimEnd('\n');
|
||||
return datString;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get Datafile from a standard DAT
|
||||
/// </summary>
|
||||
/// <param name="dat">Path to the DAT file to parse</param>
|
||||
/// <returns>Filled Datafile on success, null on error</returns>
|
||||
protected static Datafile GetDatafile(string dat)
|
||||
{
|
||||
// If there's no path, we can't read the file
|
||||
if (string.IsNullOrWhiteSpace(dat))
|
||||
return null;
|
||||
|
||||
// If the file doesn't exist, we can't read it
|
||||
if (!File.Exists(dat))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Open and read in the XML file
|
||||
XmlReader xtr = XmlReader.Create(dat, new XmlReaderSettings
|
||||
{
|
||||
CheckCharacters = false,
|
||||
DtdProcessing = DtdProcessing.Ignore,
|
||||
IgnoreComments = true,
|
||||
IgnoreWhitespace = true,
|
||||
ValidationFlags = XmlSchemaValidationFlags.None,
|
||||
ValidationType = ValidationType.None,
|
||||
});
|
||||
|
||||
// If the reader is null for some reason, we can't do anything
|
||||
if (xtr == null)
|
||||
return null;
|
||||
|
||||
XmlSerializer serializer = new XmlSerializer(typeof(Datafile));
|
||||
Datafile obj = serializer.Deserialize(xtr) as Datafile;
|
||||
|
||||
return obj;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets disc information from a PIC file
|
||||
/// </summary>
|
||||
/// <param name="pic">Path to a PIC.bin file</param>
|
||||
/// <returns>Filled DiscInformation on success, null on error</returns>
|
||||
/// <remarks>This omits the emergency brake information, if it exists</remarks>
|
||||
protected static DiscInformation GetDiscInformation(string pic)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new SabreTools.Serialization.Files.PIC().Deserialize(pic);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input file path
|
||||
/// </summary>
|
||||
@@ -1104,6 +1287,22 @@ namespace MPF.Modules
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the last modified date from a file path, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <returns>Filled DateTime on success, null on failure</returns>
|
||||
protected static DateTime? GetFileModifiedDate(string filename, bool fallback = false)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(filename))
|
||||
return fallback ? (DateTime?)DateTime.UtcNow : null;
|
||||
else if (!File.Exists(filename))
|
||||
return fallback ? (DateTime?)DateTime.UtcNow : null;
|
||||
|
||||
var fi = new FileInfo(filename);
|
||||
return fi.LastWriteTimeUtc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the split values for ISO-based media
|
||||
/// </summary>
|
||||
@@ -1116,6 +1315,8 @@ namespace MPF.Modules
|
||||
if (string.IsNullOrWhiteSpace(hashData))
|
||||
return false;
|
||||
|
||||
// TODO: Use deserialization to Rom instead of Regex
|
||||
|
||||
Regex hashreg = new Regex(@"<rom name="".*?"" size=""(.*?)"" crc=""(.*?)"" md5=""(.*?)"" sha1=""(.*?)""");
|
||||
Match m = hashreg.Match(hashData);
|
||||
if (m.Success)
|
||||
@@ -1132,6 +1333,92 @@ namespace MPF.Modules
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the split values for ISO-based media
|
||||
/// </summary>
|
||||
/// <param name="datafile">Datafile represenging the hash data</param>
|
||||
/// <returns>True if extraction was successful, false otherwise</returns>
|
||||
protected static bool GetISOHashValues(Datafile datafile, out long size, out string crc32, out string md5, out string sha1)
|
||||
{
|
||||
size = -1; crc32 = null; md5 = null; sha1 = null;
|
||||
|
||||
if (datafile?.Games == null || datafile.Games.Length == 0 || datafile.Games[0].Roms.Length == 0)
|
||||
return false;
|
||||
|
||||
var rom = datafile.Games[0].Roms[0];
|
||||
|
||||
Int64.TryParse(rom.Size, out size);
|
||||
crc32 = rom.Crc;
|
||||
md5 = rom.Md5;
|
||||
sha1 = rom.Sha1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the layerbreak info associated from the disc information
|
||||
/// </summary>
|
||||
/// <param name="di">Disc information containing unformatted data</param>
|
||||
/// <returns>True if layerbreak info was set, false otherwise</returns>
|
||||
protected static bool GetLayerbreaks(DiscInformation di, out long? layerbreak1, out long? layerbreak2, out long? layerbreak3)
|
||||
{
|
||||
// Set the default values
|
||||
layerbreak1 = null; layerbreak2 = null; layerbreak3 = null;
|
||||
|
||||
// If we don't have valid disc information, we can't do anything
|
||||
if (di?.Units == null || di.Units.Length <= 1)
|
||||
return false;
|
||||
|
||||
int ReadFromArrayBigEndian(byte[] bytes, int offset)
|
||||
{
|
||||
var span = new ReadOnlySpan<byte>(bytes, offset, 0x04);
|
||||
byte[] rev = span.ToArray();
|
||||
Array.Reverse(rev);
|
||||
return BitConverter.ToInt32(rev, 0);
|
||||
}
|
||||
|
||||
// Layerbreak 1 (2+ layers)
|
||||
if (di.Units.Length >= 2)
|
||||
{
|
||||
long offset = ReadFromArrayBigEndian(di.Units[0].Body.FormatDependentContents, 0x0C);
|
||||
long value = ReadFromArrayBigEndian(di.Units[0].Body.FormatDependentContents, 0x10);
|
||||
layerbreak1 = value - offset + 2;
|
||||
}
|
||||
|
||||
// Layerbreak 2 (3+ layers)
|
||||
if (di.Units.Length >= 3)
|
||||
{
|
||||
long offset = ReadFromArrayBigEndian(di.Units[1].Body.FormatDependentContents, 0x0C);
|
||||
long value = ReadFromArrayBigEndian(di.Units[1].Body.FormatDependentContents, 0x10);
|
||||
layerbreak2 = layerbreak1 + value - offset + 2;
|
||||
}
|
||||
|
||||
// Layerbreak 3 (4 layers)
|
||||
if (di.Units.Length >= 4)
|
||||
{
|
||||
long offset = ReadFromArrayBigEndian(di.Units[2].Body.FormatDependentContents, 0x0C);
|
||||
long value = ReadFromArrayBigEndian(di.Units[2].Body.FormatDependentContents, 0x10);
|
||||
layerbreak3 = layerbreak2 + value - offset + 2;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the PIC identifier from the first disc information unit, if possible
|
||||
/// </summary>
|
||||
/// <param name="di">Disc information containing the data</param>
|
||||
/// <returns>String representing the PIC identifier, null on error</returns>
|
||||
protected static string GetPICIdentifier(DiscInformation di)
|
||||
{
|
||||
// If we don't have valid disc information, we can't do anything
|
||||
if (di?.Units == null || di.Units.Length <= 1)
|
||||
return null;
|
||||
|
||||
// We assume the identifier is consistent across all units
|
||||
return di.Units[0].Body.DiscTypeIdentifier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the EXE date from a PlayStation disc, if possible
|
||||
/// </summary>
|
||||
@@ -1188,6 +1475,9 @@ namespace MPF.Modules
|
||||
serial = exeName
|
||||
.Replace('_', '-')
|
||||
.Replace(".", string.Empty);
|
||||
|
||||
// Some games may have the EXE in a subfolder
|
||||
serial = Path.GetFileName(serial);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1244,6 +1534,117 @@ namespace MPF.Modules
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
protected static string GetPlayStation3Serial(char? driveLetter)
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find PARAM.SFO, we don't have a PlayStation 3 disc
|
||||
string paramSfoPath = Path.Combine(drivePath, "PS3_GAME", "PARAM.SFO");
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading PARAM.SFO to find the serial at the end of the file
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(File.OpenRead(paramSfoPath)))
|
||||
{
|
||||
br.BaseStream.Seek(-0x18, SeekOrigin.End);
|
||||
return new string(br.ReadChars(9));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
protected static string GetPlayStation3Version(char? driveLetter)
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find PARAM.SFO, we don't have a PlayStation 3 disc
|
||||
string paramSfoPath = Path.Combine(drivePath, "PS3_GAME", "PARAM.SFO");
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading PARAM.SFO to find the version at the end of the file
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(File.OpenRead(paramSfoPath)))
|
||||
{
|
||||
br.BaseStream.Seek(-0x08, SeekOrigin.End);
|
||||
return new string(br.ReadChars(5));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 4 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
protected static string GetPlayStation4Serial(char? driveLetter)
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find param.sfo, we don't have a PlayStation 4 disc
|
||||
string paramSfoPath = Path.Combine(drivePath, "bd", "param.sfo");
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.sfo to find the serial at the end of the file
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(File.OpenRead(paramSfoPath)))
|
||||
{
|
||||
br.BaseStream.Seek(-0x14, SeekOrigin.End);
|
||||
return new string(br.ReadChars(9));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 4 disc, if possible
|
||||
/// </summary>
|
||||
@@ -1281,6 +1682,43 @@ namespace MPF.Modules
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 5 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="driveLetter">Drive letter to use to check</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
protected static string GetPlayStation5Serial(char? driveLetter)
|
||||
{
|
||||
// If there's no drive letter, we can't do this part
|
||||
if (driveLetter == null)
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
string drivePath = driveLetter + ":\\";
|
||||
if (!Directory.Exists(drivePath))
|
||||
return null;
|
||||
|
||||
// If we can't find param.json, we don't have a PlayStation 5 disc
|
||||
string paramJsonPath = Path.Combine(drivePath, "bd", "param.json");
|
||||
if (!File.Exists(paramJsonPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.json to find the serial in the unencrypted JSON
|
||||
try
|
||||
{
|
||||
using (BinaryReader br = new BinaryReader(File.OpenRead(paramJsonPath)))
|
||||
{
|
||||
br.BaseStream.Seek(0x82E, SeekOrigin.Begin);
|
||||
return new string(br.ReadChars(9));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 5 disc, if possible
|
||||
/// </summary>
|
||||
@@ -1359,10 +1797,33 @@ namespace MPF.Modules
|
||||
case 'A': return Region.Asia;
|
||||
case 'C': return Region.China;
|
||||
case 'E': return Region.Europe;
|
||||
case 'J': return Region.JapanKorea;
|
||||
case 'K': return Region.SouthKorea;
|
||||
case 'P': return Region.Japan;
|
||||
case 'U': return Region.UnitedStatesOfAmerica;
|
||||
case 'P':
|
||||
// Region of S_P_ serials may be Japan, Asia, or SouthKorea
|
||||
switch (serial[3])
|
||||
{
|
||||
case 'S':
|
||||
// Check first two digits of S_PS serial
|
||||
switch (serial.Substring(5, 2))
|
||||
{
|
||||
case "46": return Region.SouthKorea;
|
||||
case "56": return Region.SouthKorea;
|
||||
case "51": return Region.Asia;
|
||||
case "55": return Region.Asia;
|
||||
default: return Region.Japan;
|
||||
}
|
||||
case 'M':
|
||||
// Check first three digits of S_PM serial
|
||||
switch (serial.Substring(5, 3))
|
||||
{
|
||||
case "645": return Region.SouthKorea;
|
||||
case "675": return Region.SouthKorea;
|
||||
case "885": return Region.SouthKorea;
|
||||
default: return Region.Japan; // Remaining S_PM serials may be Japan or Asia
|
||||
}
|
||||
default: return Region.Japan;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1374,10 +1835,18 @@ namespace MPF.Modules
|
||||
else if (serial.StartsWith("PABX"))
|
||||
return null;
|
||||
|
||||
// Region appears entirely random
|
||||
else if (serial.StartsWith("PBPX"))
|
||||
return null;
|
||||
|
||||
// Japan-only special serial
|
||||
else if (serial.StartsWith("PCBX"))
|
||||
return Region.Japan;
|
||||
|
||||
// Japan-only special serial
|
||||
else if (serial.StartsWith("PCXC"))
|
||||
return Region.Japan;
|
||||
|
||||
// Single disc known, Japan
|
||||
else if (serial.StartsWith("PDBX"))
|
||||
return Region.Japan;
|
||||
@@ -1386,9 +1855,13 @@ namespace MPF.Modules
|
||||
else if (serial.StartsWith("PEBX"))
|
||||
return Region.Europe;
|
||||
|
||||
// Single disc known, USA
|
||||
else if (serial.StartsWith("PUBX"))
|
||||
return Region.UnitedStatesOfAmerica;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.CleanRip
|
||||
{
|
||||
@@ -58,12 +60,16 @@ namespace MPF.Modules.CleanRip
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
info.TracksAndWriteOffsets.ClrMameProData = GetCleanripDatfile(basePath + ".iso", basePath + "-dumpinfo.txt");
|
||||
// TODO: Determine if there's a CleanRip version anywhere
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
Datafile datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
|
||||
|
||||
// Get the individual hash data, as per internal
|
||||
if (GetISOHashValues(info.TracksAndWriteOffsets.ClrMameProData, out long size, out string crc32, out string md5, out string sha1))
|
||||
if (GetISOHashValues(datafile, out long size, out string crc32, out string md5, out string sha1))
|
||||
{
|
||||
info.SizeAndChecksums.Size = size;
|
||||
info.SizeAndChecksums.CRC32 = crc32;
|
||||
@@ -84,10 +90,11 @@ namespace MPF.Modules.CleanRip
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
info.Extras.BCA = GetBCA(basePath + ".bca");
|
||||
|
||||
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out string gcVersion))
|
||||
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out string gcVersion, out string gcName))
|
||||
{
|
||||
info.CommonDiscInfo.Region = gcRegion ?? info.CommonDiscInfo.Region;
|
||||
info.VersionAndEditions.Version = gcVersion ?? info.VersionAndEditions.Version;
|
||||
info.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = gcName ?? string.Empty;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -127,6 +134,65 @@ namespace MPF.Modules.CleanRip
|
||||
|
||||
#region Information Extraction Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted datfile from the cleanrip output, if possible
|
||||
/// </summary>
|
||||
/// <param name="iso">Path to ISO file</param>
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <returns></returns>
|
||||
private static Datafile GenerateCleanripDatafile(string iso, string dumpinfo)
|
||||
{
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(dumpinfo))
|
||||
return null;
|
||||
|
||||
using (StreamReader sr = File.OpenText(dumpinfo))
|
||||
{
|
||||
long size = new FileInfo(iso).Length;
|
||||
string crc = string.Empty;
|
||||
string md5 = string.Empty;
|
||||
string sha1 = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
// Make sure this file is a dumpinfo
|
||||
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
|
||||
return null;
|
||||
|
||||
// Read all lines and gather dat information
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string line = sr.ReadLine().Trim();
|
||||
if (line.StartsWith("CRC32"))
|
||||
crc = line.Substring(7).ToLowerInvariant();
|
||||
else if (line.StartsWith("MD5"))
|
||||
md5 = line.Substring(5);
|
||||
else if (line.StartsWith("SHA-1"))
|
||||
sha1 = line.Substring(7);
|
||||
}
|
||||
|
||||
return new Datafile
|
||||
{
|
||||
Games = new Game[]
|
||||
{
|
||||
new Game
|
||||
{
|
||||
Roms = new Rom[]
|
||||
{
|
||||
new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1 },
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the hex contents of the BCA file
|
||||
/// </summary>
|
||||
@@ -204,10 +270,11 @@ namespace MPF.Modules.CleanRip
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <param name="region">Output region, if possible</param>
|
||||
/// <param name="version">Output internal version of the game</param>
|
||||
/// <param name="name">Output internal name of the game</param>
|
||||
/// <returns></returns>
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string version)
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string version, out string name)
|
||||
{
|
||||
region = null; version = null;
|
||||
region = null; version = null; name = null;
|
||||
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(dumpinfo))
|
||||
@@ -227,11 +294,15 @@ namespace MPF.Modules.CleanRip
|
||||
string line = sr.ReadLine().Trim();
|
||||
if (line.StartsWith("Version"))
|
||||
{
|
||||
version = line.Substring(9);
|
||||
version = line.Substring("Version: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("Internal Name"))
|
||||
{
|
||||
name = line.Substring("Internal Name: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("Filename"))
|
||||
{
|
||||
string serial = line.Substring(10);
|
||||
string serial = line.Substring("Filename: ".Length);
|
||||
|
||||
// char gameType = serial[0];
|
||||
// string gameid = serial[1] + serial[2];
|
||||
|
||||
@@ -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,404 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
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, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
// TODO: Fill in submission info specifics for DD
|
||||
string outputDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
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) ?? "";
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
info.VersionAndEditions.Version = GetPlayStation5Version(drive?.Letter) ?? "";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GenerateParameters()
|
||||
{
|
||||
List<string> parameters = new List<string>();
|
||||
|
||||
if (BaseCommand == null)
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
if (!string.IsNullOrEmpty(BaseCommand))
|
||||
parameters.Add(BaseCommand);
|
||||
|
||||
#region Boolean flags
|
||||
|
||||
// Progress
|
||||
if (IsFlagSupported(FlagStrings.Progress))
|
||||
{
|
||||
if (this[FlagStrings.Progress] == true)
|
||||
parameters.Add($"{FlagStrings.Progress}");
|
||||
}
|
||||
|
||||
// Size
|
||||
if (IsFlagSupported(FlagStrings.Size))
|
||||
{
|
||||
if (this[FlagStrings.Size] == true)
|
||||
parameters.Add($"{FlagStrings.Size}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Int64 flags
|
||||
|
||||
// Block Size
|
||||
if (IsFlagSupported(FlagStrings.BlockSize))
|
||||
{
|
||||
if (this[FlagStrings.BlockSize] == true && BlockSizeValue != null)
|
||||
parameters.Add($"{FlagStrings.BlockSize}={BlockSizeValue}");
|
||||
}
|
||||
|
||||
// Count
|
||||
if (IsFlagSupported(FlagStrings.Count))
|
||||
{
|
||||
if (this[FlagStrings.Count] == true && CountValue != null)
|
||||
parameters.Add($"{FlagStrings.Count}={CountValue}");
|
||||
}
|
||||
|
||||
// Seek
|
||||
if (IsFlagSupported(FlagStrings.Seek))
|
||||
{
|
||||
if (this[FlagStrings.Seek] == true && SeekValue != null)
|
||||
parameters.Add($"{FlagStrings.Seek}={SeekValue}");
|
||||
}
|
||||
|
||||
// Skip
|
||||
if (IsFlagSupported(FlagStrings.Skip))
|
||||
{
|
||||
if (this[FlagStrings.Skip] == true && SkipValue != null)
|
||||
parameters.Add($"{FlagStrings.Skip}={SkipValue}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region String flags
|
||||
|
||||
// Filter
|
||||
if (IsFlagSupported(FlagStrings.Filter))
|
||||
{
|
||||
if (this[FlagStrings.Filter] == true && FilterValue != null)
|
||||
parameters.Add($"{FlagStrings.Filter}={FilterValue}");
|
||||
}
|
||||
|
||||
// Input File
|
||||
if (IsFlagSupported(FlagStrings.InputFile))
|
||||
{
|
||||
if (this[FlagStrings.InputFile] == true && InputFileValue != null)
|
||||
parameters.Add($"{FlagStrings.InputFile}=\"{InputFileValue}\"");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
// Output File
|
||||
if (IsFlagSupported(FlagStrings.OutputFile))
|
||||
{
|
||||
if (this[FlagStrings.OutputFile] == true && OutputFileValue != null)
|
||||
parameters.Add($"{FlagStrings.OutputFile}=\"{OutputFileValue}\"");
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
return string.Join(" ", parameters);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Dictionary<string, List<string>> GetCommandSupport()
|
||||
{
|
||||
return new Dictionary<string, List<string>>()
|
||||
{
|
||||
[CommandStrings.NONE] = new List<string>()
|
||||
{
|
||||
FlagStrings.BlockSize,
|
||||
FlagStrings.Count,
|
||||
FlagStrings.Filter,
|
||||
FlagStrings.InputFile,
|
||||
FlagStrings.OutputFile,
|
||||
FlagStrings.Progress,
|
||||
FlagStrings.Seek,
|
||||
FlagStrings.Size,
|
||||
FlagStrings.Skip,
|
||||
},
|
||||
|
||||
[CommandStrings.List] = new List<string>()
|
||||
{
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string GetDefaultExtension(MediaType? mediaType) => Converters.Extension(mediaType);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool IsDumpingCommand()
|
||||
{
|
||||
switch (this.BaseCommand)
|
||||
{
|
||||
case CommandStrings.List:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void ResetValues()
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
flags = new Dictionary<string, bool?>();
|
||||
|
||||
BlockSizeValue = null;
|
||||
CountValue = null;
|
||||
InputFileValue = null;
|
||||
OutputFileValue = null;
|
||||
SeekValue = null;
|
||||
SkipValue = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void SetDefaultParameters(char driveLetter, string filename, int? driveSpeed, Options options)
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
this[FlagStrings.InputFile] = true;
|
||||
InputFileValue = $"\\\\?\\{driveLetter}:";
|
||||
|
||||
this[FlagStrings.OutputFile] = true;
|
||||
OutputFileValue = filename;
|
||||
|
||||
// TODO: Add more common block sizes
|
||||
this[FlagStrings.BlockSize] = true;
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.FloppyDisk:
|
||||
BlockSizeValue = 1440 * 1024;
|
||||
break;
|
||||
|
||||
default:
|
||||
BlockSizeValue = 1024 * 1024 * 1024;
|
||||
break;
|
||||
}
|
||||
|
||||
this[FlagStrings.Progress] = true;
|
||||
this[FlagStrings.Size] = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override bool ValidateAndSetParameters(string parameters)
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
// The string has to be valid by itself first
|
||||
if (string.IsNullOrWhiteSpace(parameters))
|
||||
return false;
|
||||
|
||||
// Now split the string into parts for easier validation
|
||||
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
|
||||
parameters = parameters.Trim();
|
||||
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+")
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Value)
|
||||
.ToList();
|
||||
|
||||
// Determine what the commandline should look like given the first item
|
||||
int start = 0;
|
||||
if (parts[0] == CommandStrings.List)
|
||||
{
|
||||
BaseCommand = parts[0];
|
||||
start = 1;
|
||||
}
|
||||
|
||||
// Loop through all auxilary flags, if necessary
|
||||
for (int i = start; i < parts.Count; i++)
|
||||
{
|
||||
// Flag read-out values
|
||||
long? longValue = null;
|
||||
string stringValue = null;
|
||||
|
||||
// Keep a count of keys to determine if we should break out to filename handling or not
|
||||
int keyCount = Keys.Count();
|
||||
|
||||
#region Boolean flags
|
||||
|
||||
// Progress
|
||||
ProcessFlagParameter(parts, FlagStrings.Progress, ref i);
|
||||
|
||||
// Size
|
||||
ProcessFlagParameter(parts, FlagStrings.Size, ref i);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Int64 flags
|
||||
|
||||
// Block Size
|
||||
longValue = ProcessInt64Parameter(parts, FlagStrings.BlockSize, ref i);
|
||||
if (longValue != null)
|
||||
BlockSizeValue = longValue;
|
||||
|
||||
// Count
|
||||
longValue = ProcessInt64Parameter(parts, FlagStrings.Count, ref i);
|
||||
if (longValue != null)
|
||||
CountValue = longValue;
|
||||
|
||||
// Seek
|
||||
longValue = ProcessInt64Parameter(parts, FlagStrings.Seek, ref i);
|
||||
if (longValue != null)
|
||||
SeekValue = longValue;
|
||||
|
||||
// Skip
|
||||
longValue = ProcessInt64Parameter(parts, FlagStrings.Skip, ref i);
|
||||
if (longValue != null)
|
||||
SkipValue = longValue;
|
||||
|
||||
#endregion
|
||||
|
||||
#region String flags
|
||||
|
||||
// Filter (fixed, removable, disk, partition)
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.Filter, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
FilterValue = stringValue;
|
||||
|
||||
// Input File
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.InputFile, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
InputFileValue = stringValue;
|
||||
|
||||
// Output File
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.OutputFile, ref i);
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
OutputFileValue = stringValue;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
73
MPF.Modules/Datafile.cs
Normal file
73
MPF.Modules/Datafile.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace MPF.Modules
|
||||
{
|
||||
[XmlRoot("datafile")]
|
||||
public class Datafile
|
||||
{
|
||||
[XmlElement("header")]
|
||||
public Header Header;
|
||||
|
||||
[XmlElement("game")]
|
||||
public Game[] Games;
|
||||
}
|
||||
|
||||
public class Header
|
||||
{
|
||||
[XmlElement("name")]
|
||||
public string Name;
|
||||
|
||||
[XmlElement("description")]
|
||||
public string Description;
|
||||
|
||||
[XmlElement("version")]
|
||||
public string Version;
|
||||
|
||||
[XmlElement("date")]
|
||||
public string Date;
|
||||
|
||||
[XmlElement("author")]
|
||||
public string Author;
|
||||
|
||||
[XmlElement("homepage")]
|
||||
public string Homepage;
|
||||
|
||||
[XmlElement("url")]
|
||||
public string Url;
|
||||
}
|
||||
|
||||
public class Game
|
||||
{
|
||||
[XmlAttribute("name")]
|
||||
public string Name;
|
||||
|
||||
[XmlElement("category")]
|
||||
public string Category;
|
||||
|
||||
[XmlElement("description")]
|
||||
public string Description;
|
||||
|
||||
[XmlElement("rom")]
|
||||
public Rom[] Roms;
|
||||
}
|
||||
|
||||
public class Rom
|
||||
{
|
||||
[XmlAttribute("name")]
|
||||
public string Name;
|
||||
|
||||
[XmlAttribute("size")]
|
||||
public string Size;
|
||||
|
||||
[XmlAttribute("crc")]
|
||||
public string Crc;
|
||||
|
||||
[XmlAttribute("md5")]
|
||||
public string Md5;
|
||||
|
||||
[XmlAttribute("sha1")]
|
||||
public string Sha1;
|
||||
|
||||
// TODO: Add extended hashes here
|
||||
}
|
||||
}
|
||||
@@ -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,12 +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";
|
||||
@@ -57,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";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.DiscImageCreator
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,12 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.3</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<Version>2.6.5</Version>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
@@ -20,27 +18,20 @@
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<DefineConstants>NET_FRAMEWORK</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.CueSheets\MPF.CueSheets.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<ProjectReference Include="..\CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Memory" Version="4.5.4" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
|
||||
<PackageReference Include="SabreTools.Models" Version="1.1.2" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.1.2" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
||||
73
MPF.Modules/Redumper/Constants.cs
Normal file
73
MPF.Modules/Redumper/Constants.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
namespace MPF.Modules.Redumper
|
||||
{
|
||||
/// <summary>
|
||||
/// Top-level commands for Redumper
|
||||
/// </summary>
|
||||
public static class CommandStrings
|
||||
{
|
||||
public const string NONE = "";
|
||||
public const string CD = "cd";
|
||||
public const string 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>
|
||||
/// Dumping flags for Redumper
|
||||
/// </summary>
|
||||
public static class FlagStrings
|
||||
{
|
||||
// General
|
||||
public const string HelpLong = "--help";
|
||||
public const string HelpShort = "-h";
|
||||
public const string Verbose = "--verbose";
|
||||
public const string 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";
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.DD
|
||||
namespace MPF.Modules.Redumper
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
@@ -13,8 +13,18 @@ namespace MPF.Modules.DD
|
||||
/// <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";
|
||||
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
|
||||
2039
MPF.Modules/Redumper/Parameters.cs
Normal file
2039
MPF.Modules/Redumper/Parameters.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,8 +2,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.UmdImageCreator
|
||||
{
|
||||
@@ -60,13 +61,17 @@ namespace MPF.Modules.UmdImageCreator
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, Drive drive, bool includeArtifacts)
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
// TODO: Determine if there's a UMDImageCreator version anywhere
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.UMD:
|
||||
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? "";
|
||||
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
|
||||
|
||||
if (GetFileHashes(basePath + ".iso", out long filesize, out string crc32, out string md5, out string sha1))
|
||||
{
|
||||
@@ -78,9 +83,9 @@ namespace MPF.Modules.UmdImageCreator
|
||||
|
||||
if (GetUMDAuxInfo(basePath + "_disc.txt", out string title, out DiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize))
|
||||
{
|
||||
info.CommonDiscInfo.Title = title ?? "";
|
||||
info.CommonDiscInfo.Title = title ?? string.Empty;
|
||||
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
|
||||
info.VersionAndEditions.Version = umdversion ?? "";
|
||||
info.VersionAndEditions.Version = umdversion ?? string.Empty;
|
||||
info.SizeAndChecksums.Size = umdsize;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(umdlayer))
|
||||
@@ -95,6 +100,8 @@ namespace MPF.Modules.UmdImageCreator
|
||||
{
|
||||
if (File.Exists(basePath + "_disc.txt"))
|
||||
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt"));
|
||||
if (File.Exists(basePath + "_drive.txt"))
|
||||
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt"));
|
||||
if (File.Exists(basePath + "_mainError.txt"))
|
||||
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt"));
|
||||
if (File.Exists(basePath + "_mainInfo.txt"))
|
||||
@@ -113,6 +120,8 @@ namespace MPF.Modules.UmdImageCreator
|
||||
case MediaType.UMD:
|
||||
if (File.Exists($"{basePath}_disc.txt"))
|
||||
logFiles.Add($"{basePath}_disc.txt");
|
||||
if (File.Exists($"{basePath}_drive.txt"))
|
||||
logFiles.Add($"{basePath}_drive.txt");
|
||||
if (File.Exists($"{basePath}_mainError.txt"))
|
||||
logFiles.Add($"{basePath}_mainError.txt");
|
||||
if (File.Exists($"{basePath}_mainInfo.txt"))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#endif
|
||||
using MPF.Core.Converters;
|
||||
@@ -25,7 +25,7 @@ namespace MPF.Test.Core.Converters
|
||||
DriveType.Removable,
|
||||
};
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// IMAPI_MEDIA_PHYSICAL_TYPE values that map to MediaType
|
||||
/// </summary>
|
||||
@@ -71,7 +71,7 @@ namespace MPF.Test.Core.Converters
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Check that every supported IMAPI_MEDIA_PHYSICAL_TYPE maps to an MediaType
|
||||
/// </summary>
|
||||
@@ -108,7 +108,7 @@ namespace MPF.Test.Core.Converters
|
||||
return testData;
|
||||
}
|
||||
|
||||
#if NET_FRAMEWORK
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Generate a test set of IMAPI_MEDIA_PHYSICAL_TYPE values
|
||||
/// </summary>
|
||||
|
||||
@@ -22,13 +22,13 @@ namespace MPF.Test.Core.Data
|
||||
[InlineData("AV00100W\0", "AV", "001", "00", 'W')]
|
||||
public void XGD1ValidTests(string validString, string publisher, string gameId, string version, char regionIdentifier)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(validString, validate: true);
|
||||
XgdInfo xgdInfo = new XgdInfo(validString);
|
||||
|
||||
Assert.True(xgdInfo.Initialized);
|
||||
Assert.Equal(publisher, xgdInfo.PublisherIdentifier);
|
||||
Assert.Equal(gameId, xgdInfo.GameID);
|
||||
Assert.Equal(version, xgdInfo.SKU);
|
||||
Assert.Equal(regionIdentifier, xgdInfo.RegionIdentifier);
|
||||
Assert.Equal(publisher, xgdInfo.XMID.PublisherIdentifier);
|
||||
Assert.Equal(gameId, xgdInfo.XMID.GameID);
|
||||
Assert.Equal(version, xgdInfo.XMID.VersionNumber);
|
||||
Assert.Equal(regionIdentifier, xgdInfo.XMID.RegionIdentifier);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -40,7 +40,7 @@ namespace MPF.Test.Core.Data
|
||||
[InlineData("AV00000Z\0")]
|
||||
public void XGD1InvalidTests(string invalidString)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(invalidString, validate: true);
|
||||
XgdInfo xgdInfo = new XgdInfo(invalidString);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
|
||||
@@ -55,18 +55,18 @@ namespace MPF.Test.Core.Data
|
||||
[InlineData("AV200100W01F11DEADBEEF\0", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
|
||||
public void XGD23ValidTests(string validString, string publisher, string gameId, string sku, char regionIdentifier, string baseVersion, char mediaSubtype, string discNumber, string cert)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(validString, validate: true);
|
||||
XgdInfo xgdInfo = new XgdInfo(validString);
|
||||
|
||||
Assert.True(xgdInfo.Initialized);
|
||||
Assert.Equal(publisher, xgdInfo.PublisherIdentifier);
|
||||
Assert.Equal('2', xgdInfo.PlatformIdentifier);
|
||||
Assert.Equal(gameId, xgdInfo.GameID);
|
||||
Assert.Equal(sku, xgdInfo.SKU);
|
||||
Assert.Equal(regionIdentifier, xgdInfo.RegionIdentifier);
|
||||
Assert.Equal(baseVersion, xgdInfo.BaseVersion);
|
||||
Assert.Equal(mediaSubtype, xgdInfo.MediaSubtypeIdentifier);
|
||||
Assert.Equal(discNumber, xgdInfo.DiscNumberIdentifier);
|
||||
Assert.Equal(cert, xgdInfo.CertificationSubmissionIdentifier);
|
||||
Assert.Equal(publisher, xgdInfo.XeMID.PublisherIdentifier);
|
||||
Assert.Equal('2', xgdInfo.XeMID.PlatformIdentifier);
|
||||
Assert.Equal(gameId, xgdInfo.XeMID.GameID);
|
||||
Assert.Equal(sku, xgdInfo.XeMID.SKU);
|
||||
Assert.Equal(regionIdentifier, xgdInfo.XeMID.RegionIdentifier);
|
||||
Assert.Equal(baseVersion, xgdInfo.XeMID.BaseVersion);
|
||||
Assert.Equal(mediaSubtype, xgdInfo.XeMID.MediaSubtypeIdentifier);
|
||||
Assert.Equal(discNumber, xgdInfo.XeMID.DiscNumberIdentifier);
|
||||
Assert.Equal(cert, xgdInfo.XeMID.CertificationSubmissionIdentifier);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -108,7 +108,7 @@ namespace MPF.Test.Core.Data
|
||||
[InlineData("AV200000W00A0000000000\0")]
|
||||
public void XGD23InvalidTests(string invalidString)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(invalidString, validate: true);
|
||||
XgdInfo xgdInfo = new XgdInfo(invalidString);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.IO;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Library;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Library
|
||||
@@ -18,11 +18,13 @@ namespace MPF.Test.Library
|
||||
public void ParametersValidTest(string parameters, char letter, bool isFloppy, MediaType? mediaType, bool expected)
|
||||
{
|
||||
var options = new Options() { InternalProgram = InternalProgram.DiscImageCreator };
|
||||
var drive = isFloppy
|
||||
? new Drive(InternalDriveType.Floppy, new DriveInfo(letter.ToString()))
|
||||
: new Drive(InternalDriveType.Optical, new DriveInfo(letter.ToString()));
|
||||
|
||||
var env = new DumpEnvironment(options, string.Empty, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, parameters);
|
||||
// TODO: This relies on creating real objects for the drive. Can we mock this out instead?
|
||||
var drive = isFloppy
|
||||
? Drive.Create(InternalDriveType.Floppy, letter.ToString())
|
||||
: Drive.Create(InternalDriveType.Optical, letter.ToString());
|
||||
|
||||
var env = new DumpEnvironment(options, string.Empty, drive, RedumpSystem.IBMPCcompatible, mediaType, null, parameters);
|
||||
|
||||
bool actual = env.ParametersValid();
|
||||
Assert.Equal(expected, actual);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MPF.Library;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Library
|
||||
@@ -43,24 +44,27 @@ namespace MPF.Test.Library
|
||||
long layerbreak3,
|
||||
string expected)
|
||||
{
|
||||
string actual = InfoTool.GetFixedMediaType(mediaType, size, layerbreak, layerbreak2, layerbreak3);
|
||||
// TODO: Add tests around BDU
|
||||
string actual = InfoTool.GetFixedMediaType(mediaType, null, size, layerbreak, layerbreak2, layerbreak3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null, null, null)]
|
||||
[InlineData(" ", "", " ", "")]
|
||||
[InlineData("super", "blah.bin", "super", "blah.bin")]
|
||||
[InlineData("super\\hero", "blah.bin", "super\\hero", "blah.bin")]
|
||||
[InlineData("super.hero", "blah.bin", "super.hero", "blah.bin")]
|
||||
[InlineData("superhero", "blah.rev.bin", "superhero", "blah.rev.bin")]
|
||||
[InlineData("super&hero", "blah.bin", "super&hero", "blah.bin")]
|
||||
[InlineData("superhero", "blah&foo.bin", "superhero", "blah&foo.bin")]
|
||||
public void NormalizeOutputPathsTest(string outputDirectory, string outputFilename, string expectedOutputDirectory, string expectedOutputFilename)
|
||||
[InlineData(null, null)]
|
||||
[InlineData(" ", " ")]
|
||||
[InlineData("super\\blah.bin", "super\\blah.bin")]
|
||||
[InlineData("super\\hero\\blah.bin", "super\\hero\\blah.bin")]
|
||||
[InlineData("super.hero\\blah.bin", "super.hero\\blah.bin")]
|
||||
[InlineData("superhero\\blah.rev.bin", "superhero\\blah.rev.bin")]
|
||||
[InlineData("super&hero\\blah.bin", "super&hero\\blah.bin")]
|
||||
[InlineData("superhero\\blah&foo.bin", "superhero\\blah&foo.bin")]
|
||||
public void NormalizeOutputPathsTest(string outputPath, string expectedPath)
|
||||
{
|
||||
(string actualOutputDirectory, string actualOutputFilename) = InfoTool.NormalizeOutputPaths(outputDirectory, outputFilename);
|
||||
Assert.Equal(expectedOutputDirectory, actualOutputDirectory);
|
||||
Assert.Equal(expectedOutputFilename, actualOutputFilename);
|
||||
if (!string.IsNullOrWhiteSpace(expectedPath))
|
||||
expectedPath = Path.GetFullPath(expectedPath);
|
||||
|
||||
string actualPath = InfoTool.NormalizeOutputPaths(outputPath, false);
|
||||
Assert.Equal(expectedPath, actualPath);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -72,13 +76,21 @@ namespace MPF.Test.Library
|
||||
CommonDiscInfo = new CommonDiscInfoSection()
|
||||
{
|
||||
Comments = "This is a comments line\n[T:ISBN] ISBN Value",
|
||||
#if NET48
|
||||
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
CommentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
|
||||
},
|
||||
|
||||
Contents = "This is a contents line\n[T:GF] Game Footage",
|
||||
#if NET48
|
||||
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
ContentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.Patches] = "1.04 patch",
|
||||
},
|
||||
@@ -128,13 +140,21 @@ namespace MPF.Test.Library
|
||||
CommonDiscInfo = new CommonDiscInfoSection()
|
||||
{
|
||||
Comments = null,
|
||||
#if NET48
|
||||
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
CommentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
|
||||
},
|
||||
|
||||
Contents = null,
|
||||
#if NET48
|
||||
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
ContentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.Patches] = "1.04 patch",
|
||||
},
|
||||
|
||||
@@ -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,15 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0-windows</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<DefineConstants>NET_FRAMEWORK</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<COMReference Include="IMAPI2">
|
||||
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
|
||||
@@ -35,22 +30,25 @@
|
||||
<ProjectReference Include="..\MPF\MPF.csproj" />
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
|
||||
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="16.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.6.3" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="xunit" Version="2.5.0" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="0.10.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.core" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||
<PackageReference Include="xunit.analyzers" Version="1.2.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.5.0" />
|
||||
<PackageReference Include="xunit.core" Version="2.5.0" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.5.0" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.5.0" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.5.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Modules.DiscImageCreator;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Modules
|
||||
@@ -19,38 +19,122 @@ namespace MPF.Test.Modules
|
||||
[InlineData(RedumpSystem.RawThrillsVarious, MediaType.GDROM, null)]
|
||||
public void ParametersFromSystemAndTypeTest(RedumpSystem? knownSystem, MediaType? mediaType, string expected)
|
||||
{
|
||||
var options = new Options { };
|
||||
var options = new Options();
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
Assert.Equal(expected, actual.BaseCommand);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RedumpSystem.AppleMacintosh, MediaType.LaserDisc, true, 20, null, null)]
|
||||
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, false, 20, null, new string[] { FlagStrings.Raw })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, 20, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
|
||||
/* paranoid mode tests */
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, 1000, 2, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
|
||||
[InlineData(RedumpSystem.AppleMacintosh, MediaType.CDROM, false, 20, null, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, 500, null, new string[] { FlagStrings.CopyrightManagementInformation })]
|
||||
/* reread c2 */
|
||||
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, false, 1000, null, new string[] { FlagStrings.C2Opcode })]
|
||||
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, false, -1, null, new string[] { FlagStrings.C2Opcode })]
|
||||
public void ParametersFromOptionsTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoid, int rereadC2, int? subchannelLevel, string[] expected)
|
||||
[InlineData(RedumpSystem.AppleMacintosh, MediaType.LaserDisc, null)] // Deliberately unsupported
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.NintendoGameCube, MediaType.NintendoGameCubeGameDisc, new string[] { FlagStrings.Raw })]
|
||||
public void ParametersFromOptionsSpecialDefaultTest(RedumpSystem? knownSystem, MediaType? mediaType,string[] expected)
|
||||
{
|
||||
var options = new Options { DICParanoidMode = paranoid, DICRereadCount = rereadC2 };
|
||||
var options = new Options();
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
HashSet<string> actualSet = new HashSet<string>(actual.Keys ?? new string[0]);
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, 1000, new string[] { FlagStrings.C2Opcode })]
|
||||
[InlineData(RedumpSystem.SegaDreamcast, MediaType.GDROM, -1, new string[] { FlagStrings.C2Opcode })]
|
||||
public void ParametersFromOptionsC2RereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadC2, string[] expected)
|
||||
{
|
||||
var options = new Options { DICRereadCount = rereadC2 };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (rereadC2 == -1 || !knownSystem.MediaTypes().Contains(mediaType))
|
||||
Assert.Null(actual.C2OpcodeValue[0]);
|
||||
else
|
||||
Assert.Equal(rereadC2, actual.C2OpcodeValue[0]);
|
||||
Assert.Equal(subchannelLevel, actual.SubchannelReadLevelValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, 1000, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, -1, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.BluRay, 1000, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.BluRay, -1, new string[] { FlagStrings.DVDReread })]
|
||||
public void ParametersFromOptionsDVDRereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadDVDBD, string[] expected)
|
||||
{
|
||||
var options = new Options { DICDVDRereadCount = rereadDVDBD };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (rereadDVDBD == -1 || !knownSystem.MediaTypes().Contains(mediaType))
|
||||
Assert.Null(actual.DVDRereadValue);
|
||||
else
|
||||
Assert.Equal(rereadDVDBD, actual.DVDRereadValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, true, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, false, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.MultiSectorRead, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, false, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, new string[] { FlagStrings.DVDReread })]
|
||||
public void ParametersFromOptionsMultiSectorReadTest(RedumpSystem? knownSystem, MediaType? mediaType, bool multiSectorRead, string[] expected)
|
||||
{
|
||||
var options = new Options { DICMultiSectorRead = multiSectorRead };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (expectedSet.Count != 1 && multiSectorRead)
|
||||
Assert.Equal(0, actual.MultiSectorReadValue);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, true, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.BDVideo, MediaType.BluRay, false, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, true, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect, FlagStrings.ScanSectorProtect, FlagStrings.SubchannelReadLevel })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.CDROM, false, new string[] { FlagStrings.C2Opcode, FlagStrings.NoFixSubQSecuROM, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, true, new string[] { FlagStrings.DVDReread, FlagStrings.ScanFileProtect })]
|
||||
[InlineData(RedumpSystem.IBMPCcompatible, MediaType.DVD, false, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, true, new string[] { FlagStrings.DVDReread })]
|
||||
[InlineData(RedumpSystem.HDDVDVideo, MediaType.HDDVD, false, new string[] { FlagStrings.DVDReread })]
|
||||
public void ParametersFromOptionsParanoidModeTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoidMode, string[] expected)
|
||||
{
|
||||
var options = new Options { DICParanoidMode = paranoidMode };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (paranoidMode)
|
||||
{
|
||||
if (actualSet.Contains(FlagStrings.ScanSectorProtect))
|
||||
Assert.True(actual[FlagStrings.ScanSectorProtect]);
|
||||
|
||||
if (actualSet.Contains(FlagStrings.SubchannelReadLevel))
|
||||
{
|
||||
Assert.True(actual[FlagStrings.SubchannelReadLevel]);
|
||||
Assert.Equal(2, actual.SubchannelReadLevelValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (actualSet.Contains(FlagStrings.ScanSectorProtect))
|
||||
Assert.False(actual[FlagStrings.ScanSectorProtect]);
|
||||
|
||||
if (actualSet.Contains(FlagStrings.SubchannelReadLevel))
|
||||
Assert.False(actual[FlagStrings.SubchannelReadLevel]);
|
||||
|
||||
Assert.Null(actual.SubchannelReadLevelValue);
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -78,7 +162,7 @@ namespace MPF.Test.Modules
|
||||
[InlineData(MediaType.NONE, null)]
|
||||
public void MediaTypeToExtensionTest(MediaType? mediaType, string expected)
|
||||
{
|
||||
string actual = MPF.Modules.DiscImageCreator.Converters.Extension(mediaType);
|
||||
string actual = Converters.Extension(mediaType);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
@@ -102,7 +186,7 @@ namespace MPF.Test.Modules
|
||||
[InlineData(CommandStrings.XBOX, MediaType.DVD)]
|
||||
public void BaseCommandToMediaTypeTest(string command, MediaType? expected)
|
||||
{
|
||||
MediaType? actual = MPF.Modules.DiscImageCreator.Converters.ToMediaType(command);
|
||||
MediaType? actual = Converters.ToMediaType(command);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
@@ -126,7 +210,7 @@ namespace MPF.Test.Modules
|
||||
[InlineData(CommandStrings.XBOX, RedumpSystem.MicrosoftXbox)]
|
||||
public void BaseCommandToRedumpSystemTest(string command, RedumpSystem? expected)
|
||||
{
|
||||
RedumpSystem? actual = MPF.Modules.DiscImageCreator.Converters.ToRedumpSystem(command);
|
||||
RedumpSystem? actual = Converters.ToRedumpSystem(command);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
@@ -161,5 +245,25 @@ namespace MPF.Test.Modules
|
||||
Assert.NotNull(newParameters);
|
||||
Assert.Equal(originalParameters, newParameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a HashSet of keys that are considered to be set
|
||||
/// </summary>
|
||||
/// <param name="parameters">Parameters object to get keys from</param>
|
||||
/// <returns>HashSet representing the strings</returns>
|
||||
private static HashSet<string> GenerateUsedKeys(Parameters parameters)
|
||||
{
|
||||
HashSet<string> usedKeys = new HashSet<string>();
|
||||
if (parameters?.Keys == null)
|
||||
return usedKeys;
|
||||
|
||||
foreach (string key in parameters.Keys)
|
||||
{
|
||||
if (parameters[key] == true)
|
||||
usedKeys.Add(key);
|
||||
}
|
||||
|
||||
return usedKeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,12 +18,17 @@ namespace MPF.Test.RedumpLib
|
||||
private static readonly DiscType?[] _mappableDiscTypes = new DiscType?[]
|
||||
{
|
||||
DiscType.BD25,
|
||||
DiscType.BD33,
|
||||
DiscType.BD50,
|
||||
DiscType.BD66,
|
||||
DiscType.BD100,
|
||||
DiscType.BD128,
|
||||
DiscType.CD,
|
||||
DiscType.DVD5,
|
||||
DiscType.DVD9,
|
||||
DiscType.GDROM,
|
||||
DiscType.HDDVDSL,
|
||||
DiscType.HDDVDDL,
|
||||
DiscType.NintendoGameCubeGameDisc,
|
||||
DiscType.NintendoWiiOpticalDiscSL,
|
||||
DiscType.NintendoWiiOpticalDiscDL,
|
||||
@@ -573,17 +578,6 @@ namespace MPF.Test.RedumpLib
|
||||
|
||||
#region System
|
||||
|
||||
/// <summary>
|
||||
/// RedumpSystem values that are considered markers and not real systems
|
||||
/// </summary>
|
||||
private static readonly RedumpSystem?[] _markerSystemTypes = new RedumpSystem?[]
|
||||
{
|
||||
RedumpSystem.MarkerArcadeEnd,
|
||||
RedumpSystem.MarkerComputerEnd,
|
||||
RedumpSystem.MarkerDiscBasedConsoleEnd,
|
||||
RedumpSystem.MarkerOtherEnd,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Check that every RedumpSystem has a long name provided
|
||||
/// </summary>
|
||||
@@ -634,7 +628,7 @@ namespace MPF.Test.RedumpLib
|
||||
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
// We want to skip all markers for this
|
||||
if (_markerSystemTypes.Contains(redumpSystem))
|
||||
if (redumpSystem.IsMarker())
|
||||
continue;
|
||||
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
|
||||
@@ -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
|
||||
@@ -42,7 +43,8 @@ namespace MPF.Test.RedumpLib
|
||||
var submissionInfo = new SubmissionInfo()
|
||||
{
|
||||
SchemaVersion = 1,
|
||||
MatchedIDs = new List<int> { 0, 1, 2, 3 },
|
||||
FullyMatchedID = 3,
|
||||
PartiallyMatchedIDs = new List<int> { 0, 1, 2, 3 },
|
||||
Added = DateTime.UtcNow,
|
||||
LastModified = DateTime.UtcNow,
|
||||
|
||||
@@ -80,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",
|
||||
},
|
||||
@@ -132,7 +142,7 @@ namespace MPF.Test.RedumpLib
|
||||
|
||||
DumpersAndStatus = new DumpersAndStatusSection()
|
||||
{
|
||||
Status = DumpStatus.TwoOrMoHumanReadablesGreen,
|
||||
Status = DumpStatus.TwoOrMoreGreen,
|
||||
Dumpers = new string[] { "Dumper1", "Dumper2" },
|
||||
OtherDumpers = "Dumper3",
|
||||
},
|
||||
@@ -156,6 +166,16 @@ namespace MPF.Test.RedumpLib
|
||||
SHA1 = "SHA1",
|
||||
},
|
||||
|
||||
DumpingInfo = new DumpingInfoSection()
|
||||
{
|
||||
DumpingProgram = "DiscImageCreator 20500101",
|
||||
DumpingDate = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
Manufacturer = "ATAPI",
|
||||
Model = "Optical Drive",
|
||||
Firmware = "1.23",
|
||||
ReportedDiscType = "CD-R",
|
||||
},
|
||||
|
||||
Artifacts = new Dictionary<string, string>()
|
||||
{
|
||||
["Sample Artifact"] = "Sample Data",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Linq;
|
||||
using MPF.Core.Data;
|
||||
using RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Data
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
|
||||
namespace MPF
|
||||
namespace MPF.UI.Core.ComboBoxItems
|
||||
{
|
||||
/// <summary>
|
||||
/// A generic combo box element
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF
|
||||
namespace MPF.UI.Core.ComboBoxItems
|
||||
{
|
||||
public interface IElement
|
||||
{
|
||||
@@ -1,11 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Utilities;
|
||||
using RedumpLib.Data;
|
||||
using MPF.UI.Core.ComboBoxItems;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF
|
||||
namespace MPF.UI.Core.ComboBoxItems
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single item in the System combo box
|
||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MPF
|
||||
namespace MPF.UI.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Variables for UI elements
|
||||
@@ -13,11 +13,13 @@ namespace MPF
|
||||
// Private lists of known drive speed ranges
|
||||
private static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
private static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
|
||||
|
||||
// Create collections for UI based on known drive speeds
|
||||
public static DoubleCollection SpeedsForCDAsCollection { get; } = GetDoubleCollectionFromIntList(CD);
|
||||
public static DoubleCollection SpeedsForDVDAsCollection { get; } = GetDoubleCollectionFromIntList(DVD);
|
||||
public static DoubleCollection SpeedsForHDDVDAsCollection { get; } = GetDoubleCollectionFromIntList(HDDVD);
|
||||
public static DoubleCollection SpeedsForBDAsCollection { get; } = GetDoubleCollectionFromIntList(BD);
|
||||
private static DoubleCollection GetDoubleCollectionFromIntList(IReadOnlyList<int> list)
|
||||
=> new DoubleCollection(list.Select(i => Convert.ToDouble(i)).ToList());
|
||||
|
Before Width: | Height: | Size: 423 KiB After Width: | Height: | Size: 423 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
61
MPF.UI.Core/MPF.UI.Core.csproj
Normal file
61
MPF.UI.Core/MPF.UI.Core.csproj
Normal file
@@ -0,0 +1,61 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64</RuntimeIdentifiers>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
<Title>MPF.UI.Core</Title>
|
||||
<AssemblyName>MPF.UI.Core</AssemblyName>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<Version>2.6.5</Version>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
<UserSecretsId>27abb4ca-bf7a-431e-932f-49153303d5ff</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\ring-code-guide-1-layer.png" />
|
||||
<Resource Include="Images\ring-code-guide-2-layer.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationFramework.Aero" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Images\" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="UserControls\UserInput.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="Windows\DiscInformationWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="Windows\RingCodeGuideWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
400
MPF.UI.Core/Theme.cs
Normal file
400
MPF.UI.Core/Theme.cs
Normal file
@@ -0,0 +1,400 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MPF.UI.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents all required mapping values for the UI
|
||||
/// </summary>
|
||||
public class Theme
|
||||
{
|
||||
#region Application-Wide
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush used to paint the active window's border.
|
||||
/// </summary>
|
||||
public SolidColorBrush ActiveBorderBrush { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints the face of a three-dimensional display element.
|
||||
/// </summary>
|
||||
public SolidColorBrush ControlBrush { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints text in a three-dimensional display element.
|
||||
/// </summary>
|
||||
public SolidColorBrush ControlTextBrush { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints disabled text.
|
||||
/// </summary>
|
||||
public SolidColorBrush GrayTextBrush { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints the background of a window's client area.
|
||||
/// </summary>
|
||||
public SolidColorBrush WindowBrush { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints the text in the client area of a window.
|
||||
/// </summary>
|
||||
public SolidColorBrush WindowTextBrush { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Button
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.Disabled.Background resource
|
||||
/// </summary>
|
||||
public Brush Button_Disabled_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.MouseOver.Background resource
|
||||
/// </summary>
|
||||
public Brush Button_MouseOver_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.Pressed.Background resource
|
||||
/// </summary>
|
||||
public Brush Button_Pressed_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.Static.Background resource
|
||||
/// </summary>
|
||||
public Brush Button_Static_Background { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ComboBox
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Disabled.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Disabled_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Disabled.Editable.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Disabled_Editable_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Disabled.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Disabled_Editable_Button_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.MouseOver.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_MouseOver_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.MouseOver.Editable.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_MouseOver_Editable_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.MouseOver.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_MouseOver_Editable_Button_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Pressed.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Pressed_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Pressed.Editable.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Pressed_Editable_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Pressed.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Pressed_Editable_Button_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Static.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Static_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Static.Editable.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Static_Editable_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Static.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Static_Editable_Button_Background { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region CustomMessageBox
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the CustomMessageBox.Static.Background resource
|
||||
/// </summary>
|
||||
public Brush CustomMessageBox_Static_Background { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region MenuItem
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the MenuItem.SubMenu.Background resource
|
||||
/// </summary>
|
||||
public Brush MenuItem_SubMenu_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the MenuItem.SubMenu.Border resource
|
||||
/// </summary>
|
||||
public Brush MenuItem_SubMenu_Border { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ProgressBar
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ProgressBar.Background resource
|
||||
/// </summary>
|
||||
public Brush ProgressBar_Background { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region ScrollViewer
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ScrollViewer.ScrollBar.Background resource
|
||||
/// </summary>
|
||||
public Brush ScrollViewer_ScrollBar_Background { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region TabItem
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the TabItem.Selected.Background resource
|
||||
/// </summary>
|
||||
public Brush TabItem_Selected_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the TabItem.Static.Background resource
|
||||
/// </summary>
|
||||
public Brush TabItem_Static_Background { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the TabItem.Static.Border resource
|
||||
/// </summary>
|
||||
public Brush TabItem_Static_Border { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region TextBox
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the TextBox.Static.Background resource
|
||||
/// </summary>
|
||||
public Brush TextBox_Static_Background { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Apply the theme to the current application
|
||||
/// </summary>
|
||||
public void Apply()
|
||||
{
|
||||
// Handle application-wide resources
|
||||
Application.Current.Resources[SystemColors.ActiveBorderBrushKey] = this.ActiveBorderBrush;
|
||||
Application.Current.Resources[SystemColors.ControlBrushKey] = this.ControlBrush;
|
||||
Application.Current.Resources[SystemColors.ControlTextBrushKey] = this.ControlTextBrush;
|
||||
Application.Current.Resources[SystemColors.GrayTextBrushKey] = this.GrayTextBrush;
|
||||
Application.Current.Resources[SystemColors.WindowBrushKey] = this.WindowBrush;
|
||||
Application.Current.Resources[SystemColors.WindowTextBrushKey] = this.WindowTextBrush;
|
||||
|
||||
// Handle Button-specific resources
|
||||
Application.Current.Resources["Button.Disabled.Background"] = this.Button_Disabled_Background;
|
||||
Application.Current.Resources["Button.MouseOver.Background"] = this.Button_MouseOver_Background;
|
||||
Application.Current.Resources["Button.Pressed.Background"] = this.Button_Pressed_Background;
|
||||
Application.Current.Resources["Button.Static.Background"] = this.Button_Static_Background;
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
Application.Current.Resources["ComboBox.Disabled.Background"] = this.ComboBox_Disabled_Background;
|
||||
Application.Current.Resources["ComboBox.Disabled.Editable.Background"] = this.ComboBox_Disabled_Editable_Background;
|
||||
Application.Current.Resources["ComboBox.Disabled.Editable.Button.Background"] = this.ComboBox_Disabled_Editable_Button_Background;
|
||||
Application.Current.Resources["ComboBox.MouseOver.Background"] = this.ComboBox_MouseOver_Background;
|
||||
Application.Current.Resources["ComboBox.MouseOver.Editable.Background"] = this.ComboBox_MouseOver_Editable_Background;
|
||||
Application.Current.Resources["ComboBox.MouseOver.Editable.Button.Background"] = this.ComboBox_MouseOver_Editable_Button_Background;
|
||||
Application.Current.Resources["ComboBox.Pressed.Background"] = this.ComboBox_Pressed_Background;
|
||||
Application.Current.Resources["ComboBox.Pressed.Editable.Background"] = this.ComboBox_Pressed_Editable_Background;
|
||||
Application.Current.Resources["ComboBox.Pressed.Editable.Button.Background"] = this.ComboBox_Pressed_Editable_Button_Background;
|
||||
Application.Current.Resources["ComboBox.Static.Background"] = this.ComboBox_Static_Background;
|
||||
Application.Current.Resources["ComboBox.Static.Editable.Background"] = this.ComboBox_Static_Editable_Background;
|
||||
Application.Current.Resources["ComboBox.Static.Editable.Button.Background"] = this.ComboBox_Static_Editable_Button_Background;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
Application.Current.Resources["CustomMessageBox.Static.Background"] = this.CustomMessageBox_Static_Background;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
Application.Current.Resources["MenuItem.SubMenu.Background"] = this.MenuItem_SubMenu_Background;
|
||||
Application.Current.Resources["MenuItem.SubMenu.Border"] = this.MenuItem_SubMenu_Border;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
Application.Current.Resources["ProgressBar.Background"] = this.ProgressBar_Background;
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
Application.Current.Resources["ScrollViewer.ScrollBar.Background"] = this.ScrollViewer_ScrollBar_Background;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
Application.Current.Resources["TabItem.Selected.Background"] = this.TabItem_Selected_Background;
|
||||
Application.Current.Resources["TabItem.Static.Background"] = this.TabItem_Static_Background;
|
||||
Application.Current.Resources["TabItem.Static.Border"] = this.TabItem_Static_Border;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
Application.Current.Resources["TextBox.Static.Background"] = this.TextBox_Static_Background;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default light-mode theme
|
||||
/// </summary>
|
||||
public class LightModeTheme : Theme
|
||||
{
|
||||
public LightModeTheme()
|
||||
{
|
||||
// Handle application-wide resources
|
||||
this.ActiveBorderBrush = null;
|
||||
this.ControlBrush = null;
|
||||
this.ControlTextBrush = null;
|
||||
this.GrayTextBrush = null;
|
||||
this.WindowBrush = null;
|
||||
this.WindowTextBrush = null;
|
||||
|
||||
// Handle Button-specific resources
|
||||
this.Button_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF4));
|
||||
this.Button_MouseOver_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xBE, 0xE6, 0xFD));
|
||||
this.Button_Pressed_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xC4, 0xE5, 0xF6));
|
||||
this.Button_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xDD, 0xDD, 0xDD));
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
this.ComboBox_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
|
||||
this.ComboBox_Disabled_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Disabled_Editable_Button_Background = Brushes.Transparent;
|
||||
this.ComboBox_MouseOver_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xEC, 0xF4, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_MouseOver_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_MouseOver_Editable_Button_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xEB, 0xF4, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Pressed_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xDA, 0xEC, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Pressed_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Pressed_Editable_Button_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xDA, 0xEB, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Static_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
|
||||
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Static_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Static_Editable_Button_Background = Brushes.Transparent;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
this.CustomMessageBox_Static_Background = null;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
this.MenuItem_SubMenu_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
|
||||
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
this.ProgressBar_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xE6, 0xE6, 0xE6));
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
this.ScrollViewer_ScrollBar_Background = Brushes.LightGray;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
this.TabItem_Selected_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.TabItem_Static_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
|
||||
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.TabItem_Static_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
this.TextBox_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default dark-mode theme
|
||||
/// </summary>
|
||||
public class DarkModeTheme : Theme
|
||||
{
|
||||
public DarkModeTheme()
|
||||
{
|
||||
// Setup needed brushes
|
||||
var darkModeBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, 0x20, 0x20, 0x20) };
|
||||
|
||||
// Handle application-wide resources
|
||||
this.ActiveBorderBrush = Brushes.Black;
|
||||
this.ControlBrush = darkModeBrush;
|
||||
this.ControlTextBrush = Brushes.White;
|
||||
this.GrayTextBrush = Brushes.DarkGray;
|
||||
this.WindowBrush = darkModeBrush;
|
||||
this.WindowTextBrush = Brushes.White;
|
||||
|
||||
// Handle Button-specific resources
|
||||
this.Button_Disabled_Background = darkModeBrush;
|
||||
this.Button_MouseOver_Background = darkModeBrush;
|
||||
this.Button_Pressed_Background = darkModeBrush;
|
||||
this.Button_Static_Background = darkModeBrush;
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
this.ComboBox_Disabled_Background = darkModeBrush;
|
||||
this.ComboBox_Disabled_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Disabled_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Editable_Button_Background = darkModeBrush;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
this.CustomMessageBox_Static_Background = darkModeBrush;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
this.MenuItem_SubMenu_Background = darkModeBrush;
|
||||
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
this.ProgressBar_Background = darkModeBrush;
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
this.ScrollViewer_ScrollBar_Background = darkModeBrush;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
this.TabItem_Selected_Background = darkModeBrush;
|
||||
this.TabItem_Static_Background = darkModeBrush;
|
||||
this.TabItem_Static_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
this.TextBox_Static_Background = darkModeBrush;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Windows.Controls;
|
||||
using MPF.GUI.ViewModels;
|
||||
using MPF.UI.Core.ViewModels;
|
||||
|
||||
namespace MPF.UserControls
|
||||
namespace MPF.UI.Core.UserControls
|
||||
{
|
||||
public partial class LogOutput : UserControl
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
<UserControl x:Class="MPF.UserControls.UserInput"
|
||||
<UserControl x:Class="MPF.UI.Core.UserControls.UserInput"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace MPF.UserControls
|
||||
namespace MPF.UI.Core.UserControls
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for UserInput.xaml
|
||||
@@ -106,7 +106,6 @@ namespace MPF.UserControls
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public UserInput()
|
||||
{
|
||||
// Set default values
|
||||
@@ -1,11 +1,13 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.Windows;
|
||||
using RedumpLib.Data;
|
||||
using MPF.UI.Core.ComboBoxItems;
|
||||
using MPF.UI.Core.Windows;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.GUI.ViewModels
|
||||
namespace MPF.UI.Core.ViewModels
|
||||
{
|
||||
public class DiscInformationViewModel
|
||||
{
|
||||
@@ -16,6 +18,11 @@ namespace MPF.GUI.ViewModels
|
||||
/// </summary>
|
||||
public DiscInformationWindow Parent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Application-level Options object
|
||||
/// </summary>
|
||||
public Options Options { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SubmissionInfo object to fill and save
|
||||
/// </summary>
|
||||
@@ -35,11 +42,152 @@ namespace MPF.GUI.ViewModels
|
||||
/// </summary>
|
||||
public List<Element<Region>> Regions { get; private set; } = Element<Region>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of Redump-supported Regions
|
||||
/// </summary>
|
||||
private static readonly List<Region> RedumpRegions = new List<Region>
|
||||
{
|
||||
Region.Argentina,
|
||||
Region.Asia,
|
||||
Region.AsiaEurope,
|
||||
Region.AsiaUSA,
|
||||
Region.Australia,
|
||||
Region.AustraliaGermany,
|
||||
Region.AustraliaNewZealand,
|
||||
Region.Austria,
|
||||
Region.AustriaSwitzerland,
|
||||
Region.Belarus,
|
||||
Region.Belgium,
|
||||
Region.BelgiumNetherlands,
|
||||
Region.Brazil,
|
||||
Region.Bulgaria,
|
||||
Region.Canada,
|
||||
Region.China,
|
||||
Region.Croatia,
|
||||
Region.Czechia,
|
||||
Region.Denmark,
|
||||
Region.Estonia,
|
||||
Region.Europe,
|
||||
Region.EuropeAsia,
|
||||
Region.EuropeAustralia,
|
||||
Region.EuropeCanada,
|
||||
Region.EuropeGermany,
|
||||
Region.Export,
|
||||
Region.Finland,
|
||||
Region.France,
|
||||
Region.FranceSpain,
|
||||
Region.Germany,
|
||||
Region.GreaterChina,
|
||||
Region.Greece,
|
||||
Region.Hungary,
|
||||
Region.Iceland,
|
||||
Region.India,
|
||||
Region.Ireland,
|
||||
Region.Israel,
|
||||
Region.Italy,
|
||||
Region.Japan,
|
||||
Region.JapanAsia,
|
||||
Region.JapanEurope,
|
||||
Region.JapanKorea,
|
||||
Region.JapanUSA,
|
||||
Region.SouthKorea,
|
||||
Region.LatinAmerica,
|
||||
Region.Lithuania,
|
||||
Region.Netherlands,
|
||||
Region.NewZealand,
|
||||
Region.Norway,
|
||||
Region.Poland,
|
||||
Region.Portugal,
|
||||
Region.Romania,
|
||||
Region.RussianFederation,
|
||||
Region.Scandinavia,
|
||||
Region.Serbia,
|
||||
Region.Singapore,
|
||||
Region.Slovakia,
|
||||
Region.SouthAfrica,
|
||||
Region.Spain,
|
||||
Region.SpainPortugal,
|
||||
Region.Sweden,
|
||||
Region.Switzerland,
|
||||
Region.Taiwan,
|
||||
Region.Thailand,
|
||||
Region.Turkey,
|
||||
Region.UnitedArabEmirates,
|
||||
Region.UnitedKingdom,
|
||||
Region.UKAustralia,
|
||||
Region.Ukraine,
|
||||
Region.UnitedStatesOfAmerica,
|
||||
Region.USAAsia,
|
||||
Region.USAAustralia,
|
||||
Region.USABrazil,
|
||||
Region.USACanada,
|
||||
Region.USAEurope,
|
||||
Region.USAGermany,
|
||||
Region.USAJapan,
|
||||
Region.USAKorea,
|
||||
Region.World,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// List of available languages
|
||||
/// </summary>
|
||||
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of Redump-supported Languages
|
||||
/// </summary>
|
||||
private static readonly List<Language> RedumpLanguages = new List<Language>
|
||||
{
|
||||
Language.Afrikaans,
|
||||
Language.Albanian,
|
||||
Language.Arabic,
|
||||
Language.Armenian,
|
||||
Language.Basque,
|
||||
Language.Belarusian,
|
||||
Language.Bulgarian,
|
||||
Language.Catalan,
|
||||
Language.Chinese,
|
||||
Language.Croatian,
|
||||
Language.Czech,
|
||||
Language.Danish,
|
||||
Language.Dutch,
|
||||
Language.English,
|
||||
Language.Estonian,
|
||||
Language.Finnish,
|
||||
Language.French,
|
||||
Language.Gaelic,
|
||||
Language.German,
|
||||
Language.Greek,
|
||||
Language.Hebrew,
|
||||
Language.Hindi,
|
||||
Language.Hungarian,
|
||||
Language.Icelandic,
|
||||
Language.Indonesian,
|
||||
Language.Italian,
|
||||
Language.Japanese,
|
||||
Language.Korean,
|
||||
Language.Latin,
|
||||
Language.Latvian,
|
||||
Language.Lithuanian,
|
||||
Language.Macedonian,
|
||||
Language.Norwegian,
|
||||
Language.Polish,
|
||||
Language.Portuguese,
|
||||
Language.Panjabi,
|
||||
Language.Romanian,
|
||||
Language.Russian,
|
||||
Language.Serbian,
|
||||
Language.Slovak,
|
||||
Language.Slovenian,
|
||||
Language.Spanish,
|
||||
Language.Swedish,
|
||||
Language.Tamil,
|
||||
Language.Thai,
|
||||
Language.Turkish,
|
||||
Language.Ukrainian,
|
||||
Language.Vietnamese,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// List of available languages
|
||||
/// </summary>
|
||||
@@ -50,11 +198,19 @@ namespace MPF.GUI.ViewModels
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public DiscInformationViewModel(DiscInformationWindow parent, SubmissionInfo submissionInfo)
|
||||
public DiscInformationViewModel(DiscInformationWindow parent, Options options, SubmissionInfo submissionInfo)
|
||||
{
|
||||
Parent = parent;
|
||||
Options = options;
|
||||
SubmissionInfo = submissionInfo.Clone() as SubmissionInfo ?? new SubmissionInfo();
|
||||
|
||||
// Limit lists, if necessary
|
||||
if (this.Options.EnableRedumpCompatibility)
|
||||
{
|
||||
SetRedumpRegions();
|
||||
SetRedumpLanguages();
|
||||
}
|
||||
|
||||
// Add handlers
|
||||
Parent.AcceptButton.Click += OnAcceptClick;
|
||||
Parent.CancelButton.Click += OnCancelClick;
|
||||
@@ -74,15 +230,19 @@ namespace MPF.GUI.ViewModels
|
||||
{
|
||||
// Additional Information
|
||||
Parent.CommentsTextBox.Tab = true;
|
||||
|
||||
// Contents
|
||||
Parent.GeneralContent.Tab = true;
|
||||
Parent.ExtrasTextBox.Tab = true;
|
||||
Parent.GameFootageTextBox.Tab = true;
|
||||
Parent.GamesTextBox.Tab = true;
|
||||
Parent.NetYarozeGamesTextBox.Tab = true;
|
||||
Parent.PatchesTextBox.Tab = true;
|
||||
Parent.PlayableDemosTextBox.Tab = true;
|
||||
Parent.RollingDemosTextBox.Tab = true;
|
||||
Parent.SavegamesTextBox.Tab = true;
|
||||
Parent.TechDemosTextBox.Tab = true;
|
||||
Parent.GameFootageTextBox.Tab = true;
|
||||
Parent.VideosTextBox.Tab = true;
|
||||
Parent.PatchesTextBox.Tab = true;
|
||||
Parent.SavegamesTextBox.Tab = true;
|
||||
Parent.ExtrasTextBox.Tab = true;
|
||||
|
||||
// L0
|
||||
Parent.L0MasteringRing.Tab = true;
|
||||
@@ -114,14 +274,22 @@ namespace MPF.GUI.ViewModels
|
||||
/// </summary>
|
||||
private void HideReadOnlyFields()
|
||||
{
|
||||
if (SubmissionInfo?.MatchedIDs == null)
|
||||
Parent.MatchedIDs.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.FullyMatchedID == null)
|
||||
Parent.FullyMatchedID.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.MatchedIDs.Text = string.Join(", ", SubmissionInfo.MatchedIDs);
|
||||
Parent.FullyMatchedID.Text = SubmissionInfo.FullyMatchedID.ToString();
|
||||
if (SubmissionInfo?.PartiallyMatchedIDs == null)
|
||||
Parent.PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.PartiallyMatchedIDs.Text = string.Join(", ", SubmissionInfo.PartiallyMatchedIDs);
|
||||
if (SubmissionInfo?.CopyProtection?.AntiModchip == null)
|
||||
Parent.AntiModchip.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.AntiModchip.Text = SubmissionInfo.CopyProtection.AntiModchip.LongName();
|
||||
if (SubmissionInfo?.TracksAndWriteOffsets?.OtherWriteOffsets == null)
|
||||
Parent.DiscOffset.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.DiscOffset.Text = SubmissionInfo.TracksAndWriteOffsets.OtherWriteOffsets;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.DMIHash) != true)
|
||||
Parent.DMIHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CommonDiscInfo?.ErrorsCount))
|
||||
@@ -132,8 +300,12 @@ namespace MPF.GUI.ViewModels
|
||||
Parent.EDC.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.EDC.Text = SubmissionInfo.EDC.EDC.LongName();
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Filename) != true)
|
||||
Parent.Filename.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.Header))
|
||||
Parent.Header.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.InternalName) != true)
|
||||
Parent.InternalName.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.InternalSerialName) != true)
|
||||
Parent.InternalSerialName.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CopyProtection?.LibCrypt == null)
|
||||
@@ -146,10 +318,12 @@ namespace MPF.GUI.ViewModels
|
||||
Parent.PFIHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.PIC))
|
||||
Parent.PIC.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.Protection))
|
||||
Parent.Protection.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.PVD))
|
||||
Parent.PVD.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Multisession) != true)
|
||||
Parent.Multisession.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.RingNonZeroDataStart) != true)
|
||||
Parent.RingNonZeroDataStart.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.SecuROMData))
|
||||
Parent.SecuROMData.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSHash) != true)
|
||||
@@ -158,6 +332,8 @@ namespace MPF.GUI.ViewModels
|
||||
Parent.SecuritySectorRanges.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSVersion) != true)
|
||||
Parent.SSVersion.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.UniversalHash) != true)
|
||||
Parent.UniversalHash.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.VolumeLabel) != true)
|
||||
Parent.VolumeLabel.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.XeMID) != true)
|
||||
@@ -172,7 +348,7 @@ namespace MPF.GUI.ViewModels
|
||||
private void ManipulateFields()
|
||||
{
|
||||
// Enable tabs in all fields, if required
|
||||
if (App.Options.EnableTabsInInputFields)
|
||||
if (this.Options.EnableTabsInInputFields)
|
||||
EnableTabsInInputFields();
|
||||
|
||||
// Hide read-only fields that don't have values set
|
||||
@@ -219,6 +395,8 @@ namespace MPF.GUI.ViewModels
|
||||
// Physical Identifiers
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.BBFCRegistrationNumber))
|
||||
Parent.BBFCRegistrationNumberTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BBFCRegistrationNumber];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.CDProjektID))
|
||||
Parent.CDProjektIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.CDProjektID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DiscHologramID))
|
||||
Parent.DiscHologramIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DiscHologramID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DNASDiscID))
|
||||
@@ -283,14 +461,24 @@ namespace MPF.GUI.ViewModels
|
||||
// Read-Only Information
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DMIHash))
|
||||
Parent.DMIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Filename))
|
||||
Parent.Filename.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalName))
|
||||
Parent.InternalName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalSerialName))
|
||||
Parent.InternalSerialName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Multisession))
|
||||
Parent.Multisession.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PFIHash))
|
||||
Parent.PFIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.RingNonZeroDataStart))
|
||||
Parent.RingNonZeroDataStart.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSHash))
|
||||
Parent.SSHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSVersion))
|
||||
Parent.SSVersion.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.UniversalHash))
|
||||
Parent.UniversalHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VolumeLabel))
|
||||
Parent.VolumeLabel.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.XeMID))
|
||||
@@ -348,9 +536,17 @@ namespace MPF.GUI.ViewModels
|
||||
|
||||
// Initialize the dictionaries, if needed
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields == null)
|
||||
#if NET48
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode?, string>();
|
||||
#else
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields = new Dictionary<SiteCode, string>();
|
||||
#endif
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields == null)
|
||||
#if NET48
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields = new Dictionary<SiteCode?, string>();
|
||||
#else
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields = new Dictionary<SiteCode, string>();
|
||||
#endif
|
||||
|
||||
#region Comment Fields
|
||||
|
||||
@@ -364,6 +560,7 @@ namespace MPF.GUI.ViewModels
|
||||
|
||||
// Physical Identifiers
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BBFCRegistrationNumber] = Parent.BBFCRegistrationNumberTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.CDProjektID] = Parent.CDProjektIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DiscHologramID] = Parent.DiscHologramIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DNASDiscID] = Parent.DNASDiscIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISBN] = Parent.ISBNTextBox.Text;
|
||||
@@ -398,10 +595,15 @@ namespace MPF.GUI.ViewModels
|
||||
|
||||
// Read-Only Information
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = Parent.DMIHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename] = Parent.Filename.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = Parent.InternalName.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = Parent.InternalSerialName.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession] = Parent.Multisession.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = Parent.PFIHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart] = Parent.RingNonZeroDataStart.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = Parent.SSHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = Parent.SSVersion.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash] = Parent.UniversalHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel] = Parent.VolumeLabel.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = Parent.XeMID.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = Parent.XMID.Text;
|
||||
@@ -431,6 +633,22 @@ namespace MPF.GUI.ViewModels
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulate the list of Languages based on Redump support
|
||||
/// </summary>
|
||||
private void SetRedumpLanguages()
|
||||
{
|
||||
this.Languages = RedumpLanguages.Select(l => new Element<Language>(l)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulate the list of Regions based on Redump support
|
||||
/// </summary>
|
||||
private void SetRedumpRegions()
|
||||
{
|
||||
this.Regions = RedumpRegions.Select(r => new Element<Region>(r)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visible fields and sections based on the media type
|
||||
/// </summary>
|
||||
@@ -462,8 +680,13 @@ namespace MPF.GUI.ViewModels
|
||||
case DiscType.DVD5:
|
||||
case DiscType.DVD9:
|
||||
case DiscType.HDDVDSL:
|
||||
case DiscType.HDDVDDL:
|
||||
case DiscType.BD25:
|
||||
case DiscType.BD33:
|
||||
case DiscType.BD50:
|
||||
case DiscType.BD66:
|
||||
case DiscType.BD100:
|
||||
case DiscType.BD128:
|
||||
case DiscType.NintendoGameCubeGameDisc:
|
||||
case DiscType.NintendoWiiOpticalDiscSL:
|
||||
case DiscType.NintendoWiiOpticalDiscDL:
|
||||
@@ -586,7 +809,7 @@ namespace MPF.GUI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
293
MPF.UI.Core/ViewModels/LogViewModel.cs
Normal file
293
MPF.UI.Core/ViewModels/LogViewModel.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using MPF.Core.Data;
|
||||
using MPF.UI.Core.UserControls;
|
||||
|
||||
namespace MPF.UI.Core.ViewModels
|
||||
{
|
||||
public class LogViewModel
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Parent OptionsWindow object
|
||||
/// </summary>
|
||||
public LogOutput Parent { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private State Variables
|
||||
|
||||
/// <summary>
|
||||
/// Paragraph backing the log
|
||||
/// </summary>
|
||||
private readonly Paragraph _paragraph;
|
||||
|
||||
/// <summary>
|
||||
/// Cached value of the last line written
|
||||
/// </summary>
|
||||
private Run lastLine = null;
|
||||
|
||||
/// <summary>
|
||||
/// Queue of items that need to be logged
|
||||
/// </summary>
|
||||
private readonly ProcessingQueue<LogLine> logQueue;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public LogViewModel(LogOutput parent)
|
||||
{
|
||||
Parent = parent;
|
||||
|
||||
// Add handlers
|
||||
Parent.OutputViewer.SizeChanged += OutputViewerSizeChanged;
|
||||
Parent.Output.TextChanged += OnTextChanged;
|
||||
Parent.ClearButton.Click += OnClearButton;
|
||||
Parent.SaveButton.Click += OnSaveButton;
|
||||
|
||||
// Update the internal state
|
||||
var document = new FlowDocument()
|
||||
{
|
||||
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
|
||||
};
|
||||
_paragraph = new Paragraph();
|
||||
document.Blocks.Add(_paragraph);
|
||||
Parent.Output.Document = document;
|
||||
|
||||
logQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
|
||||
}
|
||||
|
||||
#region Logging
|
||||
|
||||
/// <summary>
|
||||
/// Log level for output
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
USER,
|
||||
VERBOSE,
|
||||
ERROR,
|
||||
SECRET,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log line wrapper
|
||||
/// </summary>
|
||||
private struct LogLine
|
||||
{
|
||||
public readonly string Text;
|
||||
public readonly LogLevel LogLevel;
|
||||
|
||||
public LogLine(string text, LogLevel logLevel)
|
||||
{
|
||||
this.Text = text;
|
||||
this.LogLevel = logLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the foreground Brush for the current LogLevel
|
||||
/// </summary>
|
||||
/// <returns>Brush representing the color</returns>
|
||||
public Brush GetForegroundColor()
|
||||
{
|
||||
switch (this.LogLevel)
|
||||
{
|
||||
case LogLevel.SECRET:
|
||||
return Brushes.Blue;
|
||||
case LogLevel.ERROR:
|
||||
return Brushes.Red;
|
||||
case LogLevel.VERBOSE:
|
||||
return Brushes.Yellow;
|
||||
case LogLevel.USER:
|
||||
default:
|
||||
return Brushes.White;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a Run object from the current LogLine
|
||||
/// </summary>
|
||||
/// <returns>Run object based on internal values</returns>
|
||||
public Run GenerateRun()
|
||||
{
|
||||
return new Run { Text = this.Text, Foreground = GetForegroundColor() };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue text to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void Log(string text) => LogInternal(text, LogLevel.USER);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue text with a newline to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void LogLn(string text) => Log(text + "\n");
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue error text to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void ErrorLog(string text) => LogInternal(text, LogLevel.ERROR);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue error text with a newline to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void ErrorLogLn(string text) => ErrorLog(text + "\n");
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue secret text to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void SecretLog(string text) => LogInternal(text, LogLevel.SECRET);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue secret text with a newline to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void SecretLogLn(string text) => SecretLog(text + "\n");
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue verbose text to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void VerboseLog(string text) => LogInternal(text, LogLevel.VERBOSE);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue verbose text with a newline to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void VerboseLogLn(string text) => VerboseLog(text + "\n");
|
||||
|
||||
/// <summary>
|
||||
/// Reset the progress bar state
|
||||
/// </summary>
|
||||
public void ResetProgressBar()
|
||||
{
|
||||
Parent.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Parent.ProgressBar.Value = 0;
|
||||
Parent.ProgressLabel.Text = string.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue text to the log with formatting
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
/// <param name="logLevel">LogLevel for the log</param>
|
||||
private void LogInternal(string text, LogLevel logLevel)
|
||||
{
|
||||
// Null text gets ignored
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
// Enqueue the text
|
||||
logQueue.Enqueue(new LogLine(text, logLevel));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the log lines in the queue
|
||||
/// </summary>
|
||||
/// <param name="nextLogLine">LogLine item to process</param>
|
||||
private void ProcessLogLine(LogLine nextLogLine)
|
||||
{
|
||||
// Null text gets ignored
|
||||
string nextText = Parent.Dispatcher.Invoke(() => nextLogLine.Text);
|
||||
if (nextText == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (nextText.StartsWith("\r"))
|
||||
ReplaceLastLine(nextLogLine);
|
||||
else
|
||||
AppendToTextBox(nextLogLine);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// In the event that something fails horribly, we want to log
|
||||
AppendToTextBox(new LogLine(ex.ToString(), LogLevel.ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append log line to the log text box
|
||||
/// </summary>
|
||||
/// <param name="logLine">LogLine value to append</param>
|
||||
private void AppendToTextBox(LogLine logLine)
|
||||
{
|
||||
Parent.Dispatcher.Invoke(() =>
|
||||
{
|
||||
var run = logLine.GenerateRun();
|
||||
_paragraph.Inlines.Add(run);
|
||||
lastLine = run;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace the last line written to the log text box
|
||||
/// </summary>
|
||||
/// <param name="logLine">LogLine value to append</param>
|
||||
private void ReplaceLastLine(LogLine logLine)
|
||||
{
|
||||
Parent.Dispatcher.Invoke(() =>
|
||||
{
|
||||
lastLine.Text = logLine.Text;
|
||||
lastLine.Foreground = logLine.GetForegroundColor();
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Scroll the current view to the bottom
|
||||
/// </summary>
|
||||
public void ScrollToBottom()
|
||||
{
|
||||
Parent.OutputViewer.ScrollToBottom();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EventHandlers
|
||||
|
||||
private void OnClearButton(object sender, EventArgs e)
|
||||
{
|
||||
_paragraph.Inlines.Clear();
|
||||
ResetProgressBar();
|
||||
}
|
||||
|
||||
private void OnSaveButton(object sender, EventArgs e)
|
||||
{
|
||||
using (StreamWriter tw = new StreamWriter(File.OpenWrite("console.log")))
|
||||
{
|
||||
foreach (var inline in _paragraph.Inlines)
|
||||
{
|
||||
if (inline is Run run)
|
||||
tw.Write(run.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTextChanged(object sender, TextChangedEventArgs e) =>
|
||||
ScrollToBottom();
|
||||
|
||||
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e) =>
|
||||
ScrollToBottom();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1617
MPF.UI.Core/ViewModels/MainViewModel.cs
Normal file
1617
MPF.UI.Core/ViewModels/MainViewModel.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,14 +2,16 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Windows;
|
||||
using RedumpLib.Web;
|
||||
using MPF.UI.Core.ComboBoxItems;
|
||||
using MPF.UI.Core.Windows;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
using WPFCustomMessageBox;
|
||||
|
||||
namespace MPF.GUI.ViewModels
|
||||
namespace MPF.UI.Core.ViewModels
|
||||
{
|
||||
public class OptionsViewModel
|
||||
{
|
||||
@@ -49,15 +51,14 @@ namespace MPF.GUI.ViewModels
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public OptionsViewModel(OptionsWindow parent)
|
||||
public OptionsViewModel(OptionsWindow parent, Options baseOptions)
|
||||
{
|
||||
Parent = parent;
|
||||
Options = App.Options.Clone() as Options;
|
||||
Options = new Options(baseOptions);
|
||||
|
||||
// Add handlers
|
||||
Parent.AaruPathButton.Click += BrowseForPathClick;
|
||||
Parent.DiscImageCreatorPathButton.Click += BrowseForPathClick;
|
||||
Parent.DDPathButton.Click += BrowseForPathClick;
|
||||
Parent.DefaultOutputPathButton.Click += BrowseForPathClick;
|
||||
|
||||
Parent.AcceptButton.Click += OnAcceptClick;
|
||||
@@ -103,7 +104,7 @@ namespace MPF.GUI.ViewModels
|
||||
/// </summary>
|
||||
private static List<Element<InternalProgram>> PopulateInternalPrograms()
|
||||
{
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.DD };
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.Redumper };
|
||||
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
|
||||
}
|
||||
|
||||
@@ -186,18 +187,25 @@ namespace MPF.GUI.ViewModels
|
||||
/// <summary>
|
||||
/// Test Redump login credentials
|
||||
/// </summary>
|
||||
private void TestRedumpLogin()
|
||||
#if NET48
|
||||
private bool? TestRedumpLogin()
|
||||
#else
|
||||
private async Task<bool?> TestRedumpLogin()
|
||||
#endif
|
||||
{
|
||||
using (RedumpWebClient wc = new RedumpWebClient())
|
||||
{
|
||||
bool? loggedIn = wc.Login(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
|
||||
if (loggedIn == true)
|
||||
CustomMessageBox.Show(Parent, "Redump login credentials accepted!", "Success", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
else if (loggedIn == false)
|
||||
CustomMessageBox.Show(Parent, "Redump login credentials denied!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
else
|
||||
CustomMessageBox.Show(Parent, "Error validating credentials!", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
#if NET48
|
||||
(bool? success, string message) = RedumpWebClient.ValidateCredentials(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
|
||||
#else
|
||||
(bool? success, string message) = await RedumpHttpClient.ValidateCredentials(Parent.RedumpUsernameTextBox.Text, Parent.RedumpPasswordBox.Password);
|
||||
#endif
|
||||
if (success == true)
|
||||
CustomMessageBox.Show(Parent, message, "Success", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
else if (success == false)
|
||||
CustomMessageBox.Show(Parent, message, "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
else
|
||||
CustomMessageBox.Show(Parent, message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -256,8 +264,13 @@ namespace MPF.GUI.ViewModels
|
||||
/// <summary>
|
||||
/// Test Redump credentials for validity
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private void OnRedumpTestClick(object sender, EventArgs e) =>
|
||||
TestRedumpLogin();
|
||||
#else
|
||||
private async void OnRedumpTestClick(object sender, EventArgs e) =>
|
||||
_ = await TestRedumpLogin();
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
<windows:WindowBase x:Class="MPF.Windows.DiscInformationWindow"
|
||||
<windows:WindowBase x:Class="MPF.UI.Core.Windows.DiscInformationWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UserControls"
|
||||
xmlns:windows="clr-namespace:MPF.Windows"
|
||||
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
|
||||
xmlns:windows="clr-namespace:MPF.UI.Core.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Disc Information" Width="515" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
@@ -165,6 +165,9 @@
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.Comments, Mode=TwoWay}" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top" />
|
||||
<controls:UserInput x:Name="GenreTextBox" Label="Genre"/>
|
||||
<controls:UserInput x:Name="ProtectionTextBox" Label="Protection" ToolTip="CAUTION: Only edit if you know what you are doing!"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.Protection, Mode=TwoWay}" TextHeight="75" TextWrapping="Wrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="0.75*" />
|
||||
@@ -191,6 +194,7 @@
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput x:Name="BBFCRegistrationNumberTextBox" Label="BBFC Reg. No."/>
|
||||
<controls:UserInput x:Name="CDProjektIDTextBox" Label="CD Projekt ID"/>
|
||||
<controls:UserInput x:Name="DiscHologramIDTextBox" Label="Disc Hologram ID"/>
|
||||
<controls:UserInput x:Name="DNASDiscIDTextBox" Label="DNAS Disc ID"/>
|
||||
<controls:UserInput x:Name="ISBNTextBox" Label="ISBN"/>
|
||||
@@ -335,18 +339,26 @@
|
||||
<TabItem Header="Read-Only Info" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput x:Name="MatchedIDs" Label="Matched ID(s)" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="FullyMatchedID" Label="Fully Matched ID" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="PartiallyMatchedIDs" Label="Partially Matched ID(s)" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="DiscOffset" Label="Disc Offset" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="DMIHash" Label="DMI Hash" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="EDC" Label="EDC" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="ErrorsCount" Label="Error(s) Count" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.ErrorsCount, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="EXEDateBuildDate" Label="EXE/Build Date" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.EXEDateBuildDate, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="Filename" Label="Filename" IsReadOnly="True"
|
||||
TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="Header" Label="Header" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.Header, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="InternalName" Label="Internal Names" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="Multisession" Label="Multisession" IsReadOnly="True" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="LibCryptData" Label="LibCrypt Data" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.LibCryptData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
@@ -355,12 +367,10 @@
|
||||
<controls:UserInput x:Name="PIC" Label="PIC" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.PIC, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="Protection" Label="Protection" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.Protection, Mode=TwoWay}" TextHeight="75" TextWrapping="Wrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="PVD" Label="PVD" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.PVD, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="RingNonZeroDataStart" Label="Ring Non-Zero Data Start" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="SecuROMData" Label="SecuROM Data" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.SecuROMData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
@@ -369,6 +379,7 @@
|
||||
Text="{Binding SubmissionInfo.Extras.SecuritySectorRanges, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="SSVersion" Label="Security Sector Version" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="UniversalHash" Label="Universal Hash (SHA-1)" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="VolumeLabel" Label="Volume Label" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="XeMID" Label="XeMID" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="XMID" Label="XMID" IsReadOnly="True"/>
|
||||
@@ -1,7 +1,8 @@
|
||||
using MPF.GUI.ViewModels;
|
||||
using RedumpLib.Data;
|
||||
using MPF.Core.Data;
|
||||
using MPF.UI.Core.ViewModels;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Windows
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for DiscInformationWindow.xaml
|
||||
@@ -16,10 +17,10 @@ namespace MPF.Windows
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public DiscInformationWindow(SubmissionInfo submissionInfo)
|
||||
public DiscInformationWindow(Options options, SubmissionInfo submissionInfo)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new DiscInformationViewModel(this, submissionInfo);
|
||||
DataContext = new DiscInformationViewModel(this, options, submissionInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
<windows:WindowBase x:Class="MPF.Windows.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UserControls"
|
||||
xmlns:viewModels="clr-namespace:MPF.GUI.ViewModels"
|
||||
xmlns:windows="clr-namespace:MPF.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
|
||||
xmlns:viewModels="clr-namespace:MPF.UI.Core.ViewModels"
|
||||
mc:Ignorable="d"
|
||||
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
@@ -24,7 +24,7 @@
|
||||
<Grid Margin="0,2,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="25"/>
|
||||
<ColumnDefinition Width="115"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
@@ -136,16 +136,13 @@
|
||||
</ComboBox>
|
||||
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="OutputFilenameLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="Output Filename"/>
|
||||
<TextBox x:Name="OutputFilenameTextBox" Grid.Row="1" Grid.Column="1" Height="22" VerticalContentAlignment="Center" />
|
||||
|
||||
<Label x:Name="OutputDirectoryLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Output Directory"/>
|
||||
<TextBox x:Name="OutputDirectoryTextBox" Grid.Row="2" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="OutputDirectoryBrowseButton" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
<Label x:Name="OutputPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="Output Path"/>
|
||||
<TextBox x:Name="OutputPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="OutputPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
Style="{DynamicResource CustomButtonStyle}"/>
|
||||
|
||||
<Label x:Name="DriveLetterLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="Drive Letter"/>
|
||||
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<Label x:Name="DriveLetterLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Drive Letter"/>
|
||||
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="2" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Letter}" />
|
||||
@@ -153,9 +150,12 @@
|
||||
</ComboBox.ItemTemplate>
|
||||
</ComboBox>
|
||||
|
||||
<Label x:Name="DriveSpeedLabel" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Content="Drive Speed"/>
|
||||
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
<Label x:Name="DriveSpeedLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="Drive Speed"/>
|
||||
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="DumpingProgramLabel" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Content="Dumping Program"/>
|
||||
<ComboBox x:Name="DumpingProgramComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="ParametersLabel" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" Content="Parameters"/>
|
||||
<TextBox x:Name="ParametersTextBox" Grid.Row="5" Grid.Column="1" Height="22" Width="370" HorizontalAlignment="Left" IsEnabled="False" VerticalContentAlignment="Center" />
|
||||
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right" IsChecked="False" />
|
||||
@@ -163,11 +163,13 @@
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Controls">
|
||||
<UniformGrid Columns="3" Margin="5,5,5,5">
|
||||
<UniformGrid Columns="4" Margin="5,5,5,5">
|
||||
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True" Content="Start Dumping"
|
||||
IsEnabled="False" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="MediaScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for discs"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="UpdateVolumeLabel" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Update Label"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="CopyProtectScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for protection"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
</UniformGrid>
|
||||
@@ -175,7 +177,7 @@
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
|
||||
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
|
||||
<Label x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Waiting for media..." />
|
||||
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Text="Waiting for media..." />
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
@@ -184,4 +186,4 @@
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</windows:WindowBase>
|
||||
</coreWindows:WindowBase>
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using MPF.GUI.ViewModels;
|
||||
using MPF.UI.Core.ViewModels;
|
||||
|
||||
namespace MPF.Windows
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
public partial class MainWindow : WindowBase
|
||||
{
|
||||
@@ -21,7 +21,7 @@ namespace MPF.Windows
|
||||
protected override void OnContentRendered(EventArgs e)
|
||||
{
|
||||
base.OnContentRendered(e);
|
||||
MainViewModel.Init();
|
||||
MainViewModel.Init(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
<windows:WindowBase x:Class="MPF.Windows.OptionsWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:MPF"
|
||||
xmlns:windows="clr-namespace:MPF.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Options" Width="515.132" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
<coreWindows:WindowBase x:Class="MPF.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:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Options" Width="515.132" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
@@ -70,12 +70,15 @@
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
|
||||
IsChecked="{Binding Options.EnableDarkMode}"
|
||||
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
|
||||
/>
|
||||
|
||||
/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Check for Updates on Startup"
|
||||
IsChecked="{Binding Options.CheckForUpdatesOnStartup}"
|
||||
ToolTip="Check for updates when the application starts" Margin="0,4"
|
||||
/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Fast Update Label"
|
||||
IsChecked="{Binding Options.FastUpdateLabel}"
|
||||
ToolTip="Bypasses disc checks to quickly update the output path. Use with caution!" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
@@ -92,6 +95,7 @@
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Aaru Path" />
|
||||
@@ -104,12 +108,12 @@
|
||||
<Button x:Name="DiscImageCreatorPathButton" Grid.Row="1" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DD Path" />
|
||||
<TextBox x:Name="DDPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DDPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="DDPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Redumper Path" />
|
||||
<TextBox x:Name="RedumperPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.RedumperPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="RedumperPathButton" Grid.Row="2" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Dumping Program" />
|
||||
<Label Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Dumping Program" />
|
||||
<ComboBox x:Name="InternalProgramComboBox" Grid.Row="3" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding InternalPrograms}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
@@ -123,8 +127,8 @@
|
||||
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
|
||||
<UniformGrid Columns="2" Rows="8">
|
||||
<CheckBox VerticalAlignment="Center" Content="Skip Type Detect"
|
||||
<UniformGrid Columns="2" Rows="9">
|
||||
<CheckBox x:Name="SkipMediaTypeDetectionCheckBox" VerticalAlignment="Center" Content="Skip Type Detect"
|
||||
IsChecked="{Binding Options.SkipMediaTypeDetection}"
|
||||
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
@@ -162,6 +166,11 @@
|
||||
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Output Protection File"
|
||||
IsChecked="{Binding Options.OutputSeparateProtectionFile}" IsEnabled="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Output protection information to a separate file" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Eject After Dump"
|
||||
IsChecked="{Binding Options.EjectAfterDump}"
|
||||
ToolTip="Eject the disc from the drive after dumping has completed" Margin="0,4"
|
||||
@@ -173,9 +182,14 @@
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Tab Input"
|
||||
IsChecked="{Binding Options.EnableTabsInInputFields}"
|
||||
ToolTip="Enable entering tabs in supported input fields instead of tab navigation" Margin="0,4"
|
||||
/>
|
||||
IsChecked="{Binding Options.EnableTabsInInputFields}"
|
||||
ToolTip="Enable entering tabs in supported input fields instead of tab navigation" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Redump Compatibility"
|
||||
IsChecked="{Binding Options.EnableRedumpCompatibility}"
|
||||
ToolTip="Enable limiting outputs to only those supported by Redump" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Show Eject Reminder"
|
||||
IsChecked="{Binding Options.ShowDiscEjectReminder}"
|
||||
@@ -193,7 +207,7 @@
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Artifacts"
|
||||
IsChecked="{Binding Options.IncludeArtifacts}" IsEnabled="{Binding Options.OutputSubmissionJSON}"
|
||||
IsChecked="{Binding Options.IncludeArtifacts}" IsEnabled="{Binding Options.OutputSubmissionJSON}"
|
||||
ToolTip="Include log files in serialized JSON data" Margin="0,4"
|
||||
/>
|
||||
|
||||
@@ -216,27 +230,35 @@
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="CD-ROM" />
|
||||
<Label Grid.Row="0" Grid.Column="0" Content="CD" />
|
||||
<Slider x:Name="DumpSpeedCDSlider" Grid.Row="0" Grid.Column="1" Minimum="1" Maximum="72" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static local:Constants.SpeedsForCDAsCollection}}"
|
||||
Ticks="{Binding Source={x:Static core:Constants.SpeedsForCDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedCD}" />
|
||||
<TextBox x:Name="DumpSpeedCDTextBox" Grid.Row="0" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedCDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="DVD-ROM" />
|
||||
<Label Grid.Row="1" Grid.Column="0" Content="DVD" />
|
||||
<Slider x:Name="DumpSpeedDVDSlider" Grid.Row="1" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static local:Constants.SpeedsForDVDAsCollection}}"
|
||||
Ticks="{Binding Source={x:Static core:Constants.SpeedsForDVDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedDVD}" />
|
||||
<TextBox x:Name="DumpSpeedDVDTextBox" Grid.Row="1" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="BD-ROM" />
|
||||
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static local:Constants.SpeedsForBDAsCollection}}"
|
||||
<Label Grid.Row="2" Grid.Column="0" Content="HD-DVD" />
|
||||
<Slider x:Name="DumpSpeedHDDVDSlider" Grid.Row="2" Grid.Column="1" Minimum="1" Maximum="24" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static core:Constants.SpeedsForHDDVDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedHDDVD}" />
|
||||
<TextBox x:Name="DumpSpeedHDDVDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedHDDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<Label Grid.Row="3" Grid.Column="0" Content="BD" />
|
||||
<Slider x:Name="DumpSpeedBDSlider" Grid.Row="3" Grid.Column="1" Minimum="1" Maximum="16" IsSnapToTickEnabled="True" TickPlacement="BottomRight"
|
||||
Ticks="{Binding Source={x:Static core:Constants.SpeedsForBDAsCollection}}"
|
||||
Value="{Binding Options.PreferredDumpSpeedBD}" />
|
||||
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
<TextBox x:Name="DumpSpeedBDTextBox" Grid.Row="3" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" IsReadOnlyCaretVisible="False" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedBDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
@@ -256,11 +278,6 @@
|
||||
ToolTip="Include executable packers in outputted protections" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Force Scanning All Files"
|
||||
IsChecked="{Binding Options.ForceScanningForProtection}"
|
||||
ToolTip="Force scanning all files even if they're not executables" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
|
||||
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
|
||||
ToolTip="Include debug information during protection scans" Margin="0,4"
|
||||
@@ -300,7 +317,7 @@
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="DiscImageCreator" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<UniformGrid Columns="2" Rows="6">
|
||||
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
|
||||
IsChecked="{Binding Options.DICQuietMode}"
|
||||
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
|
||||
@@ -321,10 +338,48 @@
|
||||
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Multi-Sector Read"
|
||||
IsChecked="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Enable the /mr flag for BD drive dumping" Margin="0,4"
|
||||
/>
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
|
||||
<Label Content="Multi-Sector Read Value:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICMultiSectorReadValue}" IsEnabled="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Set the default value for the /mr flag"
|
||||
/>
|
||||
|
||||
<Label Content="CD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on C2 error"
|
||||
ToolTip="Specifies how many rereads are attempted on C2 error [CD only]"
|
||||
/>
|
||||
|
||||
<Label Content="DVD/HD-DVD/BD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICDVDRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error [DVD/HD-DVD/BD only]"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Redumper" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.RedumperEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.RedumperEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.RedumperRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
@@ -340,23 +395,13 @@
|
||||
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
|
||||
ToolTip="Open the log panel when the program launches" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Log Formatting"
|
||||
IsChecked="{Binding Options.EnableLogFormatting}"
|
||||
ToolTip="Format log lines written to the log, including overwriting previous lines on match. Disable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Progress Processing"
|
||||
IsChecked="{Binding Options.EnableProgressProcessing}" IsEnabled="{Binding Options.EnableLogFormatting}"
|
||||
ToolTip="Process lines written to the log to update the progress bar. Disable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Login Info" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump Credentials">
|
||||
<UniformGrid Columns="5" Rows="1">
|
||||
<UniformGrid Columns="5">
|
||||
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Username" />
|
||||
<TextBox x:Name="RedumpUsernameTextBox" Height="22" HorizontalAlignment="Stretch"
|
||||
Text="{Binding Options.RedumpUsername}" />
|
||||
@@ -368,6 +413,12 @@
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<Label>
|
||||
<Label.Content>
|
||||
<TextBlock TextWrapping="Wrap"><Bold Foreground="Red">WARNING:</Bold> If you choose to enable validation and information retrieval, you are responsible for ensuring that all data populated matches your actual media. Some information may be marked to check for validity as a reminder, but all information should be subject to the same scrutiny.</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
@@ -385,4 +436,4 @@
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</windows:WindowBase>
|
||||
</coreWindows:WindowBase>
|
||||
@@ -1,6 +1,7 @@
|
||||
using MPF.GUI.ViewModels;
|
||||
using MPF.Core.Data;
|
||||
using MPF.UI.Core.ViewModels;
|
||||
|
||||
namespace MPF.Windows
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for OptionsWindow.xaml
|
||||
@@ -15,10 +16,16 @@ namespace MPF.Windows
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public OptionsWindow()
|
||||
public OptionsWindow(Options options)
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new OptionsViewModel(this);
|
||||
DataContext = new OptionsViewModel(this, options);
|
||||
|
||||
#if NET6_0_OR_GREATER
|
||||
this.SkipMediaTypeDetectionCheckBox.IsEnabled = false;
|
||||
this.SkipMediaTypeDetectionCheckBox.IsChecked = false;
|
||||
this.SkipMediaTypeDetectionCheckBox.ToolTip = "This feature is not enabled for .NET 6";
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
<windows:WindowBase x:Class="MPF.Windows.RingCodeGuideWindow"
|
||||
<windows:WindowBase x:Class="MPF.UI.Core.Windows.RingCodeGuideWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:MPF"
|
||||
xmlns:controls="clr-namespace:MPF.UserControls"
|
||||
xmlns:windows="clr-namespace:MPF.Windows"
|
||||
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
|
||||
xmlns:windows="clr-namespace:MPF.UI.Core.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Ring Code Guide" Width="500" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
@@ -81,12 +80,12 @@
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Image Grid.Row="0" Source="/Images/ring-code-guide-1-layer.png"
|
||||
<Image Grid.Row="0" Source="../Images/ring-code-guide-1-layer.png"
|
||||
HorizontalAlignment="Center" Stretch="Fill" Width="500" Height="500"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<Label Grid.Row="1">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Red">1. Mastering Ring:</Bold> Sony DADC<tab>A0100368905-0101<tab>15</TextBlock>
|
||||
<TextBlock><Bold Foreground="Red">1. Mastering Ring:</Bold> Sony DADC<tab>A0100368905-0101<tab>13</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="2">
|
||||
@@ -122,7 +121,7 @@
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Image Grid.Row="0" Source="/Images/ring-code-guide-2-layer.png"
|
||||
<Image Grid.Row="0" Source="../Images/ring-code-guide-2-layer.png"
|
||||
HorizontalAlignment="Center" Stretch="Fill" Width="475" Height="500"
|
||||
RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<Label Grid.Row="1">
|
||||
@@ -132,7 +131,7 @@
|
||||
</Label>
|
||||
<Label Grid.Row="2">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Green">2. Inner Mastering Ring:</Bold> IM01501A-L0<tab>03 +<tab>+</TextBlock>
|
||||
<TextBlock><Bold Foreground="Green">2. Inner Mastering Ring:</Bold> IM01501A-L0<tab>01 +<tab>+</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="3">
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF.Windows
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for RingCodeGuideWindow.xaml
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace MPF.Windows
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
public class WindowBase : Window
|
||||
{
|
||||
44
MPF.sln
44
MPF.sln
@@ -1,7 +1,7 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.28803.156
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32407.343
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF", "MPF\MPF.csproj", "{7B1B75EB-8940-466F-BD51-76471A57F9BE}"
|
||||
EndProject
|
||||
@@ -15,19 +15,22 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
appveyor.yml = appveyor.yml
|
||||
CHANGELIST.md = CHANGELIST.md
|
||||
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
|
||||
.github\ISSUE_TEMPLATE\informational.md = .github\ISSUE_TEMPLATE\informational.md
|
||||
.github\ISSUE_TEMPLATE\issue-report.md = .github\ISSUE_TEMPLATE\issue-report.md
|
||||
publish-nix.sh = publish-nix.sh
|
||||
publish-win.bat = publish-win.bat
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RedumpLib", "RedumpLib\RedumpLib.csproj", "{13574913-A426-4644-9955-F49AD0876E5F}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.CueSheets", "MPF.CueSheets\MPF.CueSheets.csproj", "{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CICMMetadataEditor", "CICMMetadata\CICMMetadataEditor\CICMMetadataEditor\CICMMetadataEditor.csproj", "{E4271454-6217-4500-BC36-F8856AC7AD6B}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Core", "MPF.Core\MPF.Core.csproj", "{70B1265D-FE49-472A-A83D-0B462152D37A}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Modules", "MPF.Modules\MPF.Modules.csproj", "{8A4254BD-552F-4238-B8EB-D59AACD768B9}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.UI.Core", "MPF.UI.Core\MPF.UI.Core.csproj", "{EA3768DB-694A-4653-82E4-9FF71B8963F3}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MPF", "MPF", "{4160167D-681D-480B-ABC6-06AC869E5769}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -50,18 +53,6 @@ Global
|
||||
{8CFDE289-E171-4D49-A40D-5293265C1253}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8CFDE289-E171-4D49-A40D-5293265C1253}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8CFDE289-E171-4D49-A40D-5293265C1253}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{13574913-A426-4644-9955-F49AD0876E5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{13574913-A426-4644-9955-F49AD0876E5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{13574913-A426-4644-9955-F49AD0876E5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{13574913-A426-4644-9955-F49AD0876E5F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F2C12798-DF53-4D4C-A55B-F5A77F29D6B1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E4271454-6217-4500-BC36-F8856AC7AD6B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{70B1265D-FE49-472A-A83D-0B462152D37A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{70B1265D-FE49-472A-A83D-0B462152D37A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{70B1265D-FE49-472A-A83D-0B462152D37A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
@@ -70,10 +61,23 @@ Global
|
||||
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A4254BD-552F-4238-B8EB-D59AACD768B9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{7B1B75EB-8940-466F-BD51-76471A57F9BE} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{7CC064D2-38AB-4A05-8519-28660DE4562A} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{51AB0928-13F9-44BF-A407-B6957A43A056} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{8CFDE289-E171-4D49-A40D-5293265C1253} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{70B1265D-FE49-472A-A83D-0B462152D37A} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{8A4254BD-552F-4238-B8EB-D59AACD768B9} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {73C62E6A-6584-4D93-83B5-ECB1FBDB469B}
|
||||
EndGlobalSection
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
|
||||
xmlns:windows="clr-namespace:MPF.Windows"
|
||||
xmlns:windows="clr-namespace:MPF.UI.Core.Windows;assembly=MPF.UI.Core"
|
||||
x:Class="MPF.App">
|
||||
<Application.MainWindow>
|
||||
<windows:MainWindow Visibility="Visible"/>
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using System.Windows;
|
||||
using MPF.Core.Data;
|
||||
using MPF.GUI.ViewModels;
|
||||
using MPF.Windows;
|
||||
|
||||
namespace MPF
|
||||
{
|
||||
@@ -15,46 +12,5 @@ namespace MPF
|
||||
/// </remarks>
|
||||
public partial class App : Application
|
||||
{
|
||||
/// <summary>
|
||||
/// Static application instance for reference
|
||||
/// </summary>
|
||||
private static App _appInstance;
|
||||
|
||||
/// <summary>
|
||||
/// Read-only access to the current main window
|
||||
/// </summary>
|
||||
public static MainWindow Instance => _appInstance.MainWindow as MainWindow;
|
||||
|
||||
/// <summary>
|
||||
/// Read-only access to the current log window
|
||||
/// </summary>
|
||||
public static LogViewModel Logger => Instance.LogOutput.LogViewModel;
|
||||
|
||||
/// <summary>
|
||||
/// Access to the current options
|
||||
/// </summary>
|
||||
public static Options Options
|
||||
{
|
||||
get => _options;
|
||||
set
|
||||
{
|
||||
_options = value;
|
||||
OptionsLoader.SaveToConfig(_options);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal reference to Options
|
||||
/// </summary>
|
||||
private static Options _options;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public App()
|
||||
{
|
||||
_appInstance = this;
|
||||
_options = OptionsLoader.LoadFromConfig();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Anything marked as internal can be used by the test methods
|
||||
[assembly: InternalsVisibleTo("MPF.Test")]
|
||||
@@ -1,8 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;netcoreapp3.1;net5.0-windows</TargetFrameworks>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win7-x64;win8-x64;win81-x64;win10-x64</RuntimeIdentifiers>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
@@ -11,13 +11,12 @@
|
||||
<AssemblyName>MPF</AssemblyName>
|
||||
<Description>Frontend for various dumping programs</Description>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2021</Copyright>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<Version>2.3</Version>
|
||||
<AssemblyVersion>$(Version)</AssemblyVersion>
|
||||
<FileVersion>$(Version)</FileVersion>
|
||||
<Version>2.6.5</Version>
|
||||
<IncludeSource>true</IncludeSource>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<InternalsVisibleTo>MPF.Test</InternalsVisibleTo>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
@@ -27,11 +26,26 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="1.8.0" GeneratePathProperty="true">
|
||||
<Resource Include="Images\Icon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
|
||||
<ProjectReference Include="..\MPF.UI.Core\MPF.UI.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationFramework.Aero" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BurnOutSharp" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="2.8.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0-rc.1.21451.13" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.1">
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
@@ -41,27 +55,4 @@
|
||||
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Images\ring-code-guide-2-layer.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Icon.ico" />
|
||||
<Resource Include="Images\ring-code-guide-2-layer.png" />
|
||||
<Resource Include="Images\ring-code-guide-1-layer.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj">
|
||||
<Project>{51ab0928-13f9-44bf-a407-b6957a43a056}</Project>
|
||||
<Name>MPF.Library</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\RedumpLib\RedumpLib.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="PresentationFramework.Aero" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,44 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using MPF.Core.Data;
|
||||
|
||||
namespace MPF
|
||||
{
|
||||
public static class OptionsLoader
|
||||
{
|
||||
/// <summary>
|
||||
/// Load the current set of options from the application configuration
|
||||
/// </summary>
|
||||
public static Options LoadFromConfig()
|
||||
{
|
||||
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||||
|
||||
var settings = configFile.AppSettings.Settings;
|
||||
var dict = new Dictionary<string, string>();
|
||||
|
||||
foreach (string key in settings.AllKeys)
|
||||
{
|
||||
dict[key] = settings[key]?.Value ?? string.Empty;
|
||||
}
|
||||
|
||||
return new Options(dict);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current set of options to the application configuration
|
||||
/// </summary>
|
||||
public static void SaveToConfig(Options options)
|
||||
{
|
||||
Configuration configFile = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
|
||||
|
||||
// Loop through all settings in Options and save them, overwriting existing settings
|
||||
foreach (var kvp in options)
|
||||
{
|
||||
configFile.AppSettings.Settings.Remove(kvp.Key);
|
||||
configFile.AppSettings.Settings.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
configFile.Save(ConfigurationSaveMode.Modified);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,577 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using MPF.Core.Data;
|
||||
using MPF.UserControls;
|
||||
|
||||
namespace MPF.GUI.ViewModels
|
||||
{
|
||||
public class LogViewModel
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Parent OptionsWindow object
|
||||
/// </summary>
|
||||
public LogOutput Parent { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private State Variables
|
||||
|
||||
/// <summary>
|
||||
/// Paragraph backing the log
|
||||
/// </summary>
|
||||
private readonly Paragraph _paragraph;
|
||||
|
||||
/// <summary>
|
||||
/// Cached value of the last line written
|
||||
/// </summary>
|
||||
private Run lastLine = null;
|
||||
|
||||
/// <summary>
|
||||
/// Queue of items that need to be logged
|
||||
/// </summary>
|
||||
private readonly ProcessingQueue<LogLine> logQueue;
|
||||
|
||||
/// <summary>
|
||||
/// List of Matchers for progress tracking
|
||||
/// </summary>
|
||||
private readonly List<Matcher?> _matchers;
|
||||
|
||||
/// <summary>
|
||||
/// Cached value of the last matcher used
|
||||
/// </summary>
|
||||
private Matcher? lastUsedMatcher = null;
|
||||
|
||||
/// <summary>
|
||||
/// Regex pattern to find DiscImageCreator progress messages
|
||||
/// </summary>
|
||||
private const string DiscImageCreatorProgressPattern = @"\s*(\d+)\/\s*(\d+)$";
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public LogViewModel(LogOutput parent)
|
||||
{
|
||||
Parent = parent;
|
||||
|
||||
// Add handlers
|
||||
Parent.OutputViewer.SizeChanged += OutputViewerSizeChanged;
|
||||
Parent.Output.TextChanged += OnTextChanged;
|
||||
Parent.ClearButton.Click += OnClearButton;
|
||||
Parent.SaveButton.Click += OnSaveButton;
|
||||
|
||||
// Update the internal state
|
||||
var document = new FlowDocument()
|
||||
{
|
||||
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
|
||||
};
|
||||
_paragraph = new Paragraph();
|
||||
document.Blocks.Add(_paragraph);
|
||||
Parent.Output.Document = document;
|
||||
|
||||
// TODO: Can we dynamically add matchers *only* during dumping?
|
||||
_matchers = new List<Matcher?>();
|
||||
AddAaruMatchers();
|
||||
AddDiscImageCreatorMatchers();
|
||||
|
||||
logQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
|
||||
}
|
||||
|
||||
#region Matching
|
||||
|
||||
/// <summary>
|
||||
/// Matching wrapper
|
||||
/// </summary>
|
||||
private struct Matcher
|
||||
{
|
||||
private readonly string prefix;
|
||||
private readonly Regex regex;
|
||||
private readonly int start;
|
||||
private readonly string progressBarText;
|
||||
private readonly Action<Match, string> lambda;
|
||||
|
||||
public Matcher(string prefix, string regex, string progressBarText, Action<Match, string> lambda)
|
||||
{
|
||||
this.prefix = prefix;
|
||||
this.regex = new Regex(regex);
|
||||
this.start = prefix.Length;
|
||||
this.progressBarText = progressBarText;
|
||||
this.lambda = lambda;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the text matches the prefix
|
||||
/// </summary>
|
||||
/// <param name="text">Text to check</param>
|
||||
/// <returns>True if the line starts with the prefix, false otherwise</returns>
|
||||
public bool Matches(string text) => text.StartsWith(prefix);
|
||||
|
||||
/// <summary>
|
||||
/// Generate a Match and apply the lambda
|
||||
/// </summary>
|
||||
/// <param name="text">Text to match and apply from</param>
|
||||
public void Apply(string text)
|
||||
{
|
||||
Match match = regex?.Match(text, start);
|
||||
lambda?.Invoke(match, progressBarText);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add all Matchers for Aaru
|
||||
/// </summary>
|
||||
private void AddAaruMatchers()
|
||||
{
|
||||
// TODO: Determine matchers that can be added
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add all Matchers for DiscImageCreator
|
||||
/// </summary>
|
||||
private void AddDiscImageCreatorMatchers()
|
||||
{
|
||||
#region Pre-dump Checking
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Checking EXE",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Checking executables...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Checking Pregap sync, msf, mode (LBA)",
|
||||
@"\s*-(\d+)$",
|
||||
"Checking Pregap sync, msf, mode",
|
||||
(match, text) =>
|
||||
{
|
||||
Parent.ProgressBar.Value = 0;
|
||||
Parent.ProgressLabel.Text = text;
|
||||
}));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Checking SubQ adr (Track)",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Checking SubQ adr...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Checking SubQ ctl (Track)",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Checking SubQ ctl...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Checking SubRtoW (Track)",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Checking SubRtoW...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Reading DirectoryRecord",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Reading directory records...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Scanning sector for anti-mod string (LBA)",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Scanning sectors for anti-mod string...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dumping
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
@"Creating iso(LBA)",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Creating ISO...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
@"Creating .scm (LBA)",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Creating scrambled image...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Post-Dump Processing
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Checking sectors:",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Checking for errors...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Creating bin (Track)",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Creating BIN(s)...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Creating cue and ccd (Track)",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Creating CUE and CCD...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Descrambling data sector of img:",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Descrambling image...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
_matchers.Add(new Matcher(
|
||||
"Scanning sector (LBA)",
|
||||
DiscImageCreatorProgressPattern,
|
||||
"Scanning sectors for protection...",
|
||||
StandardDiscImageCreatorProgress
|
||||
));
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Logging
|
||||
|
||||
/// <summary>
|
||||
/// Log level for output
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
USER,
|
||||
VERBOSE,
|
||||
ERROR,
|
||||
SECRET,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log line wrapper
|
||||
/// </summary>
|
||||
private struct LogLine
|
||||
{
|
||||
public readonly string Text;
|
||||
public readonly LogLevel LogLevel;
|
||||
|
||||
public LogLine(string text, LogLevel logLevel)
|
||||
{
|
||||
this.Text = text;
|
||||
this.LogLevel = logLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the foreground Brush for the current LogLevel
|
||||
/// </summary>
|
||||
/// <returns>Brush representing the color</returns>
|
||||
public Brush GetForegroundColor()
|
||||
{
|
||||
switch (this.LogLevel)
|
||||
{
|
||||
case LogLevel.SECRET:
|
||||
return Brushes.Blue;
|
||||
case LogLevel.ERROR:
|
||||
return Brushes.Red;
|
||||
case LogLevel.VERBOSE:
|
||||
return Brushes.Yellow;
|
||||
case LogLevel.USER:
|
||||
default:
|
||||
return Brushes.White;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a Run object from the current LogLine
|
||||
/// </summary>
|
||||
/// <returns>Run object based on internal values</returns>
|
||||
public Run GenerateRun()
|
||||
{
|
||||
return new Run { Text = this.Text, Foreground = GetForegroundColor() };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue text to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void Log(string text) => LogInternal(text);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue text with a newline to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void LogLn(string text) => Log(text + "\n");
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue error text to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void ErrorLog(string text) => LogInternal(text, LogViewModel.LogLevel.ERROR);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue error text with a newline to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void ErrorLogLn(string text) => ErrorLog(text + "\n");
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue secret text to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void SecretLog(string text) => LogInternal(text, LogViewModel.LogLevel.SECRET);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue secret text with a newline to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void SecretLogLn(string text) => SecretLog(text + "\n");
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue verbose text to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void VerboseLog(string text) => LogInternal(text, LogViewModel.LogLevel.VERBOSE);
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue verbose text with a newline to the log
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void VerboseLogLn(string text) => VerboseLog(text + "\n");
|
||||
|
||||
/// <summary>
|
||||
/// Reset the progress bar state
|
||||
/// </summary>
|
||||
public void ResetProgressBar()
|
||||
{
|
||||
Parent.Dispatcher.Invoke(() =>
|
||||
{
|
||||
Parent.ProgressBar.Value = 0;
|
||||
Parent.ProgressLabel.Text = string.Empty;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue text to the log with formatting
|
||||
/// </summary>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
/// <param name="logLevel">LogLevel for the log, defaults to USER</param>
|
||||
private void LogInternal(string text, LogLevel logLevel = LogLevel.USER)
|
||||
{
|
||||
// Null text gets ignored
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
// If we have verbose logs but not enabled, ignore
|
||||
if (logLevel == LogLevel.VERBOSE && !App.Options.VerboseLogging)
|
||||
return;
|
||||
|
||||
// Enqueue the text
|
||||
logQueue.Enqueue(new LogLine(text, logLevel));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the log lines in the queue
|
||||
/// </summary>
|
||||
/// <param name="nextLogLine">LogLine item to process</param>
|
||||
private void ProcessLogLine(LogLine nextLogLine)
|
||||
{
|
||||
// Null text gets ignored
|
||||
string nextText = Parent.Dispatcher.Invoke(() => nextLogLine.Text);
|
||||
if (nextText == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// If we're not processing log formatting, just append and continue
|
||||
if (!App.Options.EnableLogFormatting)
|
||||
{
|
||||
if (nextText.StartsWith("\r"))
|
||||
ReplaceLastLine(nextLogLine);
|
||||
else
|
||||
AppendToTextBox(nextLogLine);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get last line
|
||||
lastLine = lastLine ?? GetLastLine();
|
||||
|
||||
// Always append if there's no previous line
|
||||
if (lastLine == null)
|
||||
{
|
||||
AppendToTextBox(nextLogLine);
|
||||
lastUsedMatcher = _matchers.FirstOrDefault(m => m?.Matches(nextText) == true);
|
||||
}
|
||||
// Return always means overwrite
|
||||
else if (nextText.StartsWith("\r"))
|
||||
{
|
||||
ReplaceLastLine(nextLogLine);
|
||||
lastUsedMatcher = _matchers.FirstOrDefault(m => m?.Matches(nextText.TrimStart('\r')) == true);
|
||||
}
|
||||
// If we have a cached matcher and we match
|
||||
else if (lastUsedMatcher?.Matches(nextText) == true)
|
||||
{
|
||||
ReplaceLastLine(nextLogLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Get the first matching Matcher
|
||||
var firstMatcher = _matchers.FirstOrDefault(m => m?.Matches(nextText) == true);
|
||||
if (firstMatcher.HasValue)
|
||||
{
|
||||
string lastText = Parent.Dispatcher.Invoke(() => { return lastLine.Text; });
|
||||
if (firstMatcher.Value.Matches(lastText))
|
||||
ReplaceLastLine(nextLogLine);
|
||||
else if (string.IsNullOrWhiteSpace(lastText))
|
||||
ReplaceLastLine(nextLogLine);
|
||||
else
|
||||
AppendToTextBox(nextLogLine);
|
||||
|
||||
// Cache the last used Matcher
|
||||
lastUsedMatcher = firstMatcher;
|
||||
}
|
||||
// Default case for all other text
|
||||
else
|
||||
{
|
||||
AppendToTextBox(nextLogLine);
|
||||
lastUsedMatcher = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the bar if needed
|
||||
if (App.Options.EnableProgressProcessing)
|
||||
ProcessStringForProgressBar(nextText, lastUsedMatcher);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// In the event that something fails horribly, we want to log
|
||||
AppendToTextBox(new LogLine(ex.ToString(), LogLevel.ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append log line to the log text box
|
||||
/// </summary>
|
||||
/// <param name="logLine">LogLine value to append</param>
|
||||
private void AppendToTextBox(LogLine logLine)
|
||||
{
|
||||
Parent.Dispatcher.Invoke(() =>
|
||||
{
|
||||
var run = logLine.GenerateRun();
|
||||
_paragraph.Inlines.Add(run);
|
||||
lastLine = run;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the last line written to the log text box
|
||||
/// </summary>
|
||||
private Run GetLastLine()
|
||||
{
|
||||
return Parent.Dispatcher.Invoke(() =>
|
||||
{
|
||||
if (!_paragraph.Inlines.Any())
|
||||
return null;
|
||||
|
||||
return _paragraph.Inlines.LastInline as Run;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process text if it should update the progress bar
|
||||
/// </summary>
|
||||
/// <param name="text">Text to check and update with</param>
|
||||
private void ProcessStringForProgressBar(string text, Matcher? matcher)
|
||||
{
|
||||
Parent.Dispatcher.Invoke(() => { matcher?.Apply(text); });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace the last line written to the log text box
|
||||
/// </summary>
|
||||
/// <param name="logLine">LogLine value to append</param>
|
||||
private void ReplaceLastLine(LogLine logLine)
|
||||
{
|
||||
Parent.Dispatcher.Invoke(() =>
|
||||
{
|
||||
lastLine.Text = logLine.Text;
|
||||
lastLine.Foreground = logLine.GetForegroundColor();
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Scroll the current view to the bottom
|
||||
/// </summary>
|
||||
public void ScrollToBottom()
|
||||
{
|
||||
Parent.OutputViewer.ScrollToBottom();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EventHandlers
|
||||
|
||||
private void OnClearButton(object sender, EventArgs e)
|
||||
{
|
||||
_paragraph.Inlines.Clear();
|
||||
ResetProgressBar();
|
||||
}
|
||||
|
||||
private void OnSaveButton(object sender, EventArgs e)
|
||||
{
|
||||
using (StreamWriter tw = new StreamWriter(File.OpenWrite("console.log")))
|
||||
{
|
||||
foreach (var inline in _paragraph.Inlines)
|
||||
{
|
||||
if (inline is Run run)
|
||||
tw.Write(run.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnTextChanged(object sender, TextChangedEventArgs e) =>
|
||||
ScrollToBottom();
|
||||
|
||||
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e) =>
|
||||
ScrollToBottom();
|
||||
|
||||
private void StandardDiscImageCreatorProgress(Match match, string text)
|
||||
{
|
||||
if (uint.TryParse(match.Groups[1].Value, out uint current) && uint.TryParse(match.Groups[2].Value, out uint total))
|
||||
{
|
||||
float percentProgress = (current / (float)total) * 100;
|
||||
Parent.ProgressBar.Value = percentProgress;
|
||||
Parent.ProgressLabel.Text = string.Format($"{text} ({percentProgress:N2}%)");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
107
README.md
107
README.md
@@ -1,6 +1,6 @@
|
||||
# Media Preservation Frontend (MPF)
|
||||
|
||||
DiscImageCreator/Aaru UI in C#
|
||||
Redumper/Aaru/DiscImageCreator UI in C#
|
||||
|
||||
[](https://ci.appveyor.com/project/mnadareski/MPF/build/artifacts)
|
||||
|
||||
@@ -13,17 +13,78 @@ For those who would rather use the most recent stable build, download the latest
|
||||
|
||||
For those who like to test the newest features, download the latest AppVeyor WIP build here: [AppVeyor](https://ci.appveyor.com/project/mnadareski/MPF/build/artifacts)
|
||||
|
||||
## System Requirements
|
||||
## Media Preservation Frontend (MPF)
|
||||
|
||||
Even though this is written in C#, this program can only be used on Windows systems due to one of the base programs, DiscImageCreator, being Windows-only. There is some preliminary support for Linux underway, and we will try to integrate with that when the time comes.
|
||||
MPF is the main, UI-centric application of the MPF suite. This program allows users to use Redumper, Aaru, DiscImageCreator, or dd for Windows in a more user-friendly way. Each backend dumping program is supported as fully as possible to ensure that all information is captured on output. There are many customization options and quality of life settings that can be access through the Options menu.
|
||||
|
||||
- Windows 7 (newest version of Windows recommended) or Mono-compatible Linux environment (MPF.Check only)
|
||||
- .NET Framework 4.8 or .NET Core 3.1 Runtimes (.NET Core 3.1 is mostly functional due to a dependency issues but may be unstable in some situations)
|
||||
- 1 GB of free RAM
|
||||
### System Requirements
|
||||
|
||||
- Windows 8.1 (x86 or x64) or newer
|
||||
- Users who wish to use MPF on Windows 7 need to disable strong name validation due to `Microsoft.Management.Infrastructure` being unsigned. Add the following registry keys (accurate at time of writing):
|
||||
```
|
||||
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\StrongName\Verification\*,31bf3856ad364e35]
|
||||
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\StrongName\Verification\*,31bf3856ad364e35]
|
||||
```
|
||||
- Alternatively, look at this [StackOverflow question](https://stackoverflow.com/questions/403731/strong-name-validation-failed) for more information.
|
||||
|
||||
- .NET Framework 4.8 or .NET 6.0 Runtimes
|
||||
- As much hard drive space as the amount of discs you will be dumping (20+ GB recommended)
|
||||
|
||||
Ensure that your operating system is as up-to-date as possible, since some features may rely on those updates.
|
||||
|
||||
### Support Limitations
|
||||
|
||||
.NET 6 has some known limitations that are documented in code and in some prior support tickets:
|
||||
|
||||
- Windows-only due to reliance on WPF and Winforms
|
||||
- MAUI is not a viable alternative due to lack of out-of-box support for Linux
|
||||
- Avalonia is being heavily considered
|
||||
- No media type detection due to lack of alternatives to IMAPI2
|
||||
|
||||
### Build Instructions
|
||||
|
||||
To build for .NET Framework 4.8 (Windows only), ensure that the .NET Framework 4.8 SDK is installed and included in your PATH. Then, run the following commands from command prompt, Powershell, or Terminal:
|
||||
|
||||
```
|
||||
dotnet restore
|
||||
msbuild MPF\MPF.csproj -property:TargetFramework=net48 -property:RuntimeIdentifiers=win7-x64
|
||||
```
|
||||
|
||||
To build for .NET 6.0 (Windows only), ensure that the .NET 6.0 SDK (or later) is installed and included in your PATH. Then, run the following commands from command prompt, Powershell, or Terminal:
|
||||
|
||||
```
|
||||
dotnet build MPF\MPF.csproj --framework net6.0-windows --runtime [win7-x64|win8-x64|win81-x64|win10-x64]
|
||||
```
|
||||
|
||||
Choose one of `[win7-x64|win8-x64|win81-x64|win10-x64]` depending on the machine you are targeting. `win10-x64` also includes Windows 11.
|
||||
|
||||
|
||||
## Media Preservation Frontend Checker (MPF.Check)
|
||||
|
||||
MPF.Check is a commandline-only program that allows users to generate submission information from their personal rips. This program supports the outputs from Redumper, Aaru, DiscImageCreator, dd for Windows, Cleanrip, and UmdImageCreator. Running this program without any parameters will display the help text, including all supported parameters.
|
||||
|
||||
### System Requirements
|
||||
|
||||
- Windows 8.1 (x86 or x64) or newer, GNU/Linux x64, or OSX x64
|
||||
- .NET Framework 4.8 (Windows or `mono` only) or .NET 6.0 Runtimes
|
||||
|
||||
### Build Instructions
|
||||
|
||||
To build for .NET Framework 4.8 (Windows only), ensure that the .NET Framework 4.8 SDK is installed and included in your PATH. Then, run the following commands from command prompt, Powershell, or Terminal:
|
||||
|
||||
```
|
||||
dotnet restore
|
||||
msbuild MPF.Check\MPF.Check.csproj -property:TargetFramework=net48 -property:RuntimeIdentifiers=win7-x64
|
||||
```
|
||||
|
||||
To build for .NET 6.0 (All supported OSes), ensure that the .NET 6.0 SDK (or later) is installed and included in your PATH. Then, run the following commands from command prompt, Powershell, Terminal, or shell:
|
||||
|
||||
```
|
||||
dotnet build MPF.Check\MPF.Check.csproj --framework net6.0 --runtime [win7-x64|win8-x64|win81-x64|win10-x64|linux-x64|osx-x64]
|
||||
```
|
||||
|
||||
Choose one of `[win7-x64|win8-x64|win81-x64|win10-x64|linux-x64|osx-x64]` depending on the machine you are targeting. `win10-x64` also includes Windows 11.
|
||||
|
||||
## Information
|
||||
|
||||
For all additional information, including information about the individual components included in the project and what dumping programs are supported, please see [the wiki](https://github.com/SabreTools/MPF/wiki) for more details.
|
||||
@@ -37,33 +98,43 @@ A list of all changes in each stable release and current WIP builds can now be f
|
||||
MPF uses some external libraries to assist with additional information gathering after the dumping process.
|
||||
|
||||
- **BurnOutSharp** - Protection scanning - [GitHub](https://github.com/mnadareski/BurnOutSharp)
|
||||
- **WPFCustomMessageBox.thabse** - Custom message boxes in UI - [GitHub](https://github.com/thabse/WPFCustomMessageBox)
|
||||
- **UnshieldSharp** - Protection scanning - [GitHub](https://github.com/mnadareski/UnshieldSharp)
|
||||
- **WPFCustomMessageBox.thabse** - Custom message boxes in UI - [GitHub](https://github.com/thabse/WPFCustomMessageBox)
|
||||
|
||||
## Contributors
|
||||
|
||||
Here are the talented people who have contributed to the project so far:
|
||||
Here are the talented people who have contributed to the project so far in ways that GitHub doesn't like to track:
|
||||
|
||||
- **darksabre76** - Project Lead / Backend Design / UI Maintenence
|
||||
- **ReignStumble** - Former Project Lead / UI Design
|
||||
- **Jakz** - Primary Feature Contributor
|
||||
- **NHellFire** - Feature Contributor
|
||||
- **Shadów** - UI Support
|
||||
|
||||
For all others who have contributed in some way, please see [here](https://github.com/SabreTools/MPF/graphs/contributors).
|
||||
|
||||
## Notable Testers
|
||||
|
||||
These are the tireless individuals who have dedicated countless hours to help test the many features of MPF and have worked with the development team closely:
|
||||
|
||||
- **Dizzzy/user7** - Additonal thanks for the original concept
|
||||
- [**ajshell1**](https://github.com/ajshell1)
|
||||
- [**Billy**](https://github.com/InternalLoss)
|
||||
- [**David 'Foxhack' Silva**](https://github.com/FoxhackDN)
|
||||
- [**ehw**](https://github.com/ehw)
|
||||
- [**fuzzball**](https://github.com/fuzz6001)
|
||||
- [**Gameboi64**](https://github.com/gameboi64)
|
||||
- [**Intothisworld**](https://github.com/Intothisworld)
|
||||
- [**John Veness**](https://github.com/JohnVeness)
|
||||
- **Kludge**
|
||||
- **ajshell1**
|
||||
- **Whovian**
|
||||
- **Gameboi64**
|
||||
- **silasqwerty**
|
||||
- [**Matt Sephton**](https://github.com/gingerbeardman)
|
||||
- [**NightsoN Blaze**](https://github.com/nightson)
|
||||
- [**NovaSAurora**](https://github.com/NovaSAurora)
|
||||
- [**Seventy7**](https://github.com/7Seventy7) - Additonal thanks for the original concept
|
||||
- [**Silent**](https://github.com/CookiePLMonster)
|
||||
- [**Terry Janas**](https://github.com/tjanas)
|
||||
- [**TheRogueArchivist**](https://github.com/TheRogueArchivist)
|
||||
- [**Whovian9369**](https://github.com/Whovian9369)
|
||||
|
||||
## Community Shout-Outs
|
||||
|
||||
Thanks to these communities for their use, testing, and feedback. I can't even hope to be able to thank everyone individually.
|
||||
|
||||
- **VGPC Discord** - Fast feedback and a lot of testing
|
||||
- **Redump Community** - Near-daily use to assist with metadata gathering
|
||||
- [**VGPC Discord**](https://discord.gg/AHTfxQV) - Fast feedback and a lot of testing
|
||||
- [**Redump Community**](http://redump.org/) - Near-daily use to assist with metadata gathering
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace RedumpLib.Attributes
|
||||
{
|
||||
public static class AttributeHelper<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the HumanReadableAttribute from a supported value
|
||||
/// </summary>
|
||||
/// <param name="value">Value to use</param>
|
||||
/// <returns>HumanReadableAttribute attached to the value</returns>
|
||||
public static HumanReadableAttribute GetAttribute(T value)
|
||||
{
|
||||
// Null value in, null value out
|
||||
if (value == null)
|
||||
return null;
|
||||
|
||||
// Current enumeration type
|
||||
var enumType = typeof(T);
|
||||
if (Nullable.GetUnderlyingType(enumType) != null)
|
||||
enumType = Nullable.GetUnderlyingType(enumType);
|
||||
|
||||
// If the value returns a null on ToString, just return null
|
||||
string valueStr = value.ToString();
|
||||
if (string.IsNullOrWhiteSpace(valueStr))
|
||||
return null;
|
||||
|
||||
// Get the member info array
|
||||
var memberInfos = enumType?.GetMember(valueStr);
|
||||
if (memberInfos == null)
|
||||
return null;
|
||||
|
||||
// Get the enum value info from the array, if possible
|
||||
var enumValueMemberInfo = memberInfos.FirstOrDefault(m => m.DeclaringType == enumType);
|
||||
if (enumValueMemberInfo == null)
|
||||
return null;
|
||||
|
||||
// Try to get the relevant attribute
|
||||
var attributes = enumValueMemberInfo.GetCustomAttributes(typeof(HumanReadableAttribute), true);
|
||||
if (attributes == null)
|
||||
return null;
|
||||
|
||||
// Return the first attribute, if possible
|
||||
return (HumanReadableAttribute)attributes.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user