mirror of
https://github.com/SabreTools/MPF.git
synced 2026-02-04 05:35:52 +00:00
Compare commits
361 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccf2166b72 | ||
|
|
024394bbec | ||
|
|
301a0cb188 | ||
|
|
64231da666 | ||
|
|
5f56977021 | ||
|
|
436ccf7a34 | ||
|
|
ef7510804e | ||
|
|
8c61b87954 | ||
|
|
17ba117949 | ||
|
|
0737ba7641 | ||
|
|
e9dba0767e | ||
|
|
2d142e9e9d | ||
|
|
7a928decff | ||
|
|
eb5409bdee | ||
|
|
1578193068 | ||
|
|
131c95e6ef | ||
|
|
a7790a271f | ||
|
|
1b342d56ef | ||
|
|
a500211129 | ||
|
|
4d798fa547 | ||
|
|
597ebdc973 | ||
|
|
c6a8a9265f | ||
|
|
393c53769d | ||
|
|
fa21999d3f | ||
|
|
dafbb05b16 | ||
|
|
1c1b23a84b | ||
|
|
fd0fe4912d | ||
|
|
b5b54d13a2 | ||
|
|
da77987db3 | ||
|
|
774f44c8ce | ||
|
|
251b3754e4 | ||
|
|
963acc3336 | ||
|
|
90588a0f8b | ||
|
|
a56c212488 | ||
|
|
6484ab5fe0 | ||
|
|
1ff48258b8 | ||
|
|
81019f9d56 | ||
|
|
d47c435236 | ||
|
|
d59b114cba | ||
|
|
7f26dcba4e | ||
|
|
5e2766f982 | ||
|
|
c883f899bb | ||
|
|
8c9950d5fa | ||
|
|
3e842af273 | ||
|
|
b837623da2 | ||
|
|
6742901243 | ||
|
|
d6460a2b68 | ||
|
|
7af59dacc6 | ||
|
|
fc3ef36fef | ||
|
|
6298487346 | ||
|
|
727d9844d5 | ||
|
|
72e7619e2d | ||
|
|
24b4647037 | ||
|
|
713b3f0557 | ||
|
|
f796a9b131 | ||
|
|
2cdf92bf92 | ||
|
|
ccc1687f1a | ||
|
|
6057ec3a59 | ||
|
|
2a5e736285 | ||
|
|
010ef9016b | ||
|
|
02606318b0 | ||
|
|
d4f641b122 | ||
|
|
a1dd6e2d21 | ||
|
|
d35679d688 | ||
|
|
83f5083ce7 | ||
|
|
5b6457f4b7 | ||
|
|
c6517d526b | ||
|
|
e35f1fc2ec | ||
|
|
14f4128d4a | ||
|
|
5465252dc7 | ||
|
|
2573b47a79 | ||
|
|
fe20905524 | ||
|
|
88f19180a4 | ||
|
|
de89968a1d | ||
|
|
8fc53c91b0 | ||
|
|
1a1fbd4b40 | ||
|
|
cac6c3049b | ||
|
|
6a6871e922 | ||
|
|
4a02a3efac | ||
|
|
f6eb961af4 | ||
|
|
faeaaef02a | ||
|
|
ebf393e634 | ||
|
|
3fbd4ea719 | ||
|
|
d09ff6cf1c | ||
|
|
1dc0d57d47 | ||
|
|
a748bd4d3a | ||
|
|
35dec7fe57 | ||
|
|
c22d16349a | ||
|
|
0d77a8950c | ||
|
|
285e94ca69 | ||
|
|
747ac4ea3b | ||
|
|
405ae7c7e4 | ||
|
|
f5ebe968c0 | ||
|
|
06a61b17cb | ||
|
|
9e8e4f6e36 | ||
|
|
fa72211b57 | ||
|
|
d5f66000a9 | ||
|
|
a52ba0aa7a | ||
|
|
eb045928f9 | ||
|
|
440302495b | ||
|
|
0732e9db78 | ||
|
|
a167652b2b | ||
|
|
cfa07c1918 | ||
|
|
53b31f91cf | ||
|
|
01cbd2cff5 | ||
|
|
65ad629ee0 | ||
|
|
06adbde715 | ||
|
|
1e5000bd8a | ||
|
|
8cb0b37e80 | ||
|
|
32c12e1332 | ||
|
|
09b307aa24 | ||
|
|
a5a8fbbf51 | ||
|
|
b366d236c8 | ||
|
|
a833e926f3 | ||
|
|
950be07bf0 | ||
|
|
4c5c1417e9 | ||
|
|
6fdc3412e0 | ||
|
|
807b0c5f9e | ||
|
|
9e0b64a1d1 | ||
|
|
8cfbf2d9f1 | ||
|
|
0064737130 | ||
|
|
292e3999c5 | ||
|
|
5ed1e94d84 | ||
|
|
5b094f57cb | ||
|
|
8066b5541e | ||
|
|
921d0207c2 | ||
|
|
4374ff7f74 | ||
|
|
0be5825b5e | ||
|
|
14c630bea7 | ||
|
|
9a66c685fd | ||
|
|
5e0fa1ad47 | ||
|
|
79065dcc69 | ||
|
|
3d7355aee1 | ||
|
|
6e9a6724c3 | ||
|
|
be35acfb48 | ||
|
|
f1a46c2e82 | ||
|
|
872959c889 | ||
|
|
b848a401f8 | ||
|
|
ee4762f8b3 | ||
|
|
d68bcfb96a | ||
|
|
d2433e4749 | ||
|
|
56ec0e7057 | ||
|
|
26e5d33d17 | ||
|
|
8b8b51ace4 | ||
|
|
f350904441 | ||
|
|
8e8e3368d0 | ||
|
|
4d8153dba1 | ||
|
|
e4e4b5ecde | ||
|
|
8373a6b8f5 | ||
|
|
45c51ebc80 | ||
|
|
af27085cc1 | ||
|
|
82e3707dce | ||
|
|
85192e8d3e | ||
|
|
6f784a352e | ||
|
|
ee707cf1af | ||
|
|
a14c998b3b | ||
|
|
004208df6a | ||
|
|
2f765146d1 | ||
|
|
a7d548f7ce | ||
|
|
fbdb9875f3 | ||
|
|
39a524e3cc | ||
|
|
9740ca3a7a | ||
|
|
f8d81972bf | ||
|
|
fe9302a553 | ||
|
|
c0ed7a7a0e | ||
|
|
b0b48743ac | ||
|
|
47e79dab31 | ||
|
|
90edc42fdf | ||
|
|
45db365705 | ||
|
|
952828dddd | ||
|
|
4a1e953ffd | ||
|
|
25740c2936 | ||
|
|
3696257940 | ||
|
|
4d46b7db5c | ||
|
|
21f5f29ac0 | ||
|
|
436e198826 | ||
|
|
91eb8c96c3 | ||
|
|
edf0bcb4fe | ||
|
|
1526714ab9 | ||
|
|
767e0bb05b | ||
|
|
52e781329c | ||
|
|
0c1395d3ec | ||
|
|
791d9cdb7b | ||
|
|
7d7dc4ee4e | ||
|
|
97fd04b13a | ||
|
|
899db4c8c0 | ||
|
|
bba204cbfe | ||
|
|
145660e9f9 | ||
|
|
24de14aea5 | ||
|
|
d57161e4d1 | ||
|
|
a066a5234a | ||
|
|
229db5dda2 | ||
|
|
4f2a8d354a | ||
|
|
b886471d72 | ||
|
|
2bab266ae2 | ||
|
|
6c5fd9bac8 | ||
|
|
08e93d7f13 | ||
|
|
7a510e084b | ||
|
|
da46d20ffc | ||
|
|
234c0bfbab | ||
|
|
82d60dbf4a | ||
|
|
6dffb80609 | ||
|
|
267c0e3184 | ||
|
|
033ccbbe67 | ||
|
|
c31b3f5894 | ||
|
|
9b1a303fea | ||
|
|
80a0f6da35 | ||
|
|
0c30eb7a60 | ||
|
|
7a8125bb71 | ||
|
|
c4beffd845 | ||
|
|
f97c112a53 | ||
|
|
5ef43ab6be | ||
|
|
2c399f99bf | ||
|
|
42e9eb0b96 | ||
|
|
e67d65f908 | ||
|
|
4ec8954b14 | ||
|
|
1a6abb039c | ||
|
|
bb5d1e5ac8 | ||
|
|
03c4c475eb | ||
|
|
04d7817d28 | ||
|
|
7cd100bc53 | ||
|
|
019924232a | ||
|
|
b5fc9f0275 | ||
|
|
44509b72ed | ||
|
|
d532a63dbd | ||
|
|
227785b079 | ||
|
|
0e364be998 | ||
|
|
7ae1f64ee3 | ||
|
|
92463a103d | ||
|
|
101cdb7b34 | ||
|
|
e924299f85 | ||
|
|
b983f7eb4a | ||
|
|
33b4be8b24 | ||
|
|
71a4edc8ba | ||
|
|
ceb305eb54 | ||
|
|
0b0d13dcf3 | ||
|
|
9f02368622 | ||
|
|
152010ee14 | ||
|
|
c6d5f0aea5 | ||
|
|
8c2ad64bb8 | ||
|
|
fa54d694b6 | ||
|
|
dc35b08624 | ||
|
|
4429515ba2 | ||
|
|
fdbc7b34e5 | ||
|
|
a1ab442cf0 | ||
|
|
9ed5c297f6 | ||
|
|
4ce9b214b0 | ||
|
|
7dcdadda75 | ||
|
|
f87a4d9fe2 | ||
|
|
e4e5d173f0 | ||
|
|
115b242d59 | ||
|
|
706bf8a431 | ||
|
|
87ecf1aa99 | ||
|
|
b5cf274333 | ||
|
|
4f4b6879b6 | ||
|
|
3b19463913 | ||
|
|
37386cd182 | ||
|
|
e04bdad16c | ||
|
|
e37f12705d | ||
|
|
5c8dc2c23a | ||
|
|
e9121f3b03 | ||
|
|
d68175db4e | ||
|
|
9d8181b0e2 | ||
|
|
6d657f268a | ||
|
|
3b3c5f823d | ||
|
|
09fc313492 | ||
|
|
316d0f6e54 | ||
|
|
a0033238bd | ||
|
|
5b1c6a7f46 | ||
|
|
8c0dff6552 | ||
|
|
43b230c84a | ||
|
|
f1b657011d | ||
|
|
e4d8ac8e1c | ||
|
|
08f44173dd | ||
|
|
54765c71fd | ||
|
|
01f8b49214 | ||
|
|
e8b0b3efaa | ||
|
|
f637938858 | ||
|
|
ae326c1d2f | ||
|
|
a4a1e6bf0a | ||
|
|
ecee44966e | ||
|
|
83437977ba | ||
|
|
8fcac1d425 | ||
|
|
705b5f1049 | ||
|
|
367d0c104b | ||
|
|
5d4bed2d9e | ||
|
|
63756192d8 | ||
|
|
b68ec78184 | ||
|
|
c55d5183fb | ||
|
|
f82e925944 | ||
|
|
c19f9ea173 | ||
|
|
76b2dd79ab | ||
|
|
c96ff23ad1 | ||
|
|
cd68b55b93 | ||
|
|
cad14d96f7 | ||
|
|
daaf9f1932 | ||
|
|
cb7502b450 | ||
|
|
ece142bbf1 | ||
|
|
611fee4605 | ||
|
|
791e2d0272 | ||
|
|
81742a4676 | ||
|
|
1ff3f2210c | ||
|
|
be9e4b91d5 | ||
|
|
854dcc5f95 | ||
|
|
29b71db33a | ||
|
|
2ee64b222a | ||
|
|
afda54f97b | ||
|
|
ad37c573b6 | ||
|
|
b6cb3104ae | ||
|
|
af9b0dc214 | ||
|
|
b5440032de | ||
|
|
a8e783235c | ||
|
|
fc97fe99e3 | ||
|
|
1c73b1133f | ||
|
|
908eccaafa | ||
|
|
e114d126c5 | ||
|
|
c07b4e4a28 | ||
|
|
06491a6611 | ||
|
|
b9a35850ad | ||
|
|
17a0c5d083 | ||
|
|
1b9523c799 | ||
|
|
ae80ecefc8 | ||
|
|
50ae32e3db | ||
|
|
2d43873398 | ||
|
|
a3df433fef | ||
|
|
35e71d8527 | ||
|
|
c5d07e4be1 | ||
|
|
0df5093b45 | ||
|
|
05d920d095 | ||
|
|
d2f7ac9843 | ||
|
|
857a302aad | ||
|
|
46de589791 | ||
|
|
f14961061c | ||
|
|
453fcf5cb1 | ||
|
|
24cdb27cdb | ||
|
|
24235c5896 | ||
|
|
c226f8cf58 | ||
|
|
b8f6c9f65f | ||
|
|
bb42e2db10 | ||
|
|
db544fd4b7 | ||
|
|
370c6bde5a | ||
|
|
d7965ee37f | ||
|
|
b2c6e07ed1 | ||
|
|
19cef20ceb | ||
|
|
058a1aeeaa | ||
|
|
d185077925 | ||
|
|
e3948ba91b | ||
|
|
4a30f94007 | ||
|
|
1ae27bf9e3 | ||
|
|
bc88103471 | ||
|
|
9ec6fbfe52 | ||
|
|
ebdb8de6b3 | ||
|
|
b8b56f6308 | ||
|
|
0726f5a402 | ||
|
|
90da5d1a7e | ||
|
|
bd3c518fa0 | ||
|
|
44272f60bf | ||
|
|
640ce8d854 | ||
|
|
74732058f2 | ||
|
|
22e480ccd1 | ||
|
|
32a0cb0394 |
4
.editorconfig
Normal file
4
.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
[*.cs]
|
||||
|
||||
# SYSLIB1045: Convert to 'GeneratedRegexAttribute'.
|
||||
dotnet_diagnostic.SYSLIB1045.severity = silent
|
||||
4
.github/ISSUE_TEMPLATE/feature-request.md
vendored
4
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -8,9 +8,9 @@ assignees: mnadareski
|
||||
---
|
||||
|
||||
**Before You Submit**
|
||||
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
|
||||
- .NET 6.0 has known limitations, so make sure that what you're asking for isn't already in another build.
|
||||
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
|
||||
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
|
||||
|
||||
If none of those apply, then continue...
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/informational.md
vendored
6
.github/ISSUE_TEMPLATE/informational.md
vendored
@@ -8,9 +8,9 @@ assignees: mnadareski
|
||||
---
|
||||
|
||||
**Before You Submit**
|
||||
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
|
||||
- .NET 6.0 has known limitations, so make sure that what you're giving information on isn't already in another build.
|
||||
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
|
||||
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
|
||||
|
||||
If none of those apply, then continue...
|
||||
@@ -19,4 +19,4 @@ If none of those apply, then continue...
|
||||
A clear and concise description of what the information is. Ex. With the latest build of DumpingProgram, it [...]
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the information here.
|
||||
Add any other context or screenshots about the information here.
|
||||
|
||||
9
.github/ISSUE_TEMPLATE/issue-report.md
vendored
9
.github/ISSUE_TEMPLATE/issue-report.md
vendored
@@ -8,16 +8,16 @@ assignees: mnadareski
|
||||
---
|
||||
|
||||
**Before You Submit**
|
||||
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the issue has already been addressed.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/mnadareski/BurnOutSharp/issues) instead.
|
||||
- .NET 6.0 has known issues, please try using another build to reproduce the error
|
||||
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
|
||||
- Check multiple discs to help narrow down the issue
|
||||
- Check the Options to see if changing any of those affects your issue.
|
||||
|
||||
If all of those fail, then continue...
|
||||
|
||||
**Version**
|
||||
What version are you using?
|
||||
What version are you using?
|
||||
|
||||
- [ ] Stable release (version here)
|
||||
- [ ] WIP release (version here)
|
||||
@@ -25,14 +25,15 @@ What version are you using?
|
||||
**Build**
|
||||
What runtime version are you using?
|
||||
|
||||
- [ ] .NET Framework 4.8 running on (Operating System)
|
||||
- [ ] .NET 6.0 running on (Operating System)
|
||||
- [ ] .NET 8.0 running on (Operating System)
|
||||
|
||||
**Describe the issue**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
|
||||
50
.github/workflows/build_check.yml
vendored
Normal file
50
.github/workflows/build_check.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: MPF Check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
project: [MPF.Check]
|
||||
runtime: [win-x86, win-x64, linux-x64, osx-x64] #[win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
|
||||
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c Debug --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
|
||||
|
||||
- name: Archive build
|
||||
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip ${{ matrix.project }}/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug
|
||||
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
57
.github/workflows/build_ui.yml
vendored
Normal file
57
.github/workflows/build_ui.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: MPF UI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
project: [MPF]
|
||||
runtime: [win-x86, win-x64]
|
||||
framework: [net8.0-windows] #[net40, net452, net472, net48, netcoreapp3.1, net5.0-windows, net6.0-windows, net7.0-windows, net8.0-windows]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet publish ${{ matrix.project }}/${{ matrix.project }}.csproj -f ${{ matrix.framework }} -r ${{ matrix.runtime }} -c Debug --self-contained true --version-suffix ${{ github.sha }} ${{ (startsWith(matrix.framework, 'net5') || startsWith(matrix.framework, 'net6') || startsWith(matrix.framework, 'net7') || startsWith(matrix.framework, 'net8')) && '-p:PublishSingleFile=true' || ''}}
|
||||
|
||||
- name: Bundle Redumper
|
||||
run: |
|
||||
wget https://github.com/superg/redumper/releases/download/build_311/redumper-2024.01.08_build311-win64.zip
|
||||
unzip redumper-2024.01.08_build311-win64.zip
|
||||
mkdir -p MPF/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper
|
||||
mv redumper-2024.01.08_build311-win64/bin/redumper.exe MPF/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/Programs/Redumper/
|
||||
|
||||
- name: Archive build
|
||||
run: zip -r ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip ${{ matrix.project }}/bin/Debug/${{ matrix.framework }}/${{ matrix.runtime }}/publish/
|
||||
|
||||
- name: Upload build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug
|
||||
path: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
|
||||
|
||||
- name: Upload to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: ${{ matrix.project }}_${{ matrix.framework }}_${{ matrix.runtime }}_debug.zip
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
23
.github/workflows/check_pr.yml
vendored
Normal file
23
.github/workflows/check_pr.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Build PR
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build
|
||||
run: dotnet build --no-restore
|
||||
|
||||
- name: Test
|
||||
run: dotnet test --no-restore --verbosity normal
|
||||
15
.vscode/tasks.json
vendored
15
.vscode/tasks.json
vendored
@@ -3,22 +3,11 @@
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "msbuild",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/MPF.sln",
|
||||
"-property:RuntimeIdentifiers=win7-x64"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "msbuild",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"${workspaceFolder}/MPF/MPF.csproj",
|
||||
"-target:Publish",
|
||||
"-property:RuntimeIdentifiers=win7-x64"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
|
||||
376
CHANGELIST.md
376
CHANGELIST.md
@@ -1,3 +1,379 @@
|
||||
### 3.1.4 (2024-03-16)
|
||||
|
||||
- Update BinaryObjectScanner to 3.1.2
|
||||
|
||||
### 3.1.3 (2024-03-15)
|
||||
|
||||
- Gate debug publishing behind use all flag
|
||||
- Hide layerbreaks if value is 0
|
||||
- Make GHA debug-only
|
||||
- Remove GHA pull request builds
|
||||
- Add PR check workflow
|
||||
- Don't link to AppVeyor artifacts page anymore
|
||||
- Add PS3 CFW support to MPF.Check (Deterous)
|
||||
- Hide size if value is 0 (Deterous)
|
||||
- Fix title normalization (Deterous)
|
||||
- Ensure no labels are empty
|
||||
- Use SabreTools.Hashing
|
||||
- Update to SabreTools.RedumpLib 1.3.5
|
||||
- Update packages to latest
|
||||
- Enable LibIRD for all .NET frameworks (Deterous)
|
||||
- Try updating PR check action
|
||||
- Fix config access persmission (Deterous)
|
||||
- Fix Check UI deadlock (Deterous)
|
||||
- Fix formatting output formatting
|
||||
- Update LibIRD to 0.9.0 (Deterous)
|
||||
- Update packages
|
||||
- Fix Redumper generic drive type (Deterous)
|
||||
- Add MPF version to Submission info (Deterous)
|
||||
- Update to RedumpLib 1.3.6
|
||||
|
||||
### 3.1.2 (2024-02-27)
|
||||
|
||||
- Remove debugging lines from build script
|
||||
- Port build script fixes from BOS
|
||||
- Fix double git hash version (feat. Deterous)
|
||||
- Readd x86 builds by default
|
||||
- Hide unavailable dumping programs (Deterous)
|
||||
- Remove DIC and Aaru bundles from CI
|
||||
- Add x86 builds to AppVeyor
|
||||
- Make AppVeyor builds framework-dependent
|
||||
- Fix misattributed artifact
|
||||
- Update README with current build instructions
|
||||
- Opt-in automatic IRD creation after PS3 dump (Deterous)
|
||||
- Add CI via Github Workflows (Deterous)
|
||||
- Reorganize solution items
|
||||
- Split CI workflow files
|
||||
- Add GHA CI status badges
|
||||
- Rename badges for GHA
|
||||
- More tweaks to CI
|
||||
- Fix net35 build issue
|
||||
- Remove now-unnecessary restore step
|
||||
- Remove net35 from MPF... again
|
||||
- Fix whitespace that got unwhitespaced
|
||||
- Fix link in README
|
||||
- Attempt to add CD to existing actions
|
||||
- Try fixing the artifact upload
|
||||
- Use recommendation from upload-artifact
|
||||
- Revert artifact ID, use name?
|
||||
- Try using download-artifact
|
||||
- Download all artifacts?
|
||||
- Use newer download version
|
||||
- Build artifact before upload
|
||||
- Change link to WIP builds in README
|
||||
- Use commit SHA as body of rolling releases
|
||||
- Don't omit body when setting body
|
||||
- Remove unnecessary empty section
|
||||
- Unified tag for rolling release
|
||||
- Generate release notes automatically
|
||||
- Remove generation, just in case
|
||||
- Change link to WIP builds in README
|
||||
- Show hashes in readonly data
|
||||
- Update to BinaryObjectScanner 3.1.0
|
||||
- Add Mattel HyperScan detection
|
||||
- Pull PS3 Disc Key from redump (Deterous)
|
||||
|
||||
### 3.1.1 (2024-02-20)
|
||||
|
||||
- Remove .NET 6 from auto-builds
|
||||
- Make Redumper the default for new users
|
||||
- Fix DIC log parsing for SS version (Deterous)
|
||||
- Write outputs with UTF-8
|
||||
- Add funworld Photo Play detection
|
||||
- Fix Aaru drive parameter generation
|
||||
- Limit DVD protection outputs
|
||||
- Add a GUI for PS3 IRD Creation (Deterous)
|
||||
- Update LibIRD, disable UI elements when creating IRD (Deterous)
|
||||
|
||||
### 3.1.0 (2024-02-06)
|
||||
|
||||
- Update RedumpLib
|
||||
- Update Redumper to build 294
|
||||
- Fix commented out code
|
||||
- Make missing hash data clearer
|
||||
- Get BD PIC Identifier for redumper (Deterous)
|
||||
- Support redumper skeleton and hash files (Deterous)
|
||||
- Support ringcode and PIC for triple/quad-layer (fuzz6001)
|
||||
- Cleanup !protectionInfo.txt (Deterous)
|
||||
- Update Redumper to build 311 (Deterous)
|
||||
- Use PSX/PS2 serial as filename when Volume Label not present (Deterous)
|
||||
- Allow variables in output path (Deterous)
|
||||
- Check for presence of complete dump from other programs (Deterous)
|
||||
- Retrieve volume label from logs (Deterous)
|
||||
- Correct missing space in PVD (fuzz6001)
|
||||
- Prevent crashing on invalid parameters (Deterous)
|
||||
- Detect CDTV discs (Deterous)
|
||||
- Differentiate CD32 from CDTV (Deterous)
|
||||
- Normalise Disc Titles in Submission Info (Deterous)
|
||||
- Skip warning line in Redumper log
|
||||
- Add a GUI for MPF.Check (Deterous)
|
||||
- Fix information pulling for CleanRip and UIC
|
||||
- Add UMD handling for the disc info window
|
||||
- Detect Photo CD
|
||||
- Parse PSX/PS2/KP2 exe date from logs (Deterous)
|
||||
- Exclude extra tracks when finding disc matches (Deterous)
|
||||
- Verbose Redumper log by default (Deterous)
|
||||
- Retrieve serial from Cleanrip
|
||||
- Fix build from rushed code
|
||||
- Remove `-disc2` from Cleanrip serial
|
||||
- Enable Windows builds on Linux and Mac
|
||||
- Fix compiler warning (Deterous)
|
||||
|
||||
### 3.0.3 (2023-12-04)
|
||||
|
||||
- Fix broken tests
|
||||
- Fix using SHA-1 for track checks
|
||||
- Fix build warning for NRE
|
||||
- Remove .NET Framework 3.5 from build script
|
||||
- Handle or suppress some messages
|
||||
|
||||
### 3.0.2 (2023-12-01)
|
||||
|
||||
- Read CSS for some copy protections
|
||||
- Add Disc ID and Key fields in info window
|
||||
- Replace build script with Powershell
|
||||
- Fix Powershell build script
|
||||
- Fix cross-framework UI rendering
|
||||
- Fix most .NET Framework 3.5 issues
|
||||
- Fix cross-framework UI styles
|
||||
- Update Redumper to build 271
|
||||
- Update USE_ALL in Powershell script
|
||||
- Import WinFX for .NET Framework 3.5
|
||||
- Reference .NET Framework 3.0 for 3.5
|
||||
- Handle most VS and dotnet differences
|
||||
|
||||
### 3.0.1 (2023-11-30)
|
||||
|
||||
- Add Bandai Pippin detection
|
||||
- Zip manufacturer files for Redumper
|
||||
- Fix BE flag logic bug in DIC
|
||||
- Support ancient .NET in Core
|
||||
- Support ancient .NET in UI Core
|
||||
- Support C# 12 syntax
|
||||
- Support ancient .NET in Check
|
||||
- Support ancient .NET in UI
|
||||
- Fix TLS for older .NET
|
||||
- Perform more ancient .NET support work
|
||||
- Prepare XAML for ancient .NET support
|
||||
- Suppress deprecation warnings
|
||||
- Fix reversed ringcode test
|
||||
- Add C#12 syntax to tests
|
||||
- Trim PS3 serial and add unrelated notes
|
||||
- Update RedumpLib and use moved methods
|
||||
- Perform some post-move cleanup
|
||||
- More C# 12 cleanup in Core
|
||||
- Update to BinaryObjectScanner 3.0.1
|
||||
- Update Xunit packages
|
||||
- Get Core building with Framework 4.0
|
||||
- Get UI.Core building with Framework 4.0
|
||||
- Get Check building with Framework 4.0
|
||||
- Use TryGetValue on dictionaries
|
||||
- Support proper async in .NET Framework 4.0
|
||||
- Temporarily remove .NET Framework 4.0
|
||||
- Update to BinaryObjectScanner 3.0.2
|
||||
- Re-enable .NET Framework 4.0 building in Core
|
||||
- Re-enable .NET Framework 4.0 building in UI.Core
|
||||
- Re-enable .NET Framework 4.0 building in Check
|
||||
- Support .NET Framework 3.5
|
||||
- Update compatibility libraries
|
||||
- Support .NET Framework 2.0
|
||||
- Support .NET Framework 3.5 in UI.Core
|
||||
- Get UI building with Framework 4.0
|
||||
- Temporarily remove .NET Framework 4.0 from UI
|
||||
- Get UI building with Framework 4.0 again
|
||||
- Support .NET Framework 3.5 in UI
|
||||
- Update Redumper to build 268
|
||||
|
||||
### 3.0.0 (2023-11-14)
|
||||
|
||||
- Remove .NET Framework 4.8 from build
|
||||
- Remove .NET Framework 4.8 from projects
|
||||
- Remove .NET Framework 4.8 gated code
|
||||
- Address lingering modern .NET syntax
|
||||
- Update README for Legacy
|
||||
- Remove redundant information
|
||||
- Remove 4.8 note from build script
|
||||
- Even more README cleanup
|
||||
- Consolidate build script information
|
||||
- Remove lingering script reference
|
||||
- Make releases less verbose
|
||||
- Remove .NET Framework 4.8 from issue report
|
||||
- Remove Unshield reference
|
||||
- Update documentation for BOS change
|
||||
- Bump library versions
|
||||
- Update to .NET 8
|
||||
|
||||
### 2.7.5 (2023-11-06)
|
||||
|
||||
- Remove psxt001z Pkg Ref in MPF.Test (Deterous)
|
||||
- Update Redumper to build 247
|
||||
- Focus main window after child window closes (Deterous)
|
||||
- Try to get PS3 data from SFB
|
||||
- Fix PS3 version finding
|
||||
- Pull PS3 Firmware Version (Deterous)
|
||||
- Fix default layerbreak output
|
||||
- Enable browsing for Redumper path (Deterous)
|
||||
- Update to MMI 3.0.0 (Deterous)
|
||||
|
||||
### 2.7.4 (2023-10-31)
|
||||
|
||||
- Move Hash enum and simplify
|
||||
- Add other non-cryptographic hashes
|
||||
- Avoid unncessary allocation in hashing
|
||||
- Make Hasher more consistent
|
||||
- Move all hashing to Hasher
|
||||
- Separate out static hashing
|
||||
- Remove unncessary summary
|
||||
- Version-gate new switch statement
|
||||
- Ignore empty protection results
|
||||
- Remove and Sort Usings
|
||||
- Update Redumper to build 244
|
||||
- Enable Bluray dumping with Redumper
|
||||
- Add Bluray layerbreak support for Redumper
|
||||
- Remove now-obsolete notes
|
||||
- Compile most regex statements
|
||||
- Handle a couple of messages
|
||||
- Remove .manufacturer for Bluray
|
||||
- Fix typo that disables DIC during media check (Deterous)
|
||||
- Fix build
|
||||
- Remove duplicate check
|
||||
- Add PIC output for Redumper
|
||||
- Update Redumper to build 246
|
||||
- Enable HD-DVD for Redumper in UI
|
||||
- Attempt to fix window owner issue
|
||||
|
||||
### 2.7.3 (2023-10-26)
|
||||
|
||||
- Split InfoTool into 2 classes
|
||||
- Add path variants for PlayStation info
|
||||
- Convert Drive to use paths internally
|
||||
- Create skeleton for first-run
|
||||
- Show options window on first run
|
||||
- Fix drive letter issue in UI
|
||||
- Add first-run Options title, fix saving bug
|
||||
- Fix multiple handler invocation
|
||||
- Create method for applying theme
|
||||
- Fix drive letter check
|
||||
- Simply theme application
|
||||
- Add framework for deleteable files
|
||||
- Add deleteable file lists for Redumper and DIC
|
||||
- Add optional file deletion
|
||||
- Rearrange OptionsWindow to be easier to navigate
|
||||
- Fix up DumpEnvironment a bit
|
||||
- Add filename suffix setting (nw)
|
||||
- Wire through filename suffix
|
||||
- Expose suffix setting
|
||||
- Set UDF CD threshold at 800MB (Deterous)
|
||||
- Update package versions
|
||||
- Attempt to parse out PS5 params.json
|
||||
- Fix CRC32 hashing
|
||||
- Update XUnit packages
|
||||
- Update to BurnOutSharp 2.9.0
|
||||
- Update to MMI 3.0.0-preview.4
|
||||
- Fix two small nullability issues
|
||||
- Use Array.Empty in hasher
|
||||
- Always use relative path internally (Deterous)
|
||||
|
||||
### 2.7.2 (2023-10-17)
|
||||
|
||||
- Fix options loading for Check
|
||||
- Cleanup and gated code
|
||||
- Gate some switch expressions
|
||||
- Suppress some unnecessary messages
|
||||
- Disable dumping button when Redumper selected with unsupported media type (Deterous)
|
||||
- Improve check for which program supports which media (Deterous)
|
||||
- Remove code for getting Universal Hash from DIC logs (Deterous)
|
||||
- Fix Redumper retry count not showing
|
||||
- Enable parameters checkbox by default
|
||||
- Update Redumper to build 230
|
||||
- Fix GetPVD in dic (fuzz6001)
|
||||
- Get SecuROM data from Redumper
|
||||
- Clean up issue templates
|
||||
- Support Redumper 231 outputs
|
||||
|
||||
### 2.7.1 (2023-10-11)
|
||||
|
||||
- Add pull-all flag for Check
|
||||
- Fix errant version detection issue
|
||||
- Allow user-supplied information in Check
|
||||
- Add BE read flag for Redumper
|
||||
- Add generic drive flag for Redumper
|
||||
- Handle numeric disc titles as issue numbers
|
||||
- Unify handling of enable/disable events
|
||||
- Enable nullability in Check
|
||||
- Remove all but .NET 6 for AppVeyor packaging
|
||||
- Place message boxes at center of main window (Deterous)
|
||||
- Enable nullability in MPF
|
||||
- Enable nullability in MPF.UI.Core
|
||||
- Enable nullability in MPF.Test
|
||||
- Handle some suggested changes
|
||||
- Var-ify many instances
|
||||
- Version-gate some newer syntax
|
||||
- Fix Check always showing Help text
|
||||
- Version-gate some more newer syntax
|
||||
- Enable path browse button by default
|
||||
- Remove unnecessary namespacing
|
||||
|
||||
### 2.7.0 (2023-10-11)
|
||||
|
||||
- Attempt to replace NRT
|
||||
- Try alternate archive naming
|
||||
- Publish, but don't package, release builds
|
||||
- Add note about git being required
|
||||
- Try out using version number in AppVeyor
|
||||
- Job number not build ID
|
||||
- Fix errant space in variable name
|
||||
- Build number not job number
|
||||
- Use System.IO.Hashing for CRC32
|
||||
- Don't reverse the CRC32 output
|
||||
- Add submission info preamble
|
||||
- Fix missing comma
|
||||
- Remove IMAPI2 with no substitution
|
||||
- Remove framework exceptions
|
||||
- Slight reorganization of code
|
||||
- Update README with support information
|
||||
- Remove msbuild-specific AppVeyor items
|
||||
- Be trickier when it comes to type from size
|
||||
- Fix "detected" message
|
||||
- Be smarter about media type based on system
|
||||
- Consolidate into MPF.Core
|
||||
- Fix failing tests
|
||||
- Remove debug symbols in release builds (Deterous)
|
||||
- Allow nullability for modern .NET
|
||||
- Fix failing tests
|
||||
- Remove unnecessary include
|
||||
- Move element items to Core
|
||||
- Move LogLevel enumeration
|
||||
- Split logging code a bit more
|
||||
- Split info window code a bit more
|
||||
- Clarify build instructions in README (Deterous)
|
||||
- Split options window code a bit more
|
||||
- Use binding for more disc info textboxes
|
||||
- Handle Redump password changing better
|
||||
- Refine options window bindings
|
||||
- Refine options window bindings further
|
||||
- Refine info window bindings further
|
||||
- Move decoupled view models
|
||||
- Fix log output
|
||||
- Start migrating MainViewModel
|
||||
- Perform most of MainViewModel changes
|
||||
- Small updtes to MainViewModel
|
||||
- Remove MainWindow from MainViewModel
|
||||
- Use callback for logging, fix Options window
|
||||
- Remove WinForms from MainViewModel
|
||||
- Remove some message boxes from MainViewModel
|
||||
- Remove more Windows from MainViewModel
|
||||
- Fix null reference exception in disc type
|
||||
- Detected type and selected type are different
|
||||
- Remove message boxes from MainViewModel
|
||||
- Move MainViewModel to Core
|
||||
- Remove LogOutputViewModel
|
||||
- Consolidate some constants
|
||||
- Fix media type ordering
|
||||
- Fix dumping button not enabling
|
||||
- Recentralize some Check functionality
|
||||
- Fix media combobox not updating (Deterous)
|
||||
|
||||
### 2.6.6 (2023-10-04)
|
||||
|
||||
- Update Nuget packages
|
||||
|
||||
@@ -1,20 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Title>MPF Check</Title>
|
||||
<Description>Validator for various dumping programs</Description>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<Version>2.6.6</Version>
|
||||
</PropertyGroup>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.1.4</VersionPrefix>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
<!-- Package Properties -->
|
||||
<Title>MPF Check</Title>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Validator for various dumping programs</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -23,22 +29,17 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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">
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.2" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,10 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using BurnOutSharp;
|
||||
using MPF.Core.Converters;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.Library;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
@@ -15,16 +14,24 @@ namespace MPF.Check
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Try processing the standalone arguments
|
||||
if (ProcessStandaloneArguments(args))
|
||||
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
|
||||
if (standaloneProcessed != false)
|
||||
{
|
||||
if (standaloneProcessed == null)
|
||||
DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try processing the common arguments
|
||||
(bool success, MediaType mediaType, RedumpSystem? knownSystem) = ProcessCommonArguments(args);
|
||||
(bool success, MediaType mediaType, RedumpSystem? knownSystem, var error) = OptionsLoader.ProcessCommonArguments(args);
|
||||
if (!success)
|
||||
{
|
||||
DisplayHelp(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Loop through and process options
|
||||
(Core.Data.Options options, string path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
|
||||
(var options, var seedInfo, var path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
|
||||
if (options.InternalProgram == InternalProgram.NONE)
|
||||
{
|
||||
DisplayHelp("A program name needs to be provided");
|
||||
@@ -33,17 +40,17 @@ namespace MPF.Check
|
||||
|
||||
// Make new Progress objects
|
||||
var resultProgress = new Progress<Result>();
|
||||
resultProgress.ProgressChanged += ProgressUpdated;
|
||||
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
var protectionProgress = new Progress<ProtectionProgress>();
|
||||
protectionProgress.ProgressChanged += ProgressUpdated;
|
||||
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
|
||||
// Validate the supplied credentials
|
||||
#if NET48
|
||||
(bool? _, string message) = RedumpWebClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword);
|
||||
#if NETFRAMEWORK
|
||||
(bool? _, string? message) = RedumpWebClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty);
|
||||
#else
|
||||
(bool? _, string message) = RedumpHttpClient.ValidateCredentials(options?.RedumpUsername, options?.RedumpPassword).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
(bool? _, string? message) = RedumpHttpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
Console.WriteLine(message);
|
||||
|
||||
// Loop through all the rest of the args
|
||||
@@ -60,14 +67,20 @@ namespace MPF.Check
|
||||
string filepath = Path.GetFullPath(args[i].Trim('"'));
|
||||
|
||||
// Now populate an environment
|
||||
Drive drive = null;
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
drive = Drive.Create(null, path);
|
||||
Drive? drive = null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
drive = Drive.Create(null, path!);
|
||||
|
||||
var env = new DumpEnvironment(options, filepath, drive, knownSystem, mediaType, internalProgram: null, parameters: null);
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
#if NET40
|
||||
var resultTask = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress);
|
||||
resultTask.Wait();
|
||||
var result = resultTask.Result;
|
||||
#else
|
||||
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
Console.WriteLine(result.Message);
|
||||
}
|
||||
}
|
||||
@@ -76,7 +89,7 @@ namespace MPF.Check
|
||||
/// Display help for MPF.Check
|
||||
/// </summary>
|
||||
/// <param name="error">Error string to prefix the help text with</param>
|
||||
private static void DisplayHelp(string error = null)
|
||||
private static void DisplayHelp(string? error = null)
|
||||
{
|
||||
if (error != null)
|
||||
Console.WriteLine(error);
|
||||
@@ -99,101 +112,5 @@ namespace MPF.Check
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process common arguments for all functionality
|
||||
/// </summary>
|
||||
/// <returns>True if all arguments pass, false otherwise</returns>
|
||||
private static (bool, MediaType, RedumpSystem?) ProcessCommonArguments(string[] args)
|
||||
{
|
||||
// All other use requires at least 3 arguments
|
||||
if (args.Length < 3)
|
||||
{
|
||||
DisplayHelp("Invalid number of arguments");
|
||||
return (false, MediaType.NONE, null);
|
||||
}
|
||||
|
||||
// Check the MediaType
|
||||
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
|
||||
if (mediaType == MediaType.NONE)
|
||||
{
|
||||
DisplayHelp($"{args[0]} is not a recognized media type");
|
||||
return (false, MediaType.NONE, null);
|
||||
}
|
||||
|
||||
// Check the RedumpSystem
|
||||
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
|
||||
if (knownSystem == null)
|
||||
{
|
||||
DisplayHelp($"{args[1]} is not a recognized system");
|
||||
return (false, MediaType.NONE, null);
|
||||
}
|
||||
|
||||
return (true, mediaType, knownSystem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process any standalone arguments for the program
|
||||
/// </summary>
|
||||
/// <returns>True if one of the arguments was processed, false otherwise</returns>
|
||||
private static bool ProcessStandaloneArguments(string[] args)
|
||||
{
|
||||
// Help options
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
|
||||
{
|
||||
DisplayHelp();
|
||||
return true;
|
||||
}
|
||||
|
||||
// List options
|
||||
if (args[0] == "-lm" || args[0] == "--listmedia")
|
||||
{
|
||||
Console.WriteLine("Supported Media Types:");
|
||||
foreach (string mediaType in Extensions.ListMediaTypes())
|
||||
{
|
||||
Console.WriteLine(mediaType);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-lp" || args[0] == "--listprograms")
|
||||
{
|
||||
Console.WriteLine("Supported Programs:");
|
||||
foreach (string program in EnumExtensions.ListPrograms())
|
||||
{
|
||||
Console.WriteLine(program);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-ls" || args[0] == "--listsystems")
|
||||
{
|
||||
Console.WriteLine("Supported Systems:");
|
||||
foreach (string system in Extensions.ListSystems())
|
||||
{
|
||||
Console.WriteLine(system);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple process counter to write to console
|
||||
/// </summary>
|
||||
private static void ProgressUpdated(object sender, Result value)
|
||||
{
|
||||
Console.WriteLine(value.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple process counter to write to console
|
||||
/// </summary>
|
||||
private static void ProgressUpdated(object sender, ProtectionProgress value)
|
||||
{
|
||||
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Anything marked as internal can be used by the test methods
|
||||
[assembly: InternalsVisibleTo("MPF.Test")]
|
||||
25
MPF.Core/ConsoleLogger.cs
Normal file
25
MPF.Core/ConsoleLogger.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core.Data;
|
||||
|
||||
namespace MPF.Core
|
||||
{
|
||||
public static class ConsoleLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple process counter to write to console
|
||||
/// </summary>
|
||||
public static void ProgressUpdated(object? sender, Result value)
|
||||
{
|
||||
Console.WriteLine(value.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple process counter to write to console
|
||||
/// </summary>
|
||||
public static void ProgressUpdated(object? sender, ProtectionProgress value)
|
||||
{
|
||||
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
using System;
|
||||
#if NET20 || NET35
|
||||
using System.Collections.Generic;
|
||||
#else
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#endif
|
||||
using MPF.Core.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
@@ -21,60 +22,15 @@ namespace MPF.Core.Converters
|
||||
/// <returns>InternalDriveType, if possible, null on error</returns>
|
||||
public static InternalDriveType? ToInternalDriveType(this DriveType driveType)
|
||||
{
|
||||
switch (driveType)
|
||||
return driveType switch
|
||||
{
|
||||
case DriveType.CDRom:
|
||||
return InternalDriveType.Optical;
|
||||
case DriveType.Fixed:
|
||||
return InternalDriveType.HardDisk;
|
||||
case DriveType.Removable:
|
||||
return InternalDriveType.Removable;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
DriveType.CDRom => (InternalDriveType?)InternalDriveType.Optical,
|
||||
DriveType.Fixed => (InternalDriveType?)InternalDriveType.HardDisk,
|
||||
DriveType.Removable => (InternalDriveType?)InternalDriveType.Removable,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Convert IMAPI physical media type to a MediaType
|
||||
/// </summary>
|
||||
/// <param name="type">IMAPI_MEDIA_PHYSICAL_TYPE value to check</param>
|
||||
/// <returns>MediaType if possible, null on error</returns>
|
||||
public static MediaType? IMAPIToMediaType(this IMAPI_MEDIA_PHYSICAL_TYPE type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_UNKNOWN:
|
||||
return MediaType.NONE;
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDROM:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDR:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDRW:
|
||||
return MediaType.CDROM;
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDROM:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDRAM:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR_DUALLAYER:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHRW:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR_DUALLAYER:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DISK:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW_DUALLAYER:
|
||||
return MediaType.DVD;
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDROM:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDR:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDRAM:
|
||||
return MediaType.HDDVD;
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDROM:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDR:
|
||||
case IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDRE:
|
||||
return MediaType.BluRay;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Convert to Long Name
|
||||
@@ -82,7 +38,11 @@ namespace MPF.Core.Converters
|
||||
/// <summary>
|
||||
/// Long name method cache
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Type, MethodInfo> LongNameMethods = new ConcurrentDictionary<Type, MethodInfo>();
|
||||
#if NET20 || NET35
|
||||
private static readonly Dictionary<Type, MethodInfo?> LongNameMethods = [];
|
||||
#else
|
||||
private static readonly ConcurrentDictionary<Type, MethodInfo?> LongNameMethods = [];
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get the string representation of a generic enumerable value
|
||||
@@ -93,20 +53,23 @@ namespace MPF.Core.Converters
|
||||
{
|
||||
try
|
||||
{
|
||||
var sourceType = value?.GetType();
|
||||
var sourceType = value.GetType();
|
||||
sourceType = Nullable.GetUnderlyingType(sourceType) ?? sourceType;
|
||||
|
||||
if (!LongNameMethods.TryGetValue(sourceType, out MethodInfo method))
|
||||
if (!LongNameMethods.TryGetValue(sourceType, out var method))
|
||||
{
|
||||
method = typeof(SabreTools.RedumpLib.Data.Extensions).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
|
||||
if (method == null)
|
||||
method = typeof(EnumConverter).GetMethod("LongName", new[] { typeof(Nullable<>).MakeGenericType(sourceType) });
|
||||
method = typeof(Extensions).GetMethod("LongName", [typeof(Nullable<>).MakeGenericType(sourceType)]);
|
||||
method ??= typeof(EnumConverter).GetMethod("LongName", [typeof(Nullable<>).MakeGenericType(sourceType)]);
|
||||
|
||||
#if NET20 || NET35
|
||||
LongNameMethods[sourceType] = method;
|
||||
#else
|
||||
LongNameMethods.TryAdd(sourceType, method);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (method != null)
|
||||
return method.Invoke(null, new[] { value }) as string;
|
||||
return method.Invoke(null, new[] { value }) as string ?? string.Empty;
|
||||
else
|
||||
return string.Empty;
|
||||
}
|
||||
@@ -124,39 +87,31 @@ namespace MPF.Core.Converters
|
||||
/// <returns>String representing the value, if possible</returns>
|
||||
public static string LongName(this InternalProgram? prog)
|
||||
{
|
||||
switch (prog)
|
||||
return (prog) switch
|
||||
{
|
||||
#region Dumping support
|
||||
|
||||
case InternalProgram.Aaru:
|
||||
return "Aaru";
|
||||
case InternalProgram.DiscImageCreator:
|
||||
return "DiscImageCreator";
|
||||
case InternalProgram.Redumper:
|
||||
return "redumper";
|
||||
InternalProgram.Aaru => "Aaru",
|
||||
InternalProgram.DiscImageCreator => "DiscImageCreator",
|
||||
InternalProgram.Redumper => "Redumper",
|
||||
|
||||
#endregion
|
||||
|
||||
#region Verification support only
|
||||
|
||||
case InternalProgram.CleanRip:
|
||||
return "CleanRip";
|
||||
|
||||
case InternalProgram.DCDumper:
|
||||
return "DCDumper";
|
||||
|
||||
case InternalProgram.UmdImageCreator:
|
||||
return "UmdImageCreator";
|
||||
InternalProgram.CleanRip => "CleanRip",
|
||||
InternalProgram.DCDumper => "DCDumper",
|
||||
InternalProgram.PS3CFW => "PS3 CFW",
|
||||
InternalProgram.UmdImageCreator => "UmdImageCreator",
|
||||
|
||||
#endregion
|
||||
|
||||
case InternalProgram.NONE:
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
InternalProgram.NONE => "Unknown",
|
||||
_ => "Unknown",
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Convert From String
|
||||
|
||||
@@ -165,42 +120,40 @@ namespace MPF.Core.Converters
|
||||
/// </summary>
|
||||
/// <param name="internalProgram">String value to convert</param>
|
||||
/// <returns>InternalProgram represented by the string, if possible</returns>
|
||||
public static InternalProgram ToInternalProgram(string internalProgram)
|
||||
public static InternalProgram ToInternalProgram(string? internalProgram)
|
||||
{
|
||||
switch (internalProgram.ToLowerInvariant())
|
||||
return (internalProgram?.ToLowerInvariant()) switch
|
||||
{
|
||||
// Dumping support
|
||||
case "aaru":
|
||||
case "chef":
|
||||
case "dichef":
|
||||
case "discimagechef":
|
||||
return InternalProgram.Aaru;
|
||||
case "creator":
|
||||
case "dic":
|
||||
case "dicreator":
|
||||
case "discimagecreator":
|
||||
return InternalProgram.DiscImageCreator;
|
||||
case "rd":
|
||||
case "redumper":
|
||||
return InternalProgram.Redumper;
|
||||
"aaru"
|
||||
or "chef"
|
||||
or "dichef"
|
||||
or "discimagechef" => InternalProgram.Aaru,
|
||||
"creator"
|
||||
or "dic"
|
||||
or "dicreator"
|
||||
or "discimagecreator" => InternalProgram.DiscImageCreator,
|
||||
"rd"
|
||||
or "redumper" => InternalProgram.Redumper,
|
||||
|
||||
// Verification support only
|
||||
case "cleanrip":
|
||||
case "cr":
|
||||
return InternalProgram.CleanRip;
|
||||
case "dc":
|
||||
case "dcd":
|
||||
case "dcdumper":
|
||||
return InternalProgram.DCDumper;
|
||||
case "uic":
|
||||
case "umd":
|
||||
case "umdcreator":
|
||||
case "umdimagecreator":
|
||||
return InternalProgram.UmdImageCreator;
|
||||
"cleanrip"
|
||||
or "cr" => InternalProgram.CleanRip,
|
||||
"dc"
|
||||
or "dcd"
|
||||
or "dcdumper" => InternalProgram.DCDumper,
|
||||
"ps3cfw"
|
||||
or "ps3"
|
||||
or "getkey"
|
||||
or "managunz"
|
||||
or "multiman" => InternalProgram.PS3CFW,
|
||||
"uic"
|
||||
or "umd"
|
||||
or "umdcreator"
|
||||
or "umdimagecreator" => InternalProgram.UmdImageCreator,
|
||||
|
||||
default:
|
||||
return InternalProgram.NONE;
|
||||
}
|
||||
_ => InternalProgram.NONE,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -210,170 +163,140 @@ namespace MPF.Core.Converters
|
||||
/// <returns>MediaType represented by the string, if possible</returns>
|
||||
public static MediaType ToMediaType(string type)
|
||||
{
|
||||
switch (type.ToLowerInvariant())
|
||||
return (type.ToLowerInvariant()) switch
|
||||
{
|
||||
#region Punched Media
|
||||
|
||||
case "aperture":
|
||||
case "aperturecard":
|
||||
case "aperture card":
|
||||
return MediaType.ApertureCard;
|
||||
case "jacquardloom":
|
||||
case "jacquardloomcard":
|
||||
case "jacquard loom card":
|
||||
return MediaType.JacquardLoomCard;
|
||||
case "magneticstripe":
|
||||
case "magneticstripecard":
|
||||
case "magnetic stripe card":
|
||||
return MediaType.MagneticStripeCard;
|
||||
case "opticalphone":
|
||||
case "opticalphonecard":
|
||||
case "optical phonecard":
|
||||
return MediaType.OpticalPhonecard;
|
||||
case "punchcard":
|
||||
case "punchedcard":
|
||||
case "punched card":
|
||||
return MediaType.PunchedCard;
|
||||
case "punchtape":
|
||||
case "punchedtape":
|
||||
case "punched tape":
|
||||
return MediaType.PunchedTape;
|
||||
"aperture"
|
||||
or "aperturecard"
|
||||
or "aperture card" => MediaType.ApertureCard,
|
||||
"jacquardloom"
|
||||
or "jacquardloomcard"
|
||||
or "jacquard loom card" => MediaType.JacquardLoomCard,
|
||||
"magneticstripe"
|
||||
or "magneticstripecard"
|
||||
or "magnetic stripe card" => MediaType.MagneticStripeCard,
|
||||
"opticalphone"
|
||||
or "opticalphonecard"
|
||||
or "optical phonecard" => MediaType.OpticalPhonecard,
|
||||
"punchcard"
|
||||
or "punchedcard"
|
||||
or "punched card" => MediaType.PunchedCard,
|
||||
"punchtape"
|
||||
or "punchedtape"
|
||||
or "punched tape" => MediaType.PunchedTape,
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tape
|
||||
|
||||
case "openreel":
|
||||
case "openreeltape":
|
||||
case "open reel tape":
|
||||
return MediaType.OpenReel;
|
||||
case "datacart":
|
||||
case "datacartridge":
|
||||
case "datatapecartridge":
|
||||
case "data tape cartridge":
|
||||
return MediaType.DataCartridge;
|
||||
case "cassette":
|
||||
case "cassettetape":
|
||||
case "cassette tape":
|
||||
return MediaType.Cassette;
|
||||
"openreel"
|
||||
or "openreeltape"
|
||||
or "open reel tape" => MediaType.OpenReel,
|
||||
"datacart"
|
||||
or "datacartridge"
|
||||
or "datatapecartridge"
|
||||
or "data tape cartridge" => MediaType.DataCartridge,
|
||||
"cassette"
|
||||
or "cassettetape"
|
||||
or "cassette tape" => MediaType.Cassette,
|
||||
|
||||
#endregion
|
||||
|
||||
#region Disc / Disc
|
||||
|
||||
case "bd":
|
||||
case "bdrom":
|
||||
case "bd-rom":
|
||||
case "bluray":
|
||||
return MediaType.BluRay;
|
||||
case "cd":
|
||||
case "cdrom":
|
||||
case "cd-rom":
|
||||
return MediaType.CDROM;
|
||||
case "dvd":
|
||||
case "dvd5":
|
||||
case "dvd-5":
|
||||
case "dvd9":
|
||||
case "dvd-9":
|
||||
case "dvdrom":
|
||||
case "dvd-rom":
|
||||
return MediaType.DVD;
|
||||
case "fd":
|
||||
case "floppy":
|
||||
case "floppydisk":
|
||||
case "floppy disk":
|
||||
case "floppy diskette":
|
||||
return MediaType.FloppyDisk;
|
||||
case "floptical":
|
||||
return MediaType.Floptical;
|
||||
case "gd":
|
||||
case "gdrom":
|
||||
case "gd-rom":
|
||||
return MediaType.GDROM;
|
||||
case "hddvd":
|
||||
case "hd-dvd":
|
||||
case "hddvdrom":
|
||||
case "hd-dvd-rom":
|
||||
return MediaType.HDDVD;
|
||||
case "hdd":
|
||||
case "harddisk":
|
||||
case "hard disk":
|
||||
return MediaType.HardDisk;
|
||||
case "bernoullidisk":
|
||||
case "iomegabernoullidisk":
|
||||
case "bernoulli disk":
|
||||
case "iomega bernoulli disk":
|
||||
return MediaType.IomegaBernoulliDisk;
|
||||
case "jaz":
|
||||
case "iomegajaz":
|
||||
case "iomega jaz":
|
||||
return MediaType.IomegaJaz;
|
||||
case "zip":
|
||||
case "zipdisk":
|
||||
case "iomegazip":
|
||||
case "iomega zip":
|
||||
return MediaType.IomegaZip;
|
||||
case "ldrom":
|
||||
case "lvrom":
|
||||
case "ld-rom":
|
||||
case "lv-rom":
|
||||
case "laserdisc":
|
||||
case "laservision":
|
||||
case "ld-rom / lv-rom":
|
||||
return MediaType.LaserDisc;
|
||||
case "64dd":
|
||||
case "n64dd":
|
||||
case "64dddisk":
|
||||
case "n64dddisk":
|
||||
case "64dd disk":
|
||||
case "n64dd disk":
|
||||
return MediaType.Nintendo64DD;
|
||||
case "fds":
|
||||
case "famicom":
|
||||
case "nfds":
|
||||
case "nintendofamicom":
|
||||
case "famicomdisksystem":
|
||||
case "famicom disk system":
|
||||
case "famicom disk system disk":
|
||||
return MediaType.NintendoFamicomDiskSystem;
|
||||
case "gc":
|
||||
case "gamecube":
|
||||
case "nintendogamecube":
|
||||
case "nintendo gamecube":
|
||||
case "gamecube disc":
|
||||
case "gamecube game disc":
|
||||
return MediaType.NintendoGameCubeGameDisc;
|
||||
case "wii":
|
||||
case "nintendowii":
|
||||
case "nintendo wii":
|
||||
case "nintendo wii disc":
|
||||
case "wii optical disc":
|
||||
return MediaType.NintendoWiiOpticalDisc;
|
||||
case "wiiu":
|
||||
case "nintendowiiu":
|
||||
case "nintendo wiiu":
|
||||
case "nintendo wiiu disc":
|
||||
case "wiiu optical disc":
|
||||
case "wii u optical disc":
|
||||
return MediaType.NintendoWiiUOpticalDisc;
|
||||
case "umd":
|
||||
return MediaType.UMD;
|
||||
"bd"
|
||||
or "bdrom"
|
||||
or "bd-rom"
|
||||
or "bluray" => MediaType.BluRay,
|
||||
"cd"
|
||||
or "cdrom"
|
||||
or "cd-rom" => MediaType.CDROM,
|
||||
"dvd"
|
||||
or "dvd5"
|
||||
or "dvd-5"
|
||||
or "dvd9"
|
||||
or "dvd-9"
|
||||
or "dvdrom"
|
||||
or "dvd-rom" => MediaType.DVD,
|
||||
"fd"
|
||||
or "floppy"
|
||||
or "floppydisk"
|
||||
or "floppy disk"
|
||||
or "floppy diskette" => MediaType.FloppyDisk,
|
||||
"floptical" => MediaType.Floptical,
|
||||
"gd"
|
||||
or "gdrom"
|
||||
or "gd-rom" => MediaType.GDROM,
|
||||
"hddvd"
|
||||
or "hd-dvd"
|
||||
or "hddvdrom"
|
||||
or "hd-dvd-rom" => MediaType.HDDVD,
|
||||
"hdd"
|
||||
or "harddisk"
|
||||
or "hard disk" => MediaType.HardDisk,
|
||||
"bernoullidisk"
|
||||
or "iomegabernoullidisk"
|
||||
or "bernoulli disk"
|
||||
or "iomega bernoulli disk" => MediaType.IomegaBernoulliDisk,
|
||||
"jaz"
|
||||
or "iomegajaz"
|
||||
or "iomega jaz" => MediaType.IomegaJaz,
|
||||
"zip"
|
||||
or "zipdisk"
|
||||
or "iomegazip"
|
||||
or "iomega zip" => MediaType.IomegaZip,
|
||||
"ldrom"
|
||||
or "lvrom"
|
||||
or "ld-rom"
|
||||
or "lv-rom"
|
||||
or "laserdisc"
|
||||
or "laservision"
|
||||
or "ld-rom / lv-rom" => MediaType.LaserDisc,
|
||||
"64dd"
|
||||
or "n64dd"
|
||||
or "64dddisk"
|
||||
or "n64dddisk"
|
||||
or "64dd disk"
|
||||
or "n64dd disk" => MediaType.Nintendo64DD,
|
||||
"fds"
|
||||
or "famicom"
|
||||
or "nfds"
|
||||
or "nintendofamicom"
|
||||
or "famicomdisksystem"
|
||||
or "famicom disk system"
|
||||
or "famicom disk system disk" => MediaType.NintendoFamicomDiskSystem,
|
||||
"gc"
|
||||
or "gamecube"
|
||||
or "nintendogamecube"
|
||||
or "nintendo gamecube"
|
||||
or "gamecube disc"
|
||||
or "gamecube game disc" => MediaType.NintendoGameCubeGameDisc,
|
||||
"wii"
|
||||
or "nintendowii"
|
||||
or "nintendo wii"
|
||||
or "nintendo wii disc"
|
||||
or "wii optical disc" => MediaType.NintendoWiiOpticalDisc,
|
||||
"wiiu"
|
||||
or "nintendowiiu"
|
||||
or "nintendo wiiu"
|
||||
or "nintendo wiiu disc"
|
||||
or "wiiu optical disc"
|
||||
or "wii u optical disc" => MediaType.NintendoWiiUOpticalDisc,
|
||||
"umd" => MediaType.UMD,
|
||||
|
||||
#endregion
|
||||
|
||||
// Unsorted Formats
|
||||
case "cartridge":
|
||||
return MediaType.Cartridge;
|
||||
case "ced":
|
||||
case "rcaced":
|
||||
case "rca ced":
|
||||
case "videodisc":
|
||||
case "rca videodisc":
|
||||
return MediaType.CED;
|
||||
"cartridge" => MediaType.Cartridge,
|
||||
"ced"
|
||||
or "rcaced"
|
||||
or "rca ced"
|
||||
or "videodisc"
|
||||
or "rca videodisc" => MediaType.CED,
|
||||
|
||||
default:
|
||||
return MediaType.NONE;
|
||||
}
|
||||
_ => MediaType.NONE,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -14,38 +14,45 @@ namespace MPF.Core.Data
|
||||
public const string StopDumping = "Stop Dumping";
|
||||
|
||||
// Byte arrays for signatures
|
||||
public static readonly byte[] SaturnSectorZeroStart = new byte[] { 0x53, 0x45, 0x47, 0x41, 0x20, 0x53, 0x45, 0x47, 0x41, 0x53, 0x41, 0x54, 0x55, 0x52, 0x4E, 0x20 };
|
||||
public static readonly byte[] SaturnSectorZeroStart = [0x53, 0x45, 0x47, 0x41, 0x20, 0x53, 0x45, 0x47, 0x41, 0x53, 0x41, 0x54, 0x55, 0x52, 0x4E, 0x20];
|
||||
|
||||
// Private lists of known drive speed ranges
|
||||
private static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
private static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> 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 };
|
||||
// Lists of known drive speed ranges
|
||||
#if NET20 || NET35 || NET40
|
||||
public static IList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
public static IList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
public static IList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
public static IList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
|
||||
public static IList<int> Unknown { get; } = new List<int> { 1 };
|
||||
#else
|
||||
public static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
public static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
public static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
public static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
|
||||
public static IReadOnlyList<int> Unknown { get; } = new List<int> { 1 };
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get list of all drive speeds for a given MediaType
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType? that represents the current item</param>
|
||||
/// <returns>Read-only list of drive speeds</returns>
|
||||
#if NET20 || NET35 || NET40
|
||||
public static IList<int> GetSpeedsForMediaType(MediaType? type)
|
||||
#else
|
||||
public static IReadOnlyList<int> GetSpeedsForMediaType(MediaType? type)
|
||||
#endif
|
||||
{
|
||||
switch (type)
|
||||
return type switch
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
case MediaType.GDROM:
|
||||
return CD;
|
||||
case MediaType.DVD:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return DVD;
|
||||
case MediaType.HDDVD:
|
||||
return HDDVD;
|
||||
case MediaType.BluRay:
|
||||
return BD;
|
||||
default:
|
||||
return Unknown;
|
||||
}
|
||||
MediaType.CDROM
|
||||
or MediaType.GDROM => CD,
|
||||
MediaType.DVD
|
||||
or MediaType.NintendoGameCubeGameDisc
|
||||
or MediaType.NintendoWiiOpticalDisc => DVD,
|
||||
MediaType.HDDVD => HDDVD,
|
||||
MediaType.BluRay => BD,
|
||||
_ => Unknown,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,65 +61,6 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public static class Template
|
||||
{
|
||||
// Manual information
|
||||
|
||||
public const string TitleField = "Title";
|
||||
public const string ForeignTitleField = "Foreign Title (Non-latin)";
|
||||
public const string DiscNumberField = "Disc Number / Letter";
|
||||
public const string DiscTitleField = "Disc Title";
|
||||
public const string SystemField = "System";
|
||||
public const string MediaTypeField = "Media Type";
|
||||
public const string CategoryField = "Category";
|
||||
public const string RegionField = "Region";
|
||||
public const string LanguagesField = "Languages";
|
||||
public const string PlaystationLanguageSelectionViaField = "Language Selection Via";
|
||||
public const string DiscSerialField = "Disc Serial";
|
||||
public const string BarcodeField = "Barcode";
|
||||
public const string CommentsField = "Comments";
|
||||
public const string ContentsField = "Contents";
|
||||
public const string VersionField = "Version";
|
||||
public const string EditionField = "Edition/Release";
|
||||
public const string PlayStation3WiiDiscKeyField = "Disc Key";
|
||||
public const string PlayStation3DiscIDField = "Disc ID";
|
||||
public const string GameCubeWiiBCAField = "BCA";
|
||||
public const string CopyProtectionField = "Copy Protection";
|
||||
public const string MasteringRingField = "Mastering Code (laser branded/etched)";
|
||||
public const string MasteringSIDField = "Mastering SID Code";
|
||||
public const string MouldSIDField = "Mould SID Code";
|
||||
public const string AdditionalMouldField = "Additional Mould";
|
||||
public const string ToolstampField = "Toolstamp or Mastering Code (engraved/stamped)";
|
||||
|
||||
// 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 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)";
|
||||
public const string WriteOffsetField = "Write Offset";
|
||||
public const string LayerbreakField = "Layerbreak";
|
||||
public const string EXEDateBuildDate = "EXE/Build Date";
|
||||
public const string HeaderField = "Header";
|
||||
public const string PICField = "Permanent Information & Control (PIC)";
|
||||
public const string PlayStationEDCField = "EDC";
|
||||
public const string PlayStationAntiModchipField = "Anti-modchip";
|
||||
public const string PlayStationLibCryptField = "LibCrypt";
|
||||
public const string XBOXSSRanges = "Security Sector Ranges";
|
||||
|
||||
// Default values
|
||||
|
||||
public const string RequiredValue = "(REQUIRED)";
|
||||
public const string RequiredIfExistsValue = "(REQUIRED, IF EXISTS)";
|
||||
public const string OptionalValue = "(OPTIONAL)";
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using Microsoft.Management.Infrastructure;
|
||||
using Microsoft.Management.Infrastructure.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#endif
|
||||
using MPF.Core.Converters;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
@@ -17,7 +15,6 @@ namespace MPF.Core.Data
|
||||
/// 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
|
||||
@@ -32,12 +29,12 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Drive partition format
|
||||
/// </summary>
|
||||
public string DriveFormat { get; private set; } = null;
|
||||
public string? DriveFormat { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Windows drive path
|
||||
/// </summary>
|
||||
public string Name { get; private set; } = null;
|
||||
public string? Name { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Represents if Windows has marked the drive as active
|
||||
@@ -53,7 +50,7 @@ namespace MPF.Core.Data
|
||||
/// Media label as read by Windows
|
||||
/// </summary>
|
||||
/// <remarks>The try/catch is needed because Windows will throw an exception if the drive is not marked as active</remarks>
|
||||
public string VolumeLabel { get; private set; } = null;
|
||||
public string? VolumeLabel { get; private set; } = null;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -61,31 +58,50 @@ namespace MPF.Core.Data
|
||||
|
||||
/// <summary>
|
||||
/// Media label as read by Windows, formatted to avoid odd outputs
|
||||
/// If no volume label present, use PSX or PS2 serial if valid
|
||||
/// Otherwise, use "track" as volume label
|
||||
/// </summary>
|
||||
public string FormattedVolumeLabel
|
||||
public string? FormattedVolumeLabel
|
||||
{
|
||||
get
|
||||
{
|
||||
string volumeLabel = Template.DiscNotDetected;
|
||||
if (this.MarkedActive)
|
||||
string? volumeLabel = Template.DiscNotDetected;
|
||||
if (!this.MarkedActive)
|
||||
return volumeLabel;
|
||||
|
||||
if (!string.IsNullOrEmpty(this.VolumeLabel))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
|
||||
volumeLabel = "track";
|
||||
else
|
||||
volumeLabel = this.VolumeLabel;
|
||||
volumeLabel = this.VolumeLabel;
|
||||
}
|
||||
else
|
||||
{
|
||||
// No Volume Label found, fallback to something sensible
|
||||
switch (this.GetRedumpSystem(null))
|
||||
{
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
InfoTool.GetPlayStationExecutableInfo(this.Name, out string? serial, out _, out _);
|
||||
volumeLabel = serial ?? "track";
|
||||
break;
|
||||
|
||||
default:
|
||||
volumeLabel = "track";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (char c in Path.GetInvalidFileNameChars())
|
||||
volumeLabel = volumeLabel.Replace(c, '_');
|
||||
volumeLabel = volumeLabel?.Replace(c, '_');
|
||||
|
||||
return volumeLabel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Windows drive letter
|
||||
/// Read-only access to the drive letter
|
||||
/// </summary>
|
||||
public char Letter => this.Name == null || this.Name.Length == 0 ? '\0' : this.Name[0];
|
||||
/// <remarks>Should only be used in UI applications</remarks>
|
||||
public char? Letter => this.Name?[0] ?? '\0';
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -99,7 +115,7 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
/// <param name="driveType">InternalDriveType value representing the drive type</param>
|
||||
/// <param name="devicePath">Path to the device according to the local machine</param>
|
||||
public static Drive Create(InternalDriveType? driveType, string devicePath)
|
||||
public static Drive? Create(InternalDriveType? driveType, string devicePath)
|
||||
{
|
||||
// Create a new, empty drive object
|
||||
var drive = new Drive()
|
||||
@@ -108,16 +124,12 @@ namespace MPF.Core.Data
|
||||
};
|
||||
|
||||
// If we have an invalid device path, return null
|
||||
if (string.IsNullOrWhiteSpace(devicePath))
|
||||
if (string.IsNullOrEmpty(devicePath))
|
||||
return null;
|
||||
|
||||
// Sanitize a Windows-formatted long device path
|
||||
if (devicePath.StartsWith("\\\\.\\"))
|
||||
#if NET48
|
||||
devicePath = devicePath.Substring("\\\\.\\".Length);
|
||||
#else
|
||||
devicePath = devicePath["\\\\.\\".Length..];
|
||||
#endif
|
||||
|
||||
// Create and validate the drive info object
|
||||
var driveInfo = new DriveInfo(devicePath);
|
||||
@@ -134,7 +146,7 @@ namespace MPF.Core.Data
|
||||
/// Populate all fields from a DriveInfo object
|
||||
/// </summary>
|
||||
/// <param name="driveInfo">DriveInfo object to populate from</param>
|
||||
private void PopulateFromDriveInfo(DriveInfo driveInfo)
|
||||
private void PopulateFromDriveInfo(DriveInfo? driveInfo)
|
||||
{
|
||||
// If we have an invalid DriveInfo, just return
|
||||
if (driveInfo == null || driveInfo == default)
|
||||
@@ -167,84 +179,86 @@ namespace MPF.Core.Data
|
||||
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
|
||||
{
|
||||
var drives = GetDriveList(ignoreFixedDrives);
|
||||
drives = drives?.OrderBy(i => i.Letter)?.ToList();
|
||||
drives = [.. drives.OrderBy(i => i == null ? "\0" : i.Name)];
|
||||
return drives;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current media type from drive letter
|
||||
/// </summary>
|
||||
/// <param name="system"></param>
|
||||
/// <returns></returns>
|
||||
public (MediaType?, string) GetMediaType()
|
||||
public (MediaType?, string?) GetMediaType(RedumpSystem? system)
|
||||
{
|
||||
// Take care of the non-optical stuff first
|
||||
// TODO: See if any of these can be more granular, like Optical is
|
||||
if (this.InternalDriveType == Data.InternalDriveType.Floppy)
|
||||
return (MediaType.FloppyDisk, null);
|
||||
else if (this.InternalDriveType == Data.InternalDriveType.HardDisk)
|
||||
return (MediaType.HardDisk, null);
|
||||
else if (this.InternalDriveType == Data.InternalDriveType.Removable)
|
||||
return (MediaType.FlashDrive, null);
|
||||
#if NET6_0_OR_GREATER
|
||||
else
|
||||
return GetMediaTypeFromSize();
|
||||
#else
|
||||
|
||||
// Get the current drive information
|
||||
string deviceId = null;
|
||||
bool loaded = false;
|
||||
try
|
||||
switch (this.InternalDriveType)
|
||||
{
|
||||
// Get the device ID first
|
||||
CimSession session = CimSession.Create(null);
|
||||
var collection = session.QueryInstances("root\\CIMV2", "WQL", $"SELECT * FROM Win32_CDROMDrive WHERE Id = '{this.Letter}:\'");
|
||||
|
||||
foreach (CimInstance instance in collection)
|
||||
{
|
||||
CimKeyedCollection<CimProperty> properties = instance.CimInstanceProperties;
|
||||
deviceId = (string)properties["DeviceID"]?.Value;
|
||||
loaded = (bool)properties["MediaLoaded"]?.Value;
|
||||
}
|
||||
|
||||
// If we got no valid device, we don't care and just return
|
||||
if (deviceId == null)
|
||||
return (null, "Device could not be found");
|
||||
else if (!loaded)
|
||||
return (null, "Device is not reporting media loaded");
|
||||
|
||||
MsftDiscMaster2 discMaster = new MsftDiscMaster2();
|
||||
deviceId = deviceId.ToLower().Replace('\\', '#').Replace('/', '#');
|
||||
string id = null;
|
||||
foreach (var disc in discMaster)
|
||||
{
|
||||
if (disc.ToString().Contains(deviceId))
|
||||
id = disc.ToString();
|
||||
}
|
||||
|
||||
// If we couldn't find the drive, we don't care and return
|
||||
if (id == null)
|
||||
return (null, "Device ID could not be found");
|
||||
|
||||
// Create the required objects for reading from the drive
|
||||
MsftDiscRecorder2 recorder = new MsftDiscRecorder2();
|
||||
recorder.InitializeDiscRecorder(id);
|
||||
MsftDiscFormat2Data dataWriter = new MsftDiscFormat2Data();
|
||||
|
||||
// If the recorder is not supported, just return
|
||||
if (!dataWriter.IsRecorderSupported(recorder))
|
||||
return (null, "IMAPI2 recorder not supported");
|
||||
|
||||
// Otherwise, set the recorder to get information from
|
||||
dataWriter.Recorder = recorder;
|
||||
|
||||
var media = dataWriter.CurrentPhysicalMediaType;
|
||||
return (media.IMAPIToMediaType(), null);
|
||||
case Data.InternalDriveType.Floppy:
|
||||
return (MediaType.FloppyDisk, null);
|
||||
case Data.InternalDriveType.HardDisk:
|
||||
return (MediaType.HardDisk, null);
|
||||
case Data.InternalDriveType.Removable:
|
||||
return (MediaType.FlashDrive, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
// Some systems should default to certain media types
|
||||
switch (system)
|
||||
{
|
||||
return (null, ex.Message);
|
||||
// CD
|
||||
case RedumpSystem.Panasonic3DOInteractiveMultiplayer:
|
||||
case RedumpSystem.PhilipsCDi:
|
||||
case RedumpSystem.SegaDreamcast:
|
||||
case RedumpSystem.SegaSaturn:
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
case RedumpSystem.VideoCD:
|
||||
return (MediaType.CDROM, null);
|
||||
|
||||
// DVD
|
||||
case RedumpSystem.DVDAudio:
|
||||
case RedumpSystem.DVDVideo:
|
||||
case RedumpSystem.MicrosoftXbox:
|
||||
case RedumpSystem.MicrosoftXbox360:
|
||||
return (MediaType.DVD, null);
|
||||
|
||||
// HD-DVD
|
||||
case RedumpSystem.HDDVDVideo:
|
||||
return (MediaType.HDDVD, null);
|
||||
|
||||
// Blu-ray
|
||||
case RedumpSystem.BDVideo:
|
||||
case RedumpSystem.MicrosoftXboxOne:
|
||||
case RedumpSystem.MicrosoftXboxSeriesXS:
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
return (MediaType.BluRay, null);
|
||||
|
||||
// GameCube
|
||||
case RedumpSystem.NintendoGameCube:
|
||||
return (MediaType.NintendoGameCubeGameDisc, null);
|
||||
|
||||
// Wii
|
||||
case RedumpSystem.NintendoWii:
|
||||
return (MediaType.NintendoWiiOpticalDisc, null);
|
||||
|
||||
// WiiU
|
||||
case RedumpSystem.NintendoWiiU:
|
||||
return (MediaType.NintendoWiiUOpticalDisc, null);
|
||||
|
||||
// PSP
|
||||
case RedumpSystem.SonyPlayStationPortable:
|
||||
return (MediaType.UMD, null);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Handle optical media by size and filesystem
|
||||
if (this.TotalSize >= 0 && this.TotalSize <= 800_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
|
||||
return (MediaType.CDROM, null);
|
||||
else if (this.TotalSize > 800_000_000 && this.TotalSize <= 8_540_000_000 && (this.DriveFormat == "CDFS" || this.DriveFormat == "UDF"))
|
||||
return (MediaType.DVD, null);
|
||||
else if (this.TotalSize > 8_540_000_000)
|
||||
return (MediaType.BluRay, null);
|
||||
|
||||
return (null, "Could not determine media type!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -254,29 +268,41 @@ namespace MPF.Core.Data
|
||||
/// <returns></returns>
|
||||
public RedumpSystem? GetRedumpSystem(RedumpSystem? defaultValue)
|
||||
{
|
||||
string drivePath = $"{this.Letter}:\\";
|
||||
|
||||
// If we can't read the media in that drive, we can't do anything
|
||||
if (!Directory.Exists(drivePath))
|
||||
if (string.IsNullOrEmpty(this.Name) || !Directory.Exists(this.Name))
|
||||
return defaultValue;
|
||||
|
||||
// We're going to assume for floppies, HDDs, and removable drives
|
||||
// TODO: Try to be smarter about this
|
||||
if (this.InternalDriveType != Data.InternalDriveType.Optical)
|
||||
return RedumpSystem.IBMPCcompatible;
|
||||
|
||||
// Check volume labels first
|
||||
RedumpSystem? systemFromLabel = GetRedumpSystemFromVolumeLabel();
|
||||
RedumpSystem? systemFromLabel = GetRedumpSystemFromVolumeLabel(this.VolumeLabel);
|
||||
if (systemFromLabel != null)
|
||||
return systemFromLabel;
|
||||
|
||||
// Get a list of files for quicker checking
|
||||
#region Arcade
|
||||
|
||||
// funworld Photo Play
|
||||
if (File.Exists(Path.Combine(this.Name, "PP.INF"))
|
||||
&& Directory.Exists(Path.Combine(this.Name, "PPINC")))
|
||||
{
|
||||
return RedumpSystem.funworldPhotoPlay;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Consoles
|
||||
|
||||
// Bandai Playdia Quick Interactive System
|
||||
try
|
||||
{
|
||||
List<string> files = Directory.EnumerateFiles(drivePath, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
#if NET20 || NET35
|
||||
List<string> files = [.. Directory.GetFiles(this.Name, "*", SearchOption.TopDirectoryOnly)];
|
||||
#else
|
||||
List<string> files = Directory.EnumerateFiles(this.Name, "*", SearchOption.TopDirectoryOnly).ToList();
|
||||
#endif
|
||||
|
||||
if (files.Any(f => f.EndsWith(".AJS", StringComparison.OrdinalIgnoreCase))
|
||||
&& files.Any(f => f.EndsWith(".GLB", StringComparison.OrdinalIgnoreCase)))
|
||||
@@ -286,8 +312,37 @@ namespace MPF.Core.Data
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Bandai Pippin
|
||||
if (File.Exists(Path.Combine(this.Name, "PippinAuthenticationFile")))
|
||||
{
|
||||
return RedumpSystem.BandaiPippin;
|
||||
}
|
||||
|
||||
// Commodore CDTV/CD32
|
||||
#if NET20 || NET35
|
||||
if (File.Exists(Path.Combine(Path.Combine(this.Name, "S"), "STARTUP-SEQUENCE")))
|
||||
#else
|
||||
if (File.Exists(Path.Combine(this.Name, "S", "STARTUP-SEQUENCE")))
|
||||
#endif
|
||||
{
|
||||
if (File.Exists(Path.Combine(this.Name, "CDTV.TM")))
|
||||
return RedumpSystem.CommodoreAmigaCDTV;
|
||||
else
|
||||
return RedumpSystem.CommodoreAmigaCD32;
|
||||
}
|
||||
|
||||
// Mattel HyperScan -- TODO: May need case-insensitivity added
|
||||
if (File.Exists(Path.Combine(this.Name, "hyper.exe")))
|
||||
{
|
||||
return RedumpSystem.MattelHyperScan;
|
||||
}
|
||||
|
||||
// Mattel Fisher-Price iXL
|
||||
if (File.Exists(Path.Combine(drivePath, "iXL", "iXLUpdater.exe")))
|
||||
#if NET20 || NET35
|
||||
if (File.Exists(Path.Combine(Path.Combine(this.Name, "iXL"), "iXLUpdater.exe")))
|
||||
#else
|
||||
if (File.Exists(Path.Combine(this.Name, "iXL", "iXLUpdater.exe")))
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.MattelFisherPriceiXL;
|
||||
}
|
||||
@@ -295,8 +350,12 @@ namespace MPF.Core.Data
|
||||
// Microsoft Xbox 360
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "$SystemUpdate"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "$SystemUpdate")).Any()
|
||||
if (Directory.Exists(Path.Combine(this.Name, "$SystemUpdate"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
|
||||
#endif
|
||||
&& this.TotalSize <= 500_000_000)
|
||||
{
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
@@ -307,8 +366,12 @@ namespace MPF.Core.Data
|
||||
// Microsoft Xbox One
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "MSXC"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "MSXC")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "MSXC"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "MSXC")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "MSXC")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.MicrosoftXboxOne;
|
||||
}
|
||||
@@ -316,35 +379,30 @@ namespace MPF.Core.Data
|
||||
catch { }
|
||||
|
||||
// Sega Dreamcast
|
||||
if (File.Exists(Path.Combine(drivePath, "IP.BIN")))
|
||||
if (File.Exists(Path.Combine(this.Name, "IP.BIN")))
|
||||
{
|
||||
return RedumpSystem.SegaDreamcast;
|
||||
}
|
||||
|
||||
// Sega Mega-CD / Sega-CD
|
||||
if (File.Exists(Path.Combine(drivePath, "_BOOT", "IP.BIN"))
|
||||
|| File.Exists(Path.Combine(drivePath, "_BOOT", "SP.BIN"))
|
||||
|| File.Exists(Path.Combine(drivePath, "_BOOT", "SP_AS.BIN"))
|
||||
|| File.Exists(Path.Combine(drivePath, "FILESYSTEM.BIN")))
|
||||
#if NET20 || NET35
|
||||
if (File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "IP.BIN"))
|
||||
|| File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "SP.BIN"))
|
||||
|| File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "SP_AS.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
|
||||
#else
|
||||
if (File.Exists(Path.Combine(this.Name, "_BOOT", "IP.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP_AS.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.SegaMegaCDSegaCD;
|
||||
}
|
||||
|
||||
// Sega Saturn
|
||||
try
|
||||
{
|
||||
byte[] sector = ReadSector(0);
|
||||
if (sector != null)
|
||||
{
|
||||
if (sector.StartsWith(Interface.SaturnSectorZeroStart))
|
||||
return RedumpSystem.SegaSaturn;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Sony PlayStation and Sony PlayStation 2
|
||||
string psxExePath = Path.Combine(drivePath, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(drivePath, "SYSTEM.CNF");
|
||||
string psxExePath = Path.Combine(this.Name, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(this.Name, "SYSTEM.CNF");
|
||||
if (File.Exists(systemCnfPath))
|
||||
{
|
||||
// Check for either BOOT or BOOT2
|
||||
@@ -362,9 +420,9 @@ namespace MPF.Core.Data
|
||||
// Sony PlayStation 3
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "PS3_GAME"))
|
||||
|| Directory.Exists(Path.Combine(drivePath, "PS3_UPDATE"))
|
||||
|| File.Exists(Path.Combine(drivePath, "PS3_DISC.SFB")))
|
||||
if (Directory.Exists(Path.Combine(this.Name, "PS3_GAME"))
|
||||
|| Directory.Exists(Path.Combine(this.Name, "PS3_UPDATE"))
|
||||
|| File.Exists(Path.Combine(this.Name, "PS3_DISC.SFB")))
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
}
|
||||
@@ -383,13 +441,17 @@ namespace MPF.Core.Data
|
||||
// Is used as an on-disc update for the base game app without needing to get update from the internet.
|
||||
// "/addcont/GAME_SERIAL/CONTENT_ID/ac.pkg" can be found in Redump entry 97619.
|
||||
// Originally on disc as "/addcont/CUSA00288/FFXIVEXPS400001A/ac.pkg".
|
||||
if (File.Exists(Path.Combine(drivePath, "PS4", "UPDATE", "PS4UPDATE.PUP")))
|
||||
#if NET20 || NET35
|
||||
if (File.Exists(Path.Combine(Path.Combine(Path.Combine(this.Name, "PS4"), "UPDATE"), "PS4UPDATE.PUP")))
|
||||
#else
|
||||
if (File.Exists(Path.Combine(this.Name, "PS4", "UPDATE", "PS4UPDATE.PUP")))
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.SonyPlayStation4;
|
||||
}
|
||||
|
||||
// V.Tech V.Flash / V.Smile Pro
|
||||
if (File.Exists(Path.Combine(drivePath, "0SYSTEM")))
|
||||
if (File.Exists(Path.Combine(this.Name, "0SYSTEM")))
|
||||
{
|
||||
return RedumpSystem.VTechVFlashVSmilePro;
|
||||
}
|
||||
@@ -399,7 +461,7 @@ namespace MPF.Core.Data
|
||||
#region Computers
|
||||
|
||||
// Sharp X68000
|
||||
if (File.Exists(Path.Combine(drivePath, "COMMAND.X")))
|
||||
if (File.Exists(Path.Combine(this.Name, "COMMAND.X")))
|
||||
{
|
||||
return RedumpSystem.SharpX68000;
|
||||
}
|
||||
@@ -409,7 +471,7 @@ namespace MPF.Core.Data
|
||||
#region Video Formats
|
||||
|
||||
// BD-Video
|
||||
if (Directory.Exists(Path.Combine(drivePath, "BDMV")))
|
||||
if (Directory.Exists(Path.Combine(this.Name, "BDMV")))
|
||||
{
|
||||
// Technically BD-Audio has this as well, but it's hard to split that out right now
|
||||
return RedumpSystem.BDVideo;
|
||||
@@ -418,14 +480,22 @@ namespace MPF.Core.Data
|
||||
// DVD-Audio and DVD-Video
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "AUDIO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "AUDIO_TS")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "AUDIO_TS"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.DVDAudio;
|
||||
}
|
||||
|
||||
else if (Directory.Exists(Path.Combine(drivePath, "VIDEO_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VIDEO_TS")).Any())
|
||||
else if (Directory.Exists(Path.Combine(this.Name, "VIDEO_TS"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.DVDVideo;
|
||||
}
|
||||
@@ -435,19 +505,42 @@ namespace MPF.Core.Data
|
||||
// HD-DVD-Video
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "HVDVD_TS"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "HVDVD_TS")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "HVDVD_TS"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.HDDVDVideo;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Photo CD
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(this.Name, "PHOTO_CD"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "PHOTO_CD")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "PHOTO_CD")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.PhotoCD;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// VCD
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(drivePath, "VCD"))
|
||||
&& Directory.EnumerateFiles(Path.Combine(drivePath, "VCD")).Any())
|
||||
if (Directory.Exists(Path.Combine(this.Name, "VCD"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "VCD")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VCD")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.VideoCD;
|
||||
}
|
||||
@@ -464,95 +557,53 @@ namespace MPF.Core.Data
|
||||
/// Get the current system from the drive volume label
|
||||
/// </summary>
|
||||
/// <returns>The system based on volume label, null if none detected</returns>
|
||||
public RedumpSystem? GetRedumpSystemFromVolumeLabel()
|
||||
public static RedumpSystem? GetRedumpSystemFromVolumeLabel(string? volumeLabel)
|
||||
{
|
||||
// If the volume label is empty, we can't do anything
|
||||
if (string.IsNullOrWhiteSpace(this.VolumeLabel))
|
||||
if (string.IsNullOrEmpty(volumeLabel))
|
||||
return null;
|
||||
|
||||
// Audio CD
|
||||
if (this.VolumeLabel.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel!.Equals("Audio CD", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.AudioCD;
|
||||
|
||||
// Microsoft Xbox
|
||||
if (this.VolumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("SEP13011042", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox;
|
||||
else if (this.VolumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase))
|
||||
else if (volumeLabel.Equals("SEP13011042072", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox;
|
||||
|
||||
// Microsoft Xbox 360
|
||||
if (this.VolumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("XBOX360", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
else if (this.VolumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase))
|
||||
else if (volumeLabel.Equals("XGD2DVD_NTSC", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
|
||||
// Microsoft Xbox 360 - Too overly broad even if a lot of discs use this
|
||||
//if (this.VolumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
//if (volumeLabel.Equals("CD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
// return RedumpSystem.MicrosoftXbox360; // Also for Xbox One?
|
||||
//if (this.VolumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
//if (volumeLabel.Equals("DVD_ROM", StringComparison.OrdinalIgnoreCase))
|
||||
// return RedumpSystem.MicrosoftXbox360;
|
||||
|
||||
// Sega Mega-CD / Sega-CD
|
||||
if (this.VolumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("Sega_CD", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SegaMegaCDSegaCD;
|
||||
|
||||
// Sony PlayStation 3
|
||||
if (this.VolumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("PS3VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
|
||||
// Sony PlayStation 4
|
||||
if (this.VolumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("PS4VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SonyPlayStation4;
|
||||
|
||||
// Sony PlayStation 5
|
||||
if (this.VolumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
if (volumeLabel.Equals("PS5VOLUME", StringComparison.OrdinalIgnoreCase))
|
||||
return RedumpSystem.SonyPlayStation5;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a sector with a specified size from the drive
|
||||
/// </summary>
|
||||
/// <param name="num">Sector number, non-negative</param>
|
||||
/// <param name="size">Size of a sector in bytes</param>
|
||||
/// <returns>Byte array representing the sector, null on error</returns>
|
||||
public byte[] ReadSector(long num, int size = 2048)
|
||||
{
|
||||
// Missing drive leter is not supported
|
||||
if (string.IsNullOrEmpty(this.Name))
|
||||
return null;
|
||||
|
||||
// We don't support negative sectors
|
||||
if (num < 0)
|
||||
return null;
|
||||
|
||||
// Wrap the following in case of device access errors
|
||||
Stream fs = null;
|
||||
try
|
||||
{
|
||||
// Open the drive as a device
|
||||
fs = File.OpenRead($"\\\\?\\{this.Letter}:");
|
||||
|
||||
// Seek to the start of the sector, if possible
|
||||
long start = num * size;
|
||||
fs.Seek(start, SeekOrigin.Begin);
|
||||
|
||||
// Read and return the sector
|
||||
byte[] buffer = new byte[size];
|
||||
fs.Read(buffer, 0, size);
|
||||
return buffer;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
fs?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh the current drive information based on path
|
||||
/// </summary>
|
||||
@@ -566,20 +617,6 @@ namespace MPF.Core.Data
|
||||
|
||||
#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>
|
||||
@@ -609,7 +646,7 @@ namespace MPF.Core.Data
|
||||
{
|
||||
drives = DriveInfo.GetDrives()
|
||||
.Where(d => desiredDriveTypes.Contains(d.DriveType))
|
||||
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name))
|
||||
.Select(d => Create(EnumConverter.ToInternalDriveType(d.DriveType), d.Name) ?? new Drive())
|
||||
.ToList();
|
||||
}
|
||||
catch
|
||||
@@ -618,6 +655,7 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
// Find and update all floppy drives
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
CimSession session = CimSession.Create(null);
|
||||
@@ -629,8 +667,8 @@ namespace MPF.Core.Data
|
||||
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; } });
|
||||
char devId = (properties["Caption"].Value as string ?? string.Empty)[0];
|
||||
drives.ForEach(d => { if (d?.Name != null && d.Name[0] == devId) { d.InternalDriveType = Data.InternalDriveType.Floppy; } });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -638,6 +676,7 @@ namespace MPF.Core.Data
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
#endif
|
||||
|
||||
return drives;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,25 @@
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Available hashing types
|
||||
/// </summary>
|
||||
public enum Hash
|
||||
{
|
||||
CRC32,
|
||||
#if NET6_0_OR_GREATER
|
||||
CRC64,
|
||||
#endif
|
||||
MD5,
|
||||
SHA1,
|
||||
SHA256,
|
||||
SHA384,
|
||||
SHA512,
|
||||
#if NET6_0_OR_GREATER
|
||||
XxHash32,
|
||||
XxHash64,
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Drive type for dumping
|
||||
/// </summary>
|
||||
@@ -26,6 +46,18 @@
|
||||
// Verification support only
|
||||
CleanRip,
|
||||
DCDumper,
|
||||
PS3CFW,
|
||||
UmdImageCreator,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log level for output
|
||||
/// </summary>
|
||||
public enum LogLevel
|
||||
{
|
||||
USER,
|
||||
VERBOSE,
|
||||
ERROR,
|
||||
SECRET,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,25 +8,23 @@ namespace MPF.Core.Data
|
||||
{
|
||||
public class IniFile : IDictionary<string, string>
|
||||
{
|
||||
private Dictionary<string, string> _keyValuePairs = new Dictionary<string, string>();
|
||||
private Dictionary<string, string> _keyValuePairs = [];
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_keyValuePairs == null)
|
||||
_keyValuePairs = new Dictionary<string, string>();
|
||||
_keyValuePairs ??= [];
|
||||
|
||||
key = key.ToLowerInvariant();
|
||||
if (_keyValuePairs.ContainsKey(key))
|
||||
return _keyValuePairs[key];
|
||||
if (_keyValuePairs.TryGetValue(key, out string? val))
|
||||
return val;
|
||||
|
||||
return null;
|
||||
return string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_keyValuePairs == null)
|
||||
_keyValuePairs = new Dictionary<string, string>();
|
||||
_keyValuePairs ??= [];
|
||||
|
||||
key = key.ToLowerInvariant();
|
||||
_keyValuePairs[key] = value;
|
||||
@@ -81,10 +79,8 @@ namespace MPF.Core.Data
|
||||
if (!File.Exists(path))
|
||||
return false;
|
||||
|
||||
using (var fileStream = File.OpenRead(path))
|
||||
{
|
||||
return Parse(fileStream);
|
||||
}
|
||||
using var fileStream = File.OpenRead(path);
|
||||
return Parse(fileStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -99,45 +95,49 @@ namespace MPF.Core.Data
|
||||
// Keys are case-insensitive by default
|
||||
try
|
||||
{
|
||||
using (StreamReader sr = new StreamReader(stream))
|
||||
using var sr = new StreamReader(stream);
|
||||
string section = string.Empty;
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string section = string.Empty;
|
||||
while (!sr.EndOfStream)
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
|
||||
// Empty lines are skipped
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
string line = sr.ReadLine().Trim();
|
||||
|
||||
// Comments start with ';'
|
||||
if (line.StartsWith(";"))
|
||||
{
|
||||
// No-op, we don't process comments
|
||||
}
|
||||
|
||||
// Section titles are surrounded by square brackets
|
||||
else if (line.StartsWith("["))
|
||||
{
|
||||
section = line.TrimStart('[').TrimEnd(']');
|
||||
}
|
||||
|
||||
// Valid INI lines are in the format key=value
|
||||
else if (line.Contains("="))
|
||||
{
|
||||
// Split the line by '=' for key-value pairs
|
||||
string[] data = line.Split('=');
|
||||
|
||||
// If the value field contains an '=', we need to put them back in
|
||||
string key = data[0].Trim();
|
||||
string value = string.Join("=", data.Skip(1)).Trim();
|
||||
|
||||
// Section names are prepended to the key with a '.' separating
|
||||
if (!string.IsNullOrEmpty(section))
|
||||
key = $"{section}.{key}";
|
||||
|
||||
// Set or overwrite keys in the returned dictionary
|
||||
_keyValuePairs[key.ToLowerInvariant()] = value;
|
||||
}
|
||||
|
||||
// All other lines are ignored
|
||||
// No-op, we don't process empty lines
|
||||
}
|
||||
|
||||
// Comments start with ';'
|
||||
else if (line!.StartsWith(";"))
|
||||
{
|
||||
// No-op, we don't process comments
|
||||
}
|
||||
|
||||
// Section titles are surrounded by square brackets
|
||||
else if (line.StartsWith("["))
|
||||
{
|
||||
section = line.TrimStart('[').TrimEnd(']');
|
||||
}
|
||||
|
||||
// Valid INI lines are in the format key=value
|
||||
else if (line.Contains('='))
|
||||
{
|
||||
// Split the line by '=' for key-value pairs
|
||||
string[] data = line.Split('=');
|
||||
|
||||
// If the value field contains an '=', we need to put them back in
|
||||
string key = data[0].Trim();
|
||||
string value = string.Join("=", data.Skip(1).ToArray()).Trim();
|
||||
|
||||
// Section names are prepended to the key with a '.' separating
|
||||
if (!string.IsNullOrEmpty(section))
|
||||
key = $"{section}.{key}";
|
||||
|
||||
// Set or overwrite keys in the returned dictionary
|
||||
_keyValuePairs[key.ToLowerInvariant()] = value;
|
||||
}
|
||||
|
||||
// All other lines are ignored
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -158,10 +158,8 @@ namespace MPF.Core.Data
|
||||
if (_keyValuePairs == null || _keyValuePairs.Count == 0)
|
||||
return false;
|
||||
|
||||
using (var fileStream = File.OpenWrite(path))
|
||||
{
|
||||
return Write(fileStream);
|
||||
}
|
||||
using var fileStream = File.OpenWrite(path);
|
||||
return Write(fileStream);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,39 +177,37 @@ namespace MPF.Core.Data
|
||||
|
||||
try
|
||||
{
|
||||
using (StreamWriter sw = new StreamWriter(stream))
|
||||
// Order the dictionary by keys to link sections together
|
||||
using var sw = new StreamWriter(stream);
|
||||
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
|
||||
|
||||
string section = string.Empty;
|
||||
foreach (var keyValuePair in orderedKeyValuePairs)
|
||||
{
|
||||
// Order the dictionary by keys to link sections together
|
||||
var orderedKeyValuePairs = _keyValuePairs.OrderBy(kvp => kvp.Key);
|
||||
// Extract the key and value
|
||||
string key = keyValuePair.Key;
|
||||
string value = keyValuePair.Value;
|
||||
|
||||
string section = string.Empty;
|
||||
foreach (var keyValuePair in orderedKeyValuePairs)
|
||||
// We assume '.' is a section name separator
|
||||
if (key.Contains('.'))
|
||||
{
|
||||
// Extract the key and value
|
||||
string key = keyValuePair.Key;
|
||||
string value = keyValuePair.Value;
|
||||
// Split the key by '.'
|
||||
string[] data = keyValuePair.Key.Split('.');
|
||||
|
||||
// We assume '.' is a section name separator
|
||||
if (key.Contains('.'))
|
||||
// If the key contains an '.', we need to put them back in
|
||||
string newSection = data[0].Trim();
|
||||
key = string.Join(".", data.Skip(1).ToArray()).Trim();
|
||||
|
||||
// If we have a new section, write it out
|
||||
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Split the key by '.'
|
||||
string[] data = keyValuePair.Key.Split('.');
|
||||
|
||||
// If the key contains an '.', we need to put them back in
|
||||
string newSection = data[0].Trim();
|
||||
key = string.Join(".", data.Skip(1)).Trim();
|
||||
|
||||
// If we have a new section, write it out
|
||||
if (!string.Equals(newSection, section, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
sw.WriteLine($"[{newSection}]");
|
||||
section = newSection;
|
||||
}
|
||||
sw.WriteLine($"[{newSection}]");
|
||||
section = newSection;
|
||||
}
|
||||
|
||||
// Now write out the key and value in a standardized way
|
||||
sw.WriteLine($"{key}={value}");
|
||||
}
|
||||
|
||||
// Now write out the key and value in a standardized way
|
||||
sw.WriteLine($"{key}={value}");
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -245,7 +241,9 @@ namespace MPF.Core.Data
|
||||
|
||||
public bool TryGetValue(string key, out string value)
|
||||
{
|
||||
return ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out value);
|
||||
bool result = ((IDictionary<string, string>)_keyValuePairs).TryGetValue(key.ToLowerInvariant(), out var temp);
|
||||
value = temp ?? string.Empty;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void Add(KeyValuePair<string, string> item)
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using MPF.Core.Converters;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
public class Options : IDictionary<string, string>
|
||||
public class Options
|
||||
{
|
||||
/// <summary>
|
||||
/// All settings in the form of a dictionary
|
||||
/// </summary>
|
||||
public Dictionary<string, string> Settings { get; private set; }
|
||||
public Dictionary<string, string?> Settings { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if the program is being run with a clean configuration
|
||||
/// </summary>
|
||||
public bool FirstRun
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "FirstRun", true); }
|
||||
set { Settings["FirstRun"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#region Internal Program
|
||||
|
||||
/// <summary>
|
||||
/// Path to Aaru
|
||||
/// </summary>
|
||||
public string AaruPath
|
||||
public string? AaruPath
|
||||
{
|
||||
get { return GetStringSetting(Settings, "AaruPath", "Programs\\Aaru\\Aaru.exe"); }
|
||||
set { Settings["AaruPath"] = value; }
|
||||
@@ -27,7 +34,7 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Path to DiscImageCreator
|
||||
/// </summary>
|
||||
public string DiscImageCreatorPath
|
||||
public string? DiscImageCreatorPath
|
||||
{
|
||||
get { return GetStringSetting(Settings, "DiscImageCreatorPath", "Programs\\Creator\\DiscImageCreator.exe"); }
|
||||
set { Settings["DiscImageCreatorPath"] = value; }
|
||||
@@ -36,7 +43,7 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Path to Redumper
|
||||
/// </summary>
|
||||
public string RedumperPath
|
||||
public string? RedumperPath
|
||||
{
|
||||
get { return GetStringSetting(Settings, "RedumperPath", "Programs\\Redumper\\redumper.exe"); }
|
||||
set { Settings["RedumperPath"] = value; }
|
||||
@@ -49,9 +56,9 @@ namespace MPF.Core.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
string valueString = GetStringSetting(Settings, "InternalProgram", InternalProgram.DiscImageCreator.ToString());
|
||||
var valueString = GetStringSetting(Settings, "InternalProgram", InternalProgram.Redumper.ToString());
|
||||
var valueEnum = EnumConverter.ToInternalProgram(valueString);
|
||||
return valueEnum == InternalProgram.NONE ? InternalProgram.DiscImageCreator : valueEnum;
|
||||
return valueEnum == InternalProgram.NONE ? InternalProgram.Redumper : valueEnum;
|
||||
}
|
||||
set
|
||||
{
|
||||
@@ -93,7 +100,7 @@ namespace MPF.Core.Data
|
||||
/// <summary>
|
||||
/// Default output path for dumps
|
||||
/// </summary>
|
||||
public string DefaultOutputPath
|
||||
public string? DefaultOutputPath
|
||||
{
|
||||
get { return GetStringSetting(Settings, "DefaultOutputPath", "ISO"); }
|
||||
set { Settings["DefaultOutputPath"] = value; }
|
||||
@@ -106,8 +113,8 @@ namespace MPF.Core.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
string valueString = GetStringSetting(Settings, "DefaultSystem", null);
|
||||
var valueEnum = Extensions.ToRedumpSystem(valueString);
|
||||
var valueString = GetStringSetting(Settings, "DefaultSystem", null);
|
||||
var valueEnum = Extensions.ToRedumpSystem(valueString ?? string.Empty);
|
||||
return valueEnum;
|
||||
}
|
||||
set
|
||||
@@ -314,10 +321,28 @@ namespace MPF.Core.Data
|
||||
/// </summary>
|
||||
public bool RedumperEnableVerbose
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "RedumperEnableVerbose", false); }
|
||||
get { return GetBooleanSetting(Settings, "RedumperEnableVerbose", true); }
|
||||
set { Settings["RedumperEnableVerbose"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable BE reading by default with Redumper
|
||||
/// </summary>
|
||||
public bool RedumperUseBEReading
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "RedumperUseBEReading", false); }
|
||||
set { Settings["RedumperUseBEReading"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable generic drive type by default with Redumper
|
||||
/// </summary>
|
||||
public bool RedumperUseGenericDriveType
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "RedumperUseGenericDriveType", false); }
|
||||
set { Settings["RedumperUseGenericDriveType"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default number of rereads
|
||||
/// </summary>
|
||||
@@ -430,6 +455,15 @@ namespace MPF.Core.Data
|
||||
set { Settings["ToolsInSeparateWindow"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add the dump filename as a suffix to the auto-generated files
|
||||
/// </summary>
|
||||
public bool AddFilenameSuffix
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "AddFilenameSuffix", false); }
|
||||
set { Settings["AddFilenameSuffix"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Output the compressed JSON version of the submission info
|
||||
/// </summary>
|
||||
@@ -457,6 +491,24 @@ namespace MPF.Core.Data
|
||||
set { Settings["CompressLogFiles"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delete unnecessary files to reduce space
|
||||
/// </summary>
|
||||
public bool DeleteUnnecessaryFiles
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "DeleteUnnecessaryFiles", false); }
|
||||
set { Settings["DeleteUnnecessaryFiles"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a PS3 IRD file after dumping PS3 BD-ROM discs
|
||||
/// </summary>
|
||||
public bool CreateIRDAfterDumping
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "CreateIRDAfterDumping", false); }
|
||||
set { Settings["CreateIRDAfterDumping"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Skip Options
|
||||
@@ -510,6 +562,15 @@ namespace MPF.Core.Data
|
||||
set { Settings["IncludeDebugProtectionInformation"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove drive letters from protection scan output
|
||||
/// </summary>
|
||||
public bool HideDriveLetters
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "HideDriveLetters", false); }
|
||||
set { Settings["HideDriveLetters"] = value.ToString(); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Logging Options
|
||||
@@ -536,23 +597,26 @@ namespace MPF.Core.Data
|
||||
|
||||
#region Redump Login Information
|
||||
|
||||
public string RedumpUsername
|
||||
public string? RedumpUsername
|
||||
{
|
||||
get { return GetStringSetting(Settings, "RedumpUsername", ""); }
|
||||
set { Settings["RedumpUsername"] = value; }
|
||||
}
|
||||
|
||||
// TODO: Figure out a way to keep this encrypted in some way, BASE64 to start?
|
||||
public string RedumpPassword
|
||||
public string? RedumpPassword
|
||||
{
|
||||
get { return GetStringSetting(Settings, "RedumpPassword", ""); }
|
||||
get
|
||||
{
|
||||
return GetStringSetting(Settings, "RedumpPassword", "");
|
||||
}
|
||||
set { Settings["RedumpPassword"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a complete set of Redump credentials might exist
|
||||
/// </summary>
|
||||
public bool HasRedumpLogin { get => !string.IsNullOrWhiteSpace(RedumpUsername) && !string.IsNullOrWhiteSpace(RedumpPassword); }
|
||||
public bool HasRedumpLogin { get => !string.IsNullOrEmpty(RedumpUsername) && !string.IsNullOrEmpty(RedumpPassword); }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -560,18 +624,27 @@ namespace MPF.Core.Data
|
||||
/// Constructor taking a dictionary for settings
|
||||
/// </summary>
|
||||
/// <param name="settings"></param>
|
||||
public Options(Dictionary<string, string> settings = null)
|
||||
public Options(Dictionary<string, string?>? settings = null)
|
||||
{
|
||||
this.Settings = settings ?? new Dictionary<string, string>();
|
||||
this.Settings = settings ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor taking an existing Options object
|
||||
/// </summary>
|
||||
/// <param name="source"></param>
|
||||
public Options(Options source)
|
||||
public Options(Options? source)
|
||||
{
|
||||
Settings = new Dictionary<string, string>(source.Settings);
|
||||
Settings = new Dictionary<string, string?>(source?.Settings ?? []);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Accessor for the internal dictionary
|
||||
/// </summary>
|
||||
public string? this[string key]
|
||||
{
|
||||
get => this.Settings[key];
|
||||
set => this.Settings[key] = value;
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
@@ -583,11 +656,11 @@ namespace MPF.Core.Data
|
||||
/// <param name="key">Setting key to get a value for</param>
|
||||
/// <param name="defaultValue">Default value to return if no value is found</param>
|
||||
/// <returns>Setting value if possible, default value otherwise</returns>
|
||||
private bool GetBooleanSetting(Dictionary<string, string> settings, string key, bool defaultValue)
|
||||
private static bool GetBooleanSetting(Dictionary<string, string?> settings, string key, bool defaultValue)
|
||||
{
|
||||
if (settings.ContainsKey(key))
|
||||
{
|
||||
if (Boolean.TryParse(settings[key], out bool value))
|
||||
if (bool.TryParse(settings[key], out bool value))
|
||||
return value;
|
||||
else
|
||||
return defaultValue;
|
||||
@@ -605,11 +678,11 @@ namespace MPF.Core.Data
|
||||
/// <param name="key">Setting key to get a value for</param>
|
||||
/// <param name="defaultValue">Default value to return if no value is found</param>
|
||||
/// <returns>Setting value if possible, default value otherwise</returns>
|
||||
private int GetInt32Setting(Dictionary<string, string> settings, string key, int defaultValue)
|
||||
private static int GetInt32Setting(Dictionary<string, string?> settings, string key, int defaultValue)
|
||||
{
|
||||
if (settings.ContainsKey(key))
|
||||
{
|
||||
if (Int32.TryParse(settings[key], out int value))
|
||||
if (int.TryParse(settings[key], out int value))
|
||||
return value;
|
||||
else
|
||||
return defaultValue;
|
||||
@@ -627,7 +700,7 @@ namespace MPF.Core.Data
|
||||
/// <param name="key">Setting key to get a value for</param>
|
||||
/// <param name="defaultValue">Default value to return if no value is found</param>
|
||||
/// <returns>Setting value if possible, default value otherwise</returns>
|
||||
private string GetStringSetting(Dictionary<string, string> settings, string key, string defaultValue)
|
||||
private static string? GetStringSetting(Dictionary<string, string?> settings, string key, string? defaultValue)
|
||||
{
|
||||
if (settings.ContainsKey(key))
|
||||
return settings[key];
|
||||
@@ -636,45 +709,5 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDictionary implementations
|
||||
|
||||
public ICollection<string> Keys => Settings.Keys;
|
||||
|
||||
public ICollection<string> Values => Settings.Values;
|
||||
|
||||
public int Count => Settings.Count;
|
||||
|
||||
public bool IsReadOnly => ((IDictionary<string, string>)Settings).IsReadOnly;
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get { return (Settings.ContainsKey(key) ? Settings[key] : null); }
|
||||
set { Settings[key] = value; }
|
||||
}
|
||||
|
||||
public bool ContainsKey(string key) => Settings.ContainsKey(key);
|
||||
|
||||
public void Add(string key, string value) => Settings.Add(key, value);
|
||||
|
||||
public bool Remove(string key) => Settings.Remove(key);
|
||||
|
||||
public bool TryGetValue(string key, out string value) => Settings.TryGetValue(key, out value);
|
||||
|
||||
public void Add(KeyValuePair<string, string> item) => Settings.Add(item.Key, item.Value);
|
||||
|
||||
public void Clear() => Settings.Clear();
|
||||
|
||||
public bool Contains(KeyValuePair<string, string> item) => ((IDictionary<string, string>)Settings).Contains(item);
|
||||
|
||||
public void CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) => ((IDictionary<string, string>)Settings).CopyTo(array, arrayIndex);
|
||||
|
||||
public bool Remove(KeyValuePair<string, string> item) => ((IDictionary<string, string>)Settings).Remove(item);
|
||||
|
||||
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() => Settings.GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => Settings.GetEnumerator();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
using System;
|
||||
#if NET20 || NET35
|
||||
using System.Collections.Generic;
|
||||
#else
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
public class ProcessingQueue<T> : IDisposable
|
||||
public sealed class ProcessingQueue<T> : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal queue to hold data to process
|
||||
/// </summary>
|
||||
#if NET20 || NET35
|
||||
private readonly Queue<T> InternalQueue;
|
||||
#else
|
||||
private readonly ConcurrentQueue<T> InternalQueue;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Custom processing step for dequeued data
|
||||
@@ -24,10 +32,20 @@ namespace MPF.Core.Data
|
||||
|
||||
public ProcessingQueue(Action<T> customProcessing)
|
||||
{
|
||||
#if NET20 || NET35
|
||||
this.InternalQueue = new Queue<T>();
|
||||
#else
|
||||
this.InternalQueue = new ConcurrentQueue<T>();
|
||||
#endif
|
||||
this.CustomProcessing = customProcessing;
|
||||
this.TokenSource = new CancellationTokenSource();
|
||||
#if NET20 || NET35
|
||||
Task.Run(() => ProcessQueue());
|
||||
#elif NET40
|
||||
Task.Factory.StartNew(() => ProcessQueue());
|
||||
#else
|
||||
Task.Run(() => ProcessQueue(), this.TokenSource.Token);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,10 +57,10 @@ namespace MPF.Core.Data
|
||||
/// Enqueue a new item for processing
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
public void Enqueue(T item)
|
||||
public void Enqueue(T? item)
|
||||
{
|
||||
// Only accept new data when not cancelled
|
||||
if (!this.TokenSource.IsCancellationRequested)
|
||||
if (item != null && !this.TokenSource.IsCancellationRequested)
|
||||
this.InternalQueue.Enqueue(item);
|
||||
}
|
||||
|
||||
@@ -54,7 +72,11 @@ namespace MPF.Core.Data
|
||||
while (true)
|
||||
{
|
||||
// Nothing in the queue means we get to idle
|
||||
if (this.InternalQueue.Count == 0)
|
||||
#if NET20 || NET35
|
||||
if (InternalQueue.Count == 0)
|
||||
#else
|
||||
if (InternalQueue.IsEmpty)
|
||||
#endif
|
||||
{
|
||||
if (this.TokenSource.IsCancellationRequested)
|
||||
break;
|
||||
@@ -63,12 +85,17 @@ namespace MPF.Core.Data
|
||||
continue;
|
||||
}
|
||||
|
||||
#if NET20 || NET35
|
||||
// Get the next item from the queue and invoke the lambda, if possible
|
||||
this.CustomProcessing?.Invoke(this.InternalQueue.Dequeue());
|
||||
#else
|
||||
// Get the next item from the queue
|
||||
if (!this.InternalQueue.TryDequeue(out T nextItem))
|
||||
if (!this.InternalQueue.TryDequeue(out var nextItem))
|
||||
continue;
|
||||
|
||||
// Invoke the lambda, if possible
|
||||
this.CustomProcessing?.Invoke(nextItem);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
namespace MPF.Core.Data
|
||||
using System;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Generic success/failure result object, with optional message
|
||||
/// </summary>
|
||||
public class Result
|
||||
public class Result : System.EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Internal representation of success
|
||||
@@ -24,25 +26,30 @@
|
||||
/// <summary>
|
||||
/// Create a default success result with no message
|
||||
/// </summary>
|
||||
public static Result Success() => new Result(true, "");
|
||||
public static Result Success() => new(true, "");
|
||||
|
||||
/// <summary>
|
||||
/// Create a success result with a custom message
|
||||
/// </summary>
|
||||
/// <param name="message">String to add as a message</param>
|
||||
public static Result Success(string message) => new Result(true, message);
|
||||
public static Result Success(string? message) => new(true, message ?? string.Empty);
|
||||
|
||||
/// <summary>
|
||||
/// Create a default failure result with no message
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Result Failure() => new Result(false, "");
|
||||
public static Result Failure() => new(false, "");
|
||||
|
||||
/// <summary>
|
||||
/// Create a failure result with a custom message
|
||||
/// </summary>
|
||||
/// <param name="message">String to add as a message</param>
|
||||
public static Result Failure(string message) => new Result(false, message);
|
||||
public static Result Failure(string? message) => new(false, message ?? string.Empty);
|
||||
|
||||
internal static Result Success(object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Results can be compared to boolean values based on the success value
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Data
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains information specific to an XGD disc
|
||||
/// </summary>
|
||||
public class XgdInfo
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the information in this object is fully instantiated or not
|
||||
/// </summary>
|
||||
public bool Initialized { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Raw XMID/XeMID string that all other information is derived from
|
||||
/// </summary>
|
||||
public string RawXMID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// XGD1 XMID
|
||||
/// </summary>
|
||||
public SabreTools.Models.Xbox.XMID XMID { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// XGD2/3 XeMID
|
||||
/// </summary>
|
||||
public SabreTools.Models.Xbox.XeMID XeMID { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Populate a set of XGD information from a Master ID (XMID/XeMID) string
|
||||
/// </summary>
|
||||
/// <param name="xmid">XMID/XeMID string representing the DMI information</param>
|
||||
public XgdInfo(string xmid)
|
||||
{
|
||||
this.Initialized = false;
|
||||
if (string.IsNullOrWhiteSpace(xmid))
|
||||
return;
|
||||
|
||||
this.RawXMID = xmid.TrimEnd('\0');
|
||||
if (string.IsNullOrWhiteSpace(this.RawXMID))
|
||||
return;
|
||||
|
||||
// XGD1 information is 8 characters
|
||||
if (this.RawXMID.Length == 8)
|
||||
this.Initialized = ParseXGD1XMID(this.RawXMID);
|
||||
|
||||
// XGD2/3 information is semi-variable length
|
||||
else if (this.RawXMID.Length == 13 || this.RawXMID.Length == 14 || this.RawXMID.Length == 21 || this.RawXMID.Length == 22)
|
||||
this.Initialized = ParseXGD23XeMID(this.RawXMID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the human-readable serial string
|
||||
/// </summary>
|
||||
/// <returns>Formatted serial string, null on error</returns>
|
||||
public string GetSerial()
|
||||
{
|
||||
if (!this.Initialized)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// XGD1 doesn't use PlatformIdentifier
|
||||
if (XMID != null)
|
||||
return $"{XMID.PublisherIdentifier}-{XMID.GameID}";
|
||||
|
||||
// XGD2/3 uses a specific identifier
|
||||
else if (XeMID?.PlatformIdentifier == '2')
|
||||
return $"{XeMID.PublisherIdentifier}-{XeMID.PlatformIdentifier}{XeMID.GameID}";
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the human-readable version string
|
||||
/// </summary>
|
||||
/// <returns>Formatted version string, null on error</returns>
|
||||
/// <remarks>This may differ for XGD2/3 in the future</remarks>
|
||||
public string GetVersion()
|
||||
{
|
||||
if (!this.Initialized)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// XGD1 doesn't use PlatformIdentifier
|
||||
if (XMID != null)
|
||||
return $"1.{XMID.VersionNumber}";
|
||||
|
||||
// XGD2/3 uses a specific identifier
|
||||
else if (XeMID?.PlatformIdentifier == '2')
|
||||
return $"1.{XeMID.SKU}";
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an XGD1 XMID string
|
||||
/// </summary>
|
||||
/// <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 rawXmid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var xmid = new SabreTools.Serialization.Files.XMID().Deserialize(rawXmid);
|
||||
if (xmid == null)
|
||||
return false;
|
||||
|
||||
this.XMID = xmid;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an XGD2/3 XeMID string
|
||||
/// </summary>
|
||||
/// <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 rawXemid)
|
||||
{
|
||||
try
|
||||
{
|
||||
var xemid = new SabreTools.Serialization.Files.XeMID().Deserialize(rawXemid);
|
||||
if (xemid == null)
|
||||
return false;
|
||||
|
||||
this.XeMID = xemid;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Determine the region based on the XGD serial character
|
||||
/// </summary>
|
||||
/// <param name="region">Character denoting the region</param>
|
||||
/// <returns>Region, if possible</returns>
|
||||
public static Region? GetRegion(char region)
|
||||
{
|
||||
switch (region)
|
||||
{
|
||||
case 'W': return Region.World;
|
||||
case 'A': return Region.UnitedStatesOfAmerica;
|
||||
case 'J': return Region.JapanAsia;
|
||||
case 'E': return Region.Europe;
|
||||
case 'K': return Region.USAJapan;
|
||||
case 'L': return Region.USAEurope;
|
||||
case 'H': return Region.JapanEurope;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,15 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BurnOutSharp;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Modules;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.Modules;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Library
|
||||
namespace MPF.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the state of all settings to be used during dumping
|
||||
@@ -30,7 +31,7 @@ namespace MPF.Library
|
||||
/// <summary>
|
||||
/// Drive object representing the current drive
|
||||
/// </summary>
|
||||
public Drive Drive { get; private set; }
|
||||
public Drive? Drive { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected system
|
||||
@@ -50,36 +51,48 @@ namespace MPF.Library
|
||||
/// <summary>
|
||||
/// Options object representing user-defined options
|
||||
/// </summary>
|
||||
public Core.Data.Options Options { get; private set; }
|
||||
public Data.Options Options { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Parameters object representing what to send to the internal program
|
||||
/// </summary>
|
||||
public BaseParameters Parameters { get; private set; }
|
||||
public BaseParameters? Parameters { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Generic way of reporting a message
|
||||
/// </summary>
|
||||
public EventHandler<string> ReportStatus;
|
||||
#if NET20 || NET35 || NET40
|
||||
public EventHandler<BaseParameters.StringEventArgs>? ReportStatus;
|
||||
#else
|
||||
public EventHandler<string>? ReportStatus;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Queue of items that need to be logged
|
||||
/// </summary>
|
||||
private ProcessingQueue<string> outputQueue;
|
||||
private ProcessingQueue<string>? outputQueue;
|
||||
|
||||
/// <summary>
|
||||
/// Event handler for data returned from a process
|
||||
/// </summary>
|
||||
private void OutputToLog(object proc, string args) => outputQueue.Enqueue(args);
|
||||
#if NET20 || NET35 || NET40
|
||||
private void OutputToLog(object? proc, BaseParameters.StringEventArgs args) => outputQueue?.Enqueue(args.Value);
|
||||
#else
|
||||
private void OutputToLog(object? proc, string args) => outputQueue?.Enqueue(args);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Process the outputs in the queue
|
||||
/// </summary>
|
||||
private void ProcessOutputs(string nextOutput) => ReportStatus.Invoke(this, nextOutput);
|
||||
#if NET20 || NET35 || NET40
|
||||
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, new BaseParameters.StringEventArgs { Value = nextOutput });
|
||||
#else
|
||||
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, nextOutput);
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -93,112 +106,64 @@ namespace MPF.Library
|
||||
/// <param name="type"></param>
|
||||
/// <param name="internalProgram"></param>
|
||||
/// <param name="parameters"></param>
|
||||
public DumpEnvironment(Core.Data.Options options,
|
||||
public DumpEnvironment(Data.Options options,
|
||||
string outputPath,
|
||||
Drive drive,
|
||||
Drive? drive,
|
||||
RedumpSystem? system,
|
||||
MediaType? type,
|
||||
InternalProgram? internalProgram,
|
||||
string parameters)
|
||||
string? parameters)
|
||||
{
|
||||
// Set options object
|
||||
this.Options = options;
|
||||
Options = options;
|
||||
|
||||
// Output paths
|
||||
this.OutputPath = InfoTool.NormalizeOutputPaths(outputPath, true);
|
||||
OutputPath = InfoTool.NormalizeOutputPaths(outputPath, false);
|
||||
|
||||
// UI information
|
||||
this.Drive = drive;
|
||||
this.System = system ?? options.DefaultSystem;
|
||||
this.Type = type ?? MediaType.NONE;
|
||||
this.InternalProgram = internalProgram ?? options.InternalProgram;
|
||||
|
||||
Drive = drive;
|
||||
System = system ?? options.DefaultSystem;
|
||||
Type = type ?? MediaType.NONE;
|
||||
InternalProgram = internalProgram ?? options.InternalProgram;
|
||||
|
||||
// Dumping program
|
||||
SetParameters(parameters);
|
||||
}
|
||||
|
||||
#region Public Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Adjust output paths if we're using DiscImageCreator
|
||||
/// </summary>
|
||||
public void AdjustPathsForDiscImageCreator()
|
||||
{
|
||||
// Only DiscImageCreator has issues with paths
|
||||
if (this.Parameters.InternalProgram != InternalProgram.DiscImageCreator)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// Normalize the output path
|
||||
string outputPath = InfoTool.NormalizeOutputPaths(this.OutputPath, true);
|
||||
|
||||
// Replace all instances in the output directory
|
||||
string outputDirectory = Path.GetDirectoryName(outputPath);
|
||||
outputDirectory = outputDirectory.Replace(".", "_");
|
||||
|
||||
// Replace all instances in the output filename
|
||||
string outputFilename = Path.GetFileNameWithoutExtension(outputPath);
|
||||
outputFilename = outputFilename.Replace(".", "_");
|
||||
|
||||
// Get the extension for recreating the path
|
||||
string outputExtension = Path.GetExtension(outputPath).TrimStart('.');
|
||||
|
||||
// Rebuild the output path
|
||||
if (!string.IsNullOrWhiteSpace(outputExtension))
|
||||
this.OutputPath = Path.Combine(outputDirectory, $"{outputFilename}.{outputExtension}");
|
||||
else
|
||||
this.OutputPath = Path.Combine(outputDirectory, outputFilename);
|
||||
|
||||
// Assign the path to the filename as well for dumping
|
||||
((Modules.DiscImageCreator.Parameters)this.Parameters).Filename = this.OutputPath;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the parameters object based on the internal program and parameters string
|
||||
/// </summary>
|
||||
/// <param name="parameters">String representation of the parameters</param>
|
||||
public void SetParameters(string parameters)
|
||||
public void SetParameters(string? parameters)
|
||||
{
|
||||
switch (this.InternalProgram)
|
||||
Parameters = InternalProgram switch
|
||||
{
|
||||
// Dumping support
|
||||
case InternalProgram.Aaru:
|
||||
this.Parameters = new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath };
|
||||
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;
|
||||
InternalProgram.Aaru => new Modules.Aaru.Parameters(parameters) { ExecutablePath = Options.AaruPath },
|
||||
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath },
|
||||
InternalProgram.Redumper => new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath },
|
||||
|
||||
// Verification support only
|
||||
case InternalProgram.CleanRip:
|
||||
this.Parameters = new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null };
|
||||
break;
|
||||
InternalProgram.CleanRip => new Modules.CleanRip.Parameters(parameters) { ExecutablePath = null },
|
||||
InternalProgram.DCDumper => null, // TODO: Create correct parameter type when supported
|
||||
InternalProgram.PS3CFW => new Modules.PS3CFW.Parameters(parameters) { ExecutablePath = null },
|
||||
InternalProgram.UmdImageCreator => new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null },
|
||||
|
||||
case InternalProgram.DCDumper:
|
||||
this.Parameters = null; // TODO: Create correct parameter type when supported
|
||||
break;
|
||||
|
||||
case InternalProgram.UmdImageCreator:
|
||||
this.Parameters = new Modules.UmdImageCreator.Parameters(parameters) { ExecutablePath = null };
|
||||
break;
|
||||
// If no dumping program found, set to null
|
||||
InternalProgram.NONE => null,
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
default:
|
||||
this.Parameters = new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath };
|
||||
break;
|
||||
}
|
||||
_ => new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath },
|
||||
};
|
||||
|
||||
// Set system and type
|
||||
this.Parameters.System = this.System;
|
||||
this.Parameters.Type = this.Type;
|
||||
if (Parameters != null)
|
||||
{
|
||||
Parameters.System = System;
|
||||
Parameters.Type = Type;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -206,7 +171,7 @@ namespace MPF.Library
|
||||
/// </summary>
|
||||
/// <param name="driveSpeed">Nullable int representing the drive speed</param>
|
||||
/// <returns>String representing the params, null on error</returns>
|
||||
public string GetFullParameters(int? driveSpeed)
|
||||
public string? GetFullParameters(int? driveSpeed)
|
||||
{
|
||||
// Populate with the correct params for inputs (if we're not on the default option)
|
||||
if (System != null && Type != MediaType.NONE)
|
||||
@@ -216,28 +181,21 @@ namespace MPF.Library
|
||||
return null;
|
||||
|
||||
// Set the proper parameters
|
||||
switch (this.InternalProgram)
|
||||
Parameters = InternalProgram switch
|
||||
{
|
||||
case InternalProgram.Aaru:
|
||||
Parameters = new Modules.Aaru.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
InternalProgram.Aaru => new Modules.Aaru.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
InternalProgram.DiscImageCreator => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
InternalProgram.Redumper => new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
|
||||
case InternalProgram.DiscImageCreator:
|
||||
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;
|
||||
// If no dumping program found, set to null
|
||||
InternalProgram.NONE => null,
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
default:
|
||||
Parameters = new Modules.DiscImageCreator.Parameters(System, Type, Drive.Letter, this.OutputPath, driveSpeed, Options);
|
||||
break;
|
||||
}
|
||||
_ => new Modules.Redumper.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
};
|
||||
|
||||
// Generate and return the param string
|
||||
return Parameters.GenerateParameters();
|
||||
return Parameters?.GenerateParameters();
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -250,26 +208,34 @@ namespace MPF.Library
|
||||
/// <summary>
|
||||
/// Cancel an in-progress dumping process
|
||||
/// </summary>
|
||||
public void CancelDumping() => Parameters.KillInternalProgram();
|
||||
public void CancelDumping() => Parameters?.KillInternalProgram();
|
||||
|
||||
/// <summary>
|
||||
/// Eject the disc using DiscImageCreator
|
||||
/// </summary>
|
||||
public async Task<string> EjectDisc() =>
|
||||
public async Task<string?> EjectDisc() =>
|
||||
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Eject);
|
||||
|
||||
/// <summary>
|
||||
/// Reset the current drive using DiscImageCreator
|
||||
/// </summary>
|
||||
public async Task<string> ResetDrive() =>
|
||||
public async Task<string?> ResetDrive() =>
|
||||
await RunStandaloneDiscImageCreatorCommand(Modules.DiscImageCreator.CommandStrings.Reset);
|
||||
|
||||
/// <summary>
|
||||
/// Execute the initial invocation of the dumping programs
|
||||
/// </summary>
|
||||
/// <param name="progress">Optional result progress callback</param>
|
||||
public async Task<Result> Run(IProgress<Result> progress = null)
|
||||
#if NET40
|
||||
public Result Run(IProgress<Result>? progress = null)
|
||||
#else
|
||||
public async Task<Result> Run(IProgress<Result>? progress = null)
|
||||
#endif
|
||||
{
|
||||
// If we don't have parameters
|
||||
if (Parameters == null)
|
||||
return Result.Failure("Error! Current configuration is not supported!");
|
||||
|
||||
// Check that we have the basics for dumping
|
||||
Result result = IsValidForDump();
|
||||
if (!result)
|
||||
@@ -279,24 +245,29 @@ namespace MPF.Library
|
||||
if (!Options.ToolsInSeparateWindow)
|
||||
{
|
||||
outputQueue = new ProcessingQueue<string>(ProcessOutputs);
|
||||
Parameters.ReportStatus += OutputToLog;
|
||||
if (Parameters.ReportStatus != null)
|
||||
Parameters.ReportStatus += OutputToLog;
|
||||
}
|
||||
|
||||
// Execute internal tool
|
||||
progress?.Report(Result.Success($"Executing {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($"{this.InternalProgram} has finished!"));
|
||||
progress?.Report(Result.Success($"Executing {InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
|
||||
|
||||
// Execute additional tools
|
||||
progress?.Report(Result.Success("Running any additional tools... see log for output!"));
|
||||
result = await Task.Run(() => ExecuteAdditionalTools());
|
||||
progress?.Report(result);
|
||||
var directoryName = Path.GetDirectoryName(OutputPath);
|
||||
if (!string.IsNullOrEmpty(directoryName))
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
#if NET40
|
||||
var executeTask = Task.Factory.StartNew(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
|
||||
executeTask.Wait();
|
||||
#else
|
||||
await Task.Run(() => Parameters.ExecuteInternalProgram(Options.ToolsInSeparateWindow));
|
||||
#endif
|
||||
progress?.Report(Result.Success($"{InternalProgram} has finished!"));
|
||||
|
||||
// Remove event handler if needed
|
||||
if (!Options.ToolsInSeparateWindow)
|
||||
{
|
||||
outputQueue.Dispose();
|
||||
outputQueue?.Dispose();
|
||||
Parameters.ReportStatus -= OutputToLog;
|
||||
}
|
||||
|
||||
@@ -309,50 +280,63 @@ namespace MPF.Library
|
||||
/// <param name="resultProgress">Optional result progress callback</param>
|
||||
/// <param name="protectionProgress">Optional protection progress callback</param>
|
||||
/// <param name="processUserInfo">Optional user prompt to deal with submission information</param>
|
||||
/// <param name="seedInfo">A seed SubmissionInfo object that contains user data</param>
|
||||
/// <returns>Result instance with the outcome</returns>
|
||||
public async Task<Result> VerifyAndSaveDumpOutput(
|
||||
IProgress<Result> resultProgress = null,
|
||||
IProgress<ProtectionProgress> protectionProgress = null,
|
||||
Func<SubmissionInfo, (bool?, SubmissionInfo)> processUserInfo = null)
|
||||
IProgress<Result>? resultProgress = null,
|
||||
IProgress<BinaryObjectScanner.ProtectionProgress>? protectionProgress = null,
|
||||
Func<SubmissionInfo?, (bool?, SubmissionInfo?)>? processUserInfo = null,
|
||||
SubmissionInfo? seedInfo = null)
|
||||
{
|
||||
if (Parameters == null)
|
||||
return Result.Failure("Error! Current configuration is not supported!");
|
||||
|
||||
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);
|
||||
var outputDirectory = Path.GetDirectoryName(OutputPath);
|
||||
var outputFilename = Path.GetFileName(OutputPath);
|
||||
|
||||
// Check to make sure that the output had all the correct files
|
||||
(bool foundFiles, List<string> missingFiles) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, this.Parameters, false);
|
||||
(bool foundFiles, List<string> missingFiles) = Parameters.FoundAllFiles(outputDirectory, outputFilename, false);
|
||||
if (!foundFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", missingFiles)}"));
|
||||
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
|
||||
return Result.Failure("Error! Please check output directory as dump may be incomplete!");
|
||||
}
|
||||
|
||||
// Extract the information from the output files
|
||||
resultProgress?.Report(Result.Success("Extracting output information from output files..."));
|
||||
SubmissionInfo submissionInfo = await InfoTool.ExtractOutputInformation(
|
||||
this.OutputPath,
|
||||
this.Drive,
|
||||
this.System,
|
||||
this.Type,
|
||||
this.Options,
|
||||
this.Parameters,
|
||||
var submissionInfo = await SubmissionInfoTool.ExtractOutputInformation(
|
||||
OutputPath,
|
||||
Drive,
|
||||
System,
|
||||
Type,
|
||||
Options,
|
||||
Parameters,
|
||||
resultProgress,
|
||||
protectionProgress);
|
||||
resultProgress?.Report(Result.Success("Extracting information complete!"));
|
||||
|
||||
// Inject seed submission info data, if necessary
|
||||
if (seedInfo != null)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Injecting user-supplied information..."));
|
||||
Builder.InjectSubmissionInformation(submissionInfo, seedInfo);
|
||||
resultProgress?.Report(Result.Success("Information injection complete!"));
|
||||
}
|
||||
|
||||
// Eject the disc automatically if configured to
|
||||
if (Options.EjectAfterDump == true)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive.Letter}"));
|
||||
resultProgress?.Report(Result.Success($"Ejecting disc in drive {Drive?.Name}"));
|
||||
await EjectDisc();
|
||||
}
|
||||
|
||||
// Reset the drive automatically if configured to
|
||||
if (this.InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
|
||||
if (InternalProgram == InternalProgram.DiscImageCreator && Options.DICResetDriveAfterDump)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Resetting drive {Drive.Letter}"));
|
||||
resultProgress?.Report(Result.Success($"Resetting drive {Drive?.Name}"));
|
||||
await ResetDrive();
|
||||
}
|
||||
|
||||
@@ -372,41 +356,47 @@ namespace MPF.Library
|
||||
|
||||
// Process special fields for site codes
|
||||
resultProgress?.Report(Result.Success("Processing site codes..."));
|
||||
InfoTool.ProcessSpecialFields(submissionInfo);
|
||||
Formatter.ProcessSpecialFields(submissionInfo);
|
||||
resultProgress?.Report(Result.Success("Processing complete!"));
|
||||
|
||||
// Format the information for the text output
|
||||
resultProgress?.Report(Result.Success("Formatting information..."));
|
||||
(List<string> formattedValues, string formatResult) = InfoTool.FormatOutputData(submissionInfo, this.Options);
|
||||
(var formattedValues, var formatResult) = Formatter.FormatOutputData(submissionInfo, Options.EnableRedumpCompatibility);
|
||||
if (formattedValues == null)
|
||||
resultProgress?.Report(Result.Success(formatResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(formatResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Success(formatResult));
|
||||
|
||||
// Get the filename suffix for auto-generated files
|
||||
var filenameSuffix = Options.AddFilenameSuffix ? Path.GetFileNameWithoutExtension(outputFilename) : null;
|
||||
|
||||
// Write the text output
|
||||
resultProgress?.Report(Result.Success("Writing information to !submissionInfo.txt..."));
|
||||
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, formattedValues);
|
||||
(bool txtSuccess, string txtResult) = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, formattedValues);
|
||||
if (txtSuccess)
|
||||
resultProgress?.Report(Result.Success(txtResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(txtResult));
|
||||
|
||||
// Write the copy protection output
|
||||
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
|
||||
if (submissionInfo?.CopyProtection?.FullProtections != null && submissionInfo.CopyProtection.FullProtections.Any())
|
||||
{
|
||||
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!"));
|
||||
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo, Options.HideDriveLetters);
|
||||
if (scanSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure("Writing could not complete!"));
|
||||
}
|
||||
}
|
||||
|
||||
// Write the JSON output, if required
|
||||
if (Options.OutputSubmissionJSON)
|
||||
{
|
||||
resultProgress?.Report(Result.Success($"Writing information to !submissionInfo.json{(Options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
|
||||
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, submissionInfo, Options.IncludeArtifacts);
|
||||
bool jsonSuccess = InfoTool.WriteOutputData(outputDirectory, filenameSuffix, submissionInfo, Options.IncludeArtifacts);
|
||||
if (jsonSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
@@ -417,13 +407,35 @@ namespace MPF.Library
|
||||
if (Options.CompressLogFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Compressing log files..."));
|
||||
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, outputFilename, this.Parameters);
|
||||
(bool compressSuccess, string compressResult) = InfoTool.CompressLogFiles(outputDirectory, filenameSuffix, outputFilename, Parameters);
|
||||
if (compressSuccess)
|
||||
resultProgress?.Report(Result.Success(compressResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(compressResult));
|
||||
}
|
||||
|
||||
// Delete unnecessary files, if required
|
||||
if (Options.DeleteUnnecessaryFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Deleting unnecessary files..."));
|
||||
(bool deleteSuccess, string deleteResult) = InfoTool.DeleteUnnecessaryFiles(outputDirectory, outputFilename, Parameters);
|
||||
if (deleteSuccess)
|
||||
resultProgress?.Report(Result.Success(deleteResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(deleteResult));
|
||||
}
|
||||
|
||||
// Create PS3 IRD, if required
|
||||
if (Options.CreateIRDAfterDumping && System == RedumpSystem.SonyPlayStation3 && Type == MediaType.BluRay)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Creating IRD... please wait!"));
|
||||
(bool deleteSuccess, string deleteResult) = await InfoTool.WriteIRD(OutputPath, submissionInfo?.Extras?.DiscKey, submissionInfo?.Extras?.DiscID, submissionInfo?.Extras?.PIC, submissionInfo?.SizeAndChecksums?.Layerbreak, submissionInfo?.SizeAndChecksums?.CRC32);
|
||||
if (deleteSuccess)
|
||||
resultProgress?.Report(Result.Success(deleteResult));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(deleteResult));
|
||||
}
|
||||
|
||||
resultProgress?.Report(Result.Success("Submission information process complete!"));
|
||||
return Result.Success();
|
||||
}
|
||||
@@ -434,7 +446,11 @@ namespace MPF.Library
|
||||
/// <returns>True if the configuration is valid, false otherwise</returns>
|
||||
internal bool ParametersValid()
|
||||
{
|
||||
bool parametersValid = Parameters.IsValid();
|
||||
// Missing drive means it can never be valid
|
||||
if (Drive == null)
|
||||
return false;
|
||||
|
||||
bool parametersValid = Parameters?.IsValid() ?? false;
|
||||
bool floppyValid = !(Drive.InternalDriveType == InternalDriveType.Floppy ^ Type == MediaType.FloppyDisk);
|
||||
|
||||
// TODO: HardDisk being in the Removable category is a hack, fix this later
|
||||
@@ -444,28 +460,26 @@ namespace MPF.Library
|
||||
return parametersValid && floppyValid && removableDiskValid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Run any additional tools given a DumpEnvironment
|
||||
/// </summary>
|
||||
/// <returns>Result instance with the outcome</returns>
|
||||
private Result ExecuteAdditionalTools() => Result.Success("No external tools needed!");
|
||||
|
||||
/// <summary>
|
||||
/// Run internal program async with an input set of parameters
|
||||
/// </summary>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns>Standard output from commandline window</returns>
|
||||
private async Task<string> ExecuteInternalProgram(BaseParameters parameters)
|
||||
private static async Task<string> ExecuteInternalProgram(BaseParameters parameters)
|
||||
{
|
||||
Process childProcess;
|
||||
#if NET40
|
||||
string output = await Task.Factory.StartNew(() =>
|
||||
#else
|
||||
string output = await Task.Run(() =>
|
||||
#endif
|
||||
{
|
||||
childProcess = new Process()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo()
|
||||
{
|
||||
FileName = parameters.ExecutablePath,
|
||||
Arguments = parameters.GenerateParameters(),
|
||||
FileName = parameters.ExecutablePath!,
|
||||
Arguments = parameters.GenerateParameters()!,
|
||||
CreateNoWindow = true,
|
||||
UseShellExecute = false,
|
||||
RedirectStandardInput = true,
|
||||
@@ -494,14 +508,14 @@ namespace MPF.Library
|
||||
private Result IsValidForDump()
|
||||
{
|
||||
// Validate that everything is good
|
||||
if (!ParametersValid())
|
||||
if (Parameters == null || !ParametersValid())
|
||||
return Result.Failure("Error! Current configuration is not supported!");
|
||||
|
||||
// Fix the output paths, just in case
|
||||
this.OutputPath = InfoTool.NormalizeOutputPaths(this.OutputPath, true);
|
||||
OutputPath = InfoTool.NormalizeOutputPaths(OutputPath, false);
|
||||
|
||||
// Validate that the output path isn't on the dumping drive
|
||||
if (this.OutputPath[0] == Drive.Letter)
|
||||
if (Drive?.Name != null && OutputPath.StartsWith(Drive.Name))
|
||||
return Result.Failure("Error! Cannot output to same drive that is being dumped!");
|
||||
|
||||
// Validate that the required program exists
|
||||
@@ -509,8 +523,8 @@ namespace MPF.Library
|
||||
return Result.Failure($"Error! {Parameters.ExecutablePath} does not exist!");
|
||||
|
||||
// Validate that the dumping drive doesn't contain the executable
|
||||
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath);
|
||||
if (fullExecutablePath[0] == Drive.Letter)
|
||||
string fullExecutablePath = Path.GetFullPath(Parameters.ExecutablePath!);
|
||||
if (Drive?.Name != null && fullExecutablePath.StartsWith(Drive.Name))
|
||||
return Result.Failure("Error! Cannot dump same drive that executable resides on!");
|
||||
|
||||
// Validate that the current configuration is supported
|
||||
@@ -524,7 +538,7 @@ namespace MPF.Library
|
||||
private bool RequiredProgramsExist()
|
||||
{
|
||||
// Validate that the path is configured
|
||||
if (string.IsNullOrWhiteSpace(Options.DiscImageCreatorPath))
|
||||
if (string.IsNullOrEmpty(Options.DiscImageCreatorPath))
|
||||
return false;
|
||||
|
||||
// Validate that the required program exists
|
||||
@@ -539,14 +553,14 @@ namespace MPF.Library
|
||||
/// </summary>
|
||||
/// <param name="command">Command string to run</param>
|
||||
/// <returns>The output of the command on success, null on error</returns>
|
||||
private async Task<string> RunStandaloneDiscImageCreatorCommand(string command)
|
||||
private async Task<string?> RunStandaloneDiscImageCreatorCommand(string command)
|
||||
{
|
||||
// Validate that DiscImageCreator is all set
|
||||
if (!RequiredProgramsExist())
|
||||
return null;
|
||||
|
||||
// Validate we're not trying to eject a non-optical
|
||||
if (Drive.InternalDriveType != InternalDriveType.Optical)
|
||||
if (Drive == null || Drive.InternalDriveType != InternalDriveType.Optical)
|
||||
return null;
|
||||
|
||||
CancelDumping();
|
||||
@@ -554,7 +568,7 @@ namespace MPF.Library
|
||||
var parameters = new Modules.DiscImageCreator.Parameters(string.Empty)
|
||||
{
|
||||
BaseCommand = command,
|
||||
DriveLetter = Drive.Letter.ToString(),
|
||||
DrivePath = Drive.Name,
|
||||
ExecutablePath = Options.DiscImageCreatorPath,
|
||||
};
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace MPF.Core.Hashing
|
||||
{
|
||||
/// <summary>
|
||||
/// Available hashing types
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum Hash
|
||||
{
|
||||
CRC = 1 << 0,
|
||||
MD5 = 1 << 1,
|
||||
SHA1 = 1 << 2,
|
||||
SHA256 = 1 << 3,
|
||||
SHA384 = 1 << 4,
|
||||
SHA512 = 1 << 5,
|
||||
|
||||
// Special combinations
|
||||
Standard = CRC | MD5 | SHA1,
|
||||
All = CRC | MD5 | SHA1 | SHA256 | SHA384 | SHA512,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Async hashing class wraper
|
||||
/// </summary>
|
||||
public class Hasher
|
||||
{
|
||||
public Hash HashType { get; private set; }
|
||||
private IDisposable _hasher;
|
||||
|
||||
public Hasher(Hash hashType)
|
||||
{
|
||||
this.HashType = hashType;
|
||||
GetHasher();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the correct hashing class based on the hash type
|
||||
/// </summary>
|
||||
private void GetHasher()
|
||||
{
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
_hasher = new OptimizedCRC();
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
_hasher = MD5.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA1:
|
||||
_hasher = SHA1.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA256:
|
||||
_hasher = SHA256.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA384:
|
||||
_hasher = SHA384.Create();
|
||||
break;
|
||||
|
||||
case Hash.SHA512:
|
||||
_hasher = SHA512.Create();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_hasher.Dispose();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process a buffer of some length with the internal hash algorithm
|
||||
/// </summary>
|
||||
public void Process(byte[] buffer, int size)
|
||||
{
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
(_hasher as OptimizedCRC).Update(buffer, 0, size);
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
(_hasher as HashAlgorithm).TransformBlock(buffer, 0, size, null, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalize the internal hash algorigthm
|
||||
/// </summary>
|
||||
public void Terminate()
|
||||
{
|
||||
byte[] emptyBuffer = new byte[0];
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
(_hasher as OptimizedCRC).Update(emptyBuffer, 0, 0);
|
||||
break;
|
||||
|
||||
case Hash.MD5:
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
(_hasher as HashAlgorithm).TransformFinalBlock(emptyBuffer, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get internal hash as a byte array
|
||||
/// </summary>
|
||||
public byte[] GetHash()
|
||||
{
|
||||
switch (HashType)
|
||||
{
|
||||
case Hash.CRC:
|
||||
return BitConverter.GetBytes((_hasher as OptimizedCRC).Value).Reverse().ToArray();
|
||||
|
||||
case Hash.MD5:
|
||||
case Hash.SHA1:
|
||||
case Hash.SHA256:
|
||||
case Hash.SHA384:
|
||||
case Hash.SHA512:
|
||||
return (_hasher as HashAlgorithm).Hash;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get internal hash as a string
|
||||
/// </summary>
|
||||
public string GetHashString()
|
||||
{
|
||||
byte[] hash = GetHash();
|
||||
if (hash == null)
|
||||
return null;
|
||||
|
||||
return ByteArrayToString(hash);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a byte array to a hex string
|
||||
/// </summary>
|
||||
/// <param name="bytes">Byte array to convert</param>
|
||||
/// <returns>Hex string representing the byte array</returns>
|
||||
/// <link>http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa</link>
|
||||
private static string ByteArrayToString(byte[] bytes)
|
||||
{
|
||||
// If we get null in, we send null out
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
string hex = BitConverter.ToString(bytes);
|
||||
return hex.Replace("-", string.Empty).ToLowerInvariant();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2012-2015 Eugene Larchenko (spct@mail.ru)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
//namespace OptimizedCRC
|
||||
namespace MPF.Core.Hashing
|
||||
{
|
||||
internal class OptimizedCRC : IDisposable
|
||||
{
|
||||
private const uint kCrcPoly = 0xEDB88320;
|
||||
private const uint kInitial = 0xFFFFFFFF;
|
||||
private const int CRC_NUM_TABLES = 8;
|
||||
private static readonly uint[] Table;
|
||||
|
||||
static OptimizedCRC()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
Table = new uint[256 * CRC_NUM_TABLES];
|
||||
int i;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
uint r = (uint)i;
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
|
||||
}
|
||||
Table[i] = r;
|
||||
}
|
||||
for (; i < 256 * CRC_NUM_TABLES; i++)
|
||||
{
|
||||
uint r = Table[i - 256];
|
||||
Table[i] = Table[r & 0xFF] ^ (r >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public uint UnsignedValue;
|
||||
|
||||
public OptimizedCRC()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset CRC
|
||||
/// </summary>
|
||||
public void Init()
|
||||
{
|
||||
UnsignedValue = kInitial;
|
||||
}
|
||||
|
||||
public int Value
|
||||
{
|
||||
get { return (int)~UnsignedValue; }
|
||||
}
|
||||
|
||||
public void Update(byte[] data, int offset, int count)
|
||||
{
|
||||
new ArraySegment<byte>(data, offset, count); // check arguments
|
||||
if (count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var table = OptimizedCRC.Table;
|
||||
|
||||
uint crc = UnsignedValue;
|
||||
|
||||
for (; (offset & 7) != 0 && count != 0; count--)
|
||||
{
|
||||
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
|
||||
}
|
||||
|
||||
if (count >= 8)
|
||||
{
|
||||
/*
|
||||
* Idea from 7-zip project sources (http://7-zip.org/sdk.html)
|
||||
*/
|
||||
|
||||
int end = (count - 8) & ~7;
|
||||
count -= end;
|
||||
end += offset;
|
||||
|
||||
while (offset != end)
|
||||
{
|
||||
crc ^= (uint)(data[offset] + (data[offset + 1] << 8) + (data[offset + 2] << 16) + (data[offset + 3] << 24));
|
||||
uint high = (uint)(data[offset + 4] + (data[offset + 5] << 8) + (data[offset + 6] << 16) + (data[offset + 7] << 24));
|
||||
offset += 8;
|
||||
|
||||
crc = table[(byte)crc + 0x700]
|
||||
^ table[(byte)(crc >>= 8) + 0x600]
|
||||
^ table[(byte)(crc >>= 8) + 0x500]
|
||||
^ table[/*(byte)*/(crc >> 8) + 0x400]
|
||||
^ table[(byte)(high) + 0x300]
|
||||
^ table[(byte)(high >>= 8) + 0x200]
|
||||
^ table[(byte)(high >>= 8) + 0x100]
|
||||
^ table[/*(byte)*/(high >> 8) + 0x000];
|
||||
}
|
||||
}
|
||||
|
||||
while (count-- != 0)
|
||||
{
|
||||
crc = (crc >> 8) ^ table[(byte)crc ^ data[offset++]];
|
||||
}
|
||||
|
||||
UnsignedValue = crc;
|
||||
}
|
||||
|
||||
static public int Compute(byte[] data, int offset, int count)
|
||||
{
|
||||
var crc = new OptimizedCRC();
|
||||
crc.Update(data, offset, count);
|
||||
return crc.Value;
|
||||
}
|
||||
|
||||
static public int Compute(byte[] data)
|
||||
{
|
||||
return Compute(data, 0, data.Length);
|
||||
}
|
||||
|
||||
static public int Compute(ArraySegment<byte> block)
|
||||
{
|
||||
return Compute(block.Array, block.Offset, block.Count);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
UnsignedValue = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
//namespace Compress.ThreadReaders
|
||||
namespace MPF.Core.Hashing
|
||||
{
|
||||
public class ThreadLoadBuffer : IDisposable
|
||||
{
|
||||
private readonly AutoResetEvent _waitEvent;
|
||||
private readonly AutoResetEvent _outEvent;
|
||||
private readonly Thread _tWorker;
|
||||
|
||||
private byte[] _buffer;
|
||||
private int _size;
|
||||
private readonly Stream _ds;
|
||||
private bool _finished;
|
||||
public bool errorState;
|
||||
|
||||
public int SizeRead;
|
||||
|
||||
public ThreadLoadBuffer(Stream ds)
|
||||
{
|
||||
_waitEvent = new AutoResetEvent(false);
|
||||
_outEvent = new AutoResetEvent(false);
|
||||
_finished = false;
|
||||
_ds = ds;
|
||||
errorState = false;
|
||||
|
||||
_tWorker = new Thread(MainLoop);
|
||||
_tWorker.Start();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_waitEvent.Close();
|
||||
_outEvent.Close();
|
||||
}
|
||||
|
||||
private void MainLoop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_waitEvent.WaitOne();
|
||||
if (_finished)
|
||||
{
|
||||
break;
|
||||
}
|
||||
try
|
||||
{
|
||||
SizeRead = _ds.Read(_buffer, 0, _size);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
errorState = true;
|
||||
}
|
||||
_outEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public void Trigger(byte[] buffer, int size)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_size = size;
|
||||
_waitEvent.Set();
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
_outEvent.WaitOne();
|
||||
}
|
||||
|
||||
public void Finish()
|
||||
{
|
||||
_finished = true;
|
||||
_waitEvent.Set();
|
||||
_tWorker.Join();
|
||||
}
|
||||
}
|
||||
}
|
||||
2015
MPF.Core/InfoTool.cs
Normal file
2015
MPF.Core/InfoTool.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,48 +1,63 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.1.4</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<Version>2.6.6</Version>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Description>Common code for all MPF implementations</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<COMReference Include="IMAPI2">
|
||||
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="IMAPI2FS">
|
||||
<Guid>{2C941FD0-975B-59BE-A960-9A2A262853A5}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0-preview.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.1.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.1.6" />
|
||||
<InternalsVisibleTo Include="MPF.Test" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="MinAsyncBridge" Version="0.12.4" />
|
||||
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
|
||||
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="IndexRange" Version="1.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net452`))">
|
||||
<PackageReference Include="Microsoft.Net.Http" Version="2.2.29" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
|
||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
||||
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.2" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="LibIRD" Version="0.9.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="psxt001z.Library" Version="0.21.0-beta4" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.1.4" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.4.0" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF.Modules.Aaru
|
||||
namespace MPF.Core.Modules.Aaru
|
||||
{
|
||||
/// <summary>
|
||||
/// Top-level commands for Aaru
|
||||
@@ -1,6 +1,6 @@
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.Aaru
|
||||
namespace MPF.Core.Modules.Aaru
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
395
MPF.Core/Modules/CleanRIp/Parameters.cs
Normal file
395
MPF.Core/Modules/CleanRIp/Parameters.cs
Normal file
@@ -0,0 +1,395 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Modules.CleanRip
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic set of CleanRip parameters
|
||||
/// </summary>
|
||||
public class Parameters : BaseParameters
|
||||
{
|
||||
#region Metadata
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override InternalProgram InternalProgram => InternalProgram.CleanRip;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(string? parameters) : base(parameters) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, drivePath, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
#region BaseParameters Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
var missingFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.DVD: // Only added here to help users; not strictly correct
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}-dumpinfo.txt"))
|
||||
missingFiles.Add($"{basePath}-dumpinfo.txt");
|
||||
if (!File.Exists($"{basePath}.bca"))
|
||||
missingFiles.Add($"{basePath}.bca");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
missingFiles.Add("Media and system combination not supported for CleanRip");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = Builder.EnsureAllSections(info);
|
||||
|
||||
// TODO: Determine if there's a CleanRip version anywhere
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "-dumpinfo.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// Get the Datafile information
|
||||
var datafile = GenerateCleanripDatafile(basePath + ".iso", basePath + "-dumpinfo.txt");
|
||||
|
||||
// Fill in the hash data
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
|
||||
// Get the individual hash data, as per internal
|
||||
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
info.SizeAndChecksums!.Size = size;
|
||||
info.SizeAndChecksums.CRC32 = crc32;
|
||||
info.SizeAndChecksums.MD5 = md5;
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
|
||||
// Dual-layer discs have the same size and layerbreak
|
||||
if (size == 8511160320)
|
||||
info.SizeAndChecksums.Layerbreak = 2084960;
|
||||
}
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.DVD: // Only added here to help users; not strictly correct
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
info.Extras!.BCA = GetBCA(basePath + ".bca");
|
||||
|
||||
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out var gcVersion, out var gcName, out var gcSerial))
|
||||
{
|
||||
info.CommonDiscInfo!.Region = gcRegion ?? info.CommonDiscInfo.Region;
|
||||
info.VersionAndEditions!.Version = gcVersion ?? info.VersionAndEditions.Version;
|
||||
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalName] = gcName ?? string.Empty;
|
||||
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = gcSerial ?? string.Empty;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
info.Artifacts ??= [];
|
||||
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty;
|
||||
if (File.Exists(basePath + "-dumpinfo.txt"))
|
||||
info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt")) ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
{
|
||||
var logFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.DVD: // Only added here to help users; not strictly correct
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (File.Exists($"{basePath}-dumpinfo.txt"))
|
||||
logFiles.Add($"{basePath}-dumpinfo.txt");
|
||||
if (File.Exists($"{basePath}.bca"))
|
||||
logFiles.Add($"{basePath}.bca");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return logFiles;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Information Extraction Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted datfile from the cleanrip output, if possible
|
||||
/// </summary>
|
||||
/// <param name="iso">Path to ISO file</param>
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <returns></returns>
|
||||
private static Datafile? GenerateCleanripDatafile(string iso, string dumpinfo)
|
||||
{
|
||||
// If the files don't exist, we can't get info from it
|
||||
if (!File.Exists(iso) || !File.Exists(dumpinfo))
|
||||
return null;
|
||||
|
||||
long size = new FileInfo(iso).Length;
|
||||
string crc = string.Empty;
|
||||
string md5 = string.Empty;
|
||||
string sha1 = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
// Make sure this file is a dumpinfo
|
||||
using var sr = File.OpenText(dumpinfo);
|
||||
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
|
||||
return null;
|
||||
|
||||
// Read all lines and gather dat information
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
if (string.IsNullOrEmpty(line))
|
||||
continue;
|
||||
else if (line!.StartsWith("CRC32"))
|
||||
crc = line.Substring(7).ToLowerInvariant();
|
||||
else if (line.StartsWith("MD5"))
|
||||
md5 = line.Substring(5);
|
||||
else if (line.StartsWith("SHA-1"))
|
||||
sha1 = line.Substring(7);
|
||||
}
|
||||
|
||||
return new Datafile
|
||||
{
|
||||
Games =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Roms =
|
||||
[
|
||||
new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1 },
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the hex contents of the BCA file
|
||||
/// </summary>
|
||||
/// <param name="bcaPath">Path to the BCA file associated with the dump</param>
|
||||
/// <returns>BCA data as a hex string if possible, null on error</returns>
|
||||
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
|
||||
private static string? GetBCA(string bcaPath)
|
||||
{
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(bcaPath))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var hex = GetFullFile(bcaPath, true);
|
||||
if (hex == null)
|
||||
return null;
|
||||
|
||||
return Regex.Replace(hex, ".{32}", "$0\n");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted datfile from the cleanrip output, if possible
|
||||
/// </summary>
|
||||
/// <param name="iso">Path to ISO file</param>
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <returns></returns>
|
||||
private static string? GetCleanripDatfile(string iso, string dumpinfo)
|
||||
{
|
||||
// If the files don't exist, we can't get info from it
|
||||
if (!File.Exists(iso) || !File.Exists(dumpinfo))
|
||||
return null;
|
||||
|
||||
long size = new FileInfo(iso).Length;
|
||||
string crc = string.Empty;
|
||||
string md5 = string.Empty;
|
||||
string sha1 = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
// Make sure this file is a dumpinfo
|
||||
using var sr = File.OpenText(dumpinfo);
|
||||
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
|
||||
return null;
|
||||
|
||||
// Read all lines and gather dat information
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
if (string.IsNullOrEmpty(line))
|
||||
continue;
|
||||
else if (line!.StartsWith("CRC32"))
|
||||
crc = line.Substring(7).ToLowerInvariant();
|
||||
else if (line.StartsWith("MD5"))
|
||||
md5 = line.Substring(5);
|
||||
else if (line.StartsWith("SHA-1"))
|
||||
sha1 = line.Substring(7);
|
||||
}
|
||||
|
||||
return $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the extracted GC and Wii version
|
||||
/// </summary>
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <param name="region">Output region, if possible</param>
|
||||
/// <param name="version">Output internal version of the game</param>
|
||||
/// <param name="name">Output internal name of the game</param>
|
||||
/// <param name="serial">Output internal serial of the game</param>
|
||||
/// <returns></returns>
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string? version, out string? name, out string? serial)
|
||||
{
|
||||
region = null; version = null; name = null; serial = null;
|
||||
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(dumpinfo))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
// Make sure this file is a dumpinfo
|
||||
using var sr = File.OpenText(dumpinfo);
|
||||
if (sr.ReadLine()?.Contains("--File Generated by CleanRip") != true)
|
||||
return false;
|
||||
|
||||
// Read all lines and gather dat information
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (line!.StartsWith("Version"))
|
||||
{
|
||||
version = line.Substring("Version: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("Internal Name"))
|
||||
{
|
||||
name = line.Substring("Internal Name: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("Filename"))
|
||||
{
|
||||
serial = line.Substring("Filename: ".Length);
|
||||
if (serial.EndsWith("-disc2"))
|
||||
serial = serial.Replace("-disc2", string.Empty);
|
||||
|
||||
// char gameType = serial[0];
|
||||
// string gameid = serial[1] + serial[2];
|
||||
// string version = serial[4] + serial[5]
|
||||
|
||||
switch (serial[3])
|
||||
{
|
||||
case 'A':
|
||||
region = Region.World;
|
||||
break;
|
||||
case 'D':
|
||||
region = Region.Germany;
|
||||
break;
|
||||
case 'E':
|
||||
region = Region.UnitedStatesOfAmerica;
|
||||
break;
|
||||
case 'F':
|
||||
region = Region.France;
|
||||
break;
|
||||
case 'I':
|
||||
region = Region.Italy;
|
||||
break;
|
||||
case 'J':
|
||||
region = Region.Japan;
|
||||
break;
|
||||
case 'K':
|
||||
region = Region.SouthKorea;
|
||||
break;
|
||||
case 'L':
|
||||
region = Region.Europe; // Japanese import to Europe
|
||||
break;
|
||||
case 'M':
|
||||
region = Region.Europe; // American import to Europe
|
||||
break;
|
||||
case 'N':
|
||||
region = Region.UnitedStatesOfAmerica; // Japanese import to USA
|
||||
break;
|
||||
case 'P':
|
||||
region = Region.Europe;
|
||||
break;
|
||||
case 'R':
|
||||
region = Region.RussianFederation;
|
||||
break;
|
||||
case 'S':
|
||||
region = Region.Spain;
|
||||
break;
|
||||
case 'Q':
|
||||
region = Region.SouthKorea; // Korea with Japanese language
|
||||
break;
|
||||
case 'T':
|
||||
region = Region.SouthKorea; // Korea with English language
|
||||
break;
|
||||
case 'X':
|
||||
region = null; // Not a real region code
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,72 +1,72 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace MPF.Modules
|
||||
namespace MPF.Core.Modules
|
||||
{
|
||||
[XmlRoot("datafile")]
|
||||
public class Datafile
|
||||
{
|
||||
[XmlElement("header")]
|
||||
public Header Header;
|
||||
public Header? Header;
|
||||
|
||||
[XmlElement("game")]
|
||||
public Game[] Games;
|
||||
public Game[]? Games;
|
||||
}
|
||||
|
||||
public class Header
|
||||
{
|
||||
[XmlElement("name")]
|
||||
public string Name;
|
||||
public string? Name;
|
||||
|
||||
[XmlElement("description")]
|
||||
public string Description;
|
||||
public string? Description;
|
||||
|
||||
[XmlElement("version")]
|
||||
public string Version;
|
||||
public string? Version;
|
||||
|
||||
[XmlElement("date")]
|
||||
public string Date;
|
||||
public string? Date;
|
||||
|
||||
[XmlElement("author")]
|
||||
public string Author;
|
||||
public string? Author;
|
||||
|
||||
[XmlElement("homepage")]
|
||||
public string Homepage;
|
||||
public string? Homepage;
|
||||
|
||||
[XmlElement("url")]
|
||||
public string Url;
|
||||
public string? Url;
|
||||
}
|
||||
|
||||
public class Game
|
||||
{
|
||||
[XmlAttribute("name")]
|
||||
public string Name;
|
||||
public string? Name;
|
||||
|
||||
[XmlElement("category")]
|
||||
public string Category;
|
||||
public string? Category;
|
||||
|
||||
[XmlElement("description")]
|
||||
public string Description;
|
||||
public string? Description;
|
||||
|
||||
[XmlElement("rom")]
|
||||
public Rom[] Roms;
|
||||
public Rom[]? Roms;
|
||||
}
|
||||
|
||||
public class Rom
|
||||
{
|
||||
[XmlAttribute("name")]
|
||||
public string Name;
|
||||
public string? Name;
|
||||
|
||||
[XmlAttribute("size")]
|
||||
public string Size;
|
||||
public string? Size;
|
||||
|
||||
[XmlAttribute("crc")]
|
||||
public string Crc;
|
||||
public string? Crc;
|
||||
|
||||
[XmlAttribute("md5")]
|
||||
public string Md5;
|
||||
public string? Md5;
|
||||
|
||||
[XmlAttribute("sha1")]
|
||||
public string Sha1;
|
||||
public string? Sha1;
|
||||
|
||||
// TODO: Add extended hashes here
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF.Modules.DiscImageCreator
|
||||
namespace MPF.Core.Modules.DiscImageCreator
|
||||
{
|
||||
/// <summary>
|
||||
/// Top-level commands for DiscImageCreator
|
||||
100
MPF.Core/Modules/DiscImageCreator/Converters.cs
Normal file
100
MPF.Core/Modules/DiscImageCreator/Converters.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Modules.DiscImageCreator
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
#region Cross-enumeration conversions
|
||||
|
||||
/// <summary>
|
||||
/// Get the most common known system for a given MediaType
|
||||
/// </summary>
|
||||
/// <param name="baseCommand">Command value to check</param>
|
||||
/// <returns>RedumpSystem if possible, null on error</returns>
|
||||
public static RedumpSystem? ToRedumpSystem(string baseCommand)
|
||||
{
|
||||
return baseCommand switch
|
||||
{
|
||||
CommandStrings.Audio => (RedumpSystem?)RedumpSystem.AudioCD,
|
||||
CommandStrings.CompactDisc
|
||||
or CommandStrings.Data
|
||||
or CommandStrings.DigitalVideoDisc
|
||||
or CommandStrings.Disk
|
||||
or CommandStrings.Floppy
|
||||
or CommandStrings.Tape => (RedumpSystem?)RedumpSystem.IBMPCcompatible,
|
||||
CommandStrings.GDROM
|
||||
or CommandStrings.Swap => (RedumpSystem?)RedumpSystem.SegaDreamcast,
|
||||
CommandStrings.BluRay => (RedumpSystem?)RedumpSystem.SonyPlayStation3,
|
||||
CommandStrings.SACD => (RedumpSystem?)RedumpSystem.SuperAudioCD,
|
||||
CommandStrings.XBOX
|
||||
or CommandStrings.XBOXSwap => (RedumpSystem?)RedumpSystem.MicrosoftXbox,
|
||||
CommandStrings.XGD2Swap
|
||||
or CommandStrings.XGD3Swap => (RedumpSystem?)RedumpSystem.MicrosoftXbox360,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the MediaType associated with a given base command
|
||||
/// </summary>
|
||||
/// <param name="baseCommand">Command value to check</param>
|
||||
/// <returns>MediaType if possible, null on error</returns>
|
||||
/// <remarks>This takes the "safe" route by assuming the larger of any given format</remarks>
|
||||
public static MediaType? ToMediaType(string? baseCommand)
|
||||
{
|
||||
return baseCommand switch
|
||||
{
|
||||
CommandStrings.Audio
|
||||
or CommandStrings.CompactDisc
|
||||
or CommandStrings.Data
|
||||
or CommandStrings.SACD => (MediaType?)MediaType.CDROM,
|
||||
CommandStrings.GDROM
|
||||
or CommandStrings.Swap => (MediaType?)MediaType.GDROM,
|
||||
CommandStrings.DigitalVideoDisc
|
||||
or CommandStrings.XBOX
|
||||
or CommandStrings.XBOXSwap
|
||||
or CommandStrings.XGD2Swap
|
||||
or CommandStrings.XGD3Swap => (MediaType?)MediaType.DVD,
|
||||
CommandStrings.BluRay => (MediaType?)MediaType.BluRay,
|
||||
|
||||
// Non-optical
|
||||
CommandStrings.Floppy => (MediaType?)MediaType.FloppyDisk,
|
||||
CommandStrings.Disk => (MediaType?)MediaType.HardDisk,
|
||||
CommandStrings.Tape => (MediaType?)MediaType.DataCartridge,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default extension for a given disc type
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType value to check</param>
|
||||
/// <returns>Valid extension (with leading '.'), null on error</returns>
|
||||
public static string? Extension(MediaType? type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
MediaType.CDROM
|
||||
or MediaType.GDROM
|
||||
or MediaType.Cartridge
|
||||
or MediaType.HardDisk
|
||||
or MediaType.CompactFlash
|
||||
or MediaType.MMC
|
||||
or MediaType.SDCard
|
||||
or MediaType.FlashDrive => ".bin",
|
||||
MediaType.DVD
|
||||
or MediaType.HDDVD
|
||||
or MediaType.BluRay
|
||||
or MediaType.NintendoWiiOpticalDisc => ".iso",
|
||||
MediaType.LaserDisc
|
||||
or MediaType.NintendoGameCubeGameDisc => ".raw",
|
||||
MediaType.NintendoWiiUOpticalDisc => ".wud",
|
||||
MediaType.FloppyDisk => ".img",
|
||||
MediaType.Cassette => ".wav",
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
293
MPF.Core/Modules/PS3CFW/Parameters.cs
Normal file
293
MPF.Core/Modules/PS3CFW/Parameters.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Modules.PS3CFW
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic set of PlayStation 3 Custom Firmware parameters
|
||||
/// </summary>
|
||||
public class Parameters : BaseParameters
|
||||
{
|
||||
#region Metadata
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override InternalProgram InternalProgram => InternalProgram.PS3CFW;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(string? parameters) : base(parameters) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, drivePath, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
#region BaseParameters Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
var missingFiles = new List<string>();
|
||||
|
||||
if (this.Type != MediaType.BluRay || this.System != RedumpSystem.SonyPlayStation3)
|
||||
{
|
||||
missingFiles.Add("Media and system combination not supported for PS3 CFW");
|
||||
}
|
||||
else
|
||||
{
|
||||
string? getKeyBasePath = GetCFWBasePath(basePath);
|
||||
if (!File.Exists($"{getKeyBasePath}.getkey.log"))
|
||||
missingFiles.Add($"{getKeyBasePath}.getkey.log");
|
||||
if (!File.Exists($"{getKeyBasePath}.disc.pic"))
|
||||
missingFiles.Add($"{getKeyBasePath}.disc.pic");
|
||||
}
|
||||
|
||||
return (missingFiles.Count == 0, missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = Builder.EnsureAllSections(info);
|
||||
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
|
||||
// Get the Datafile information
|
||||
Datafile? datafile = GeneratePS3CFWDatafile(basePath + ".iso");
|
||||
|
||||
// Fill in the hash data
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
|
||||
// Get the individual hash data, as per internal
|
||||
if (InfoTool.GetISOHashValues(datafile, out long size, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
info.SizeAndChecksums!.Size = size;
|
||||
info.SizeAndChecksums.CRC32 = crc32;
|
||||
info.SizeAndChecksums.MD5 = md5;
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
}
|
||||
|
||||
// Get the PVD from the ISO
|
||||
if (GetPVD(basePath + ".iso", out string? pvd))
|
||||
info.Extras!.PVD = pvd;
|
||||
|
||||
// Try get the serial, version, and firmware version if a drive is provided
|
||||
if (drive != null)
|
||||
{
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation3Version(drive?.Name) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = InfoTool.GetPlayStation3Serial(drive?.Name) ?? string.Empty;
|
||||
string? firmwareVersion = InfoTool.GetPlayStation3FirmwareVersion(drive?.Name);
|
||||
if (firmwareVersion != null)
|
||||
info.CommonDiscInfo!.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}";
|
||||
}
|
||||
|
||||
// Try to determine the name of the GetKey file(s)
|
||||
string? getKeyBasePath = GetCFWBasePath(basePath);
|
||||
|
||||
// If GenerateSubmissionInfo is run, .getkey.log existence should already be checked
|
||||
if (!File.Exists(getKeyBasePath + ".getkey.log"))
|
||||
return;
|
||||
|
||||
// Get dumping date from GetKey log date
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(getKeyBasePath + ".getkey.log")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// TODO: Put info about abnormal PIC info beyond 132 bytes in comments?
|
||||
if (File.Exists(getKeyBasePath + ".disc.pic"))
|
||||
info.Extras!.PIC = GetPIC(getKeyBasePath + ".disc.pic", 264);
|
||||
|
||||
// Parse Disc Key, Disc ID, and PIC from the .getkey.log file
|
||||
if (Utilities.Tools.ParseGetKeyLog(getKeyBasePath + ".getkey.log", out string? key, out string? id, out string? pic))
|
||||
{
|
||||
if (key != null)
|
||||
info.Extras!.DiscKey = key.ToUpperInvariant();
|
||||
if (id != null)
|
||||
info.Extras!.DiscID = id.ToUpperInvariant().Substring(0, 24) + "XXXXXXXX";
|
||||
if (string.IsNullOrEmpty(info.Extras!.PIC) && !string.IsNullOrEmpty(pic))
|
||||
{
|
||||
pic = Regex.Replace(pic, ".{32}", "$0\n");
|
||||
info.Extras.PIC = pic;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
info.Artifacts ??= [];
|
||||
|
||||
if (File.Exists(getKeyBasePath + ".disc.pic"))
|
||||
info.Artifacts["discpic"] = GetBase64(GetFullFile(getKeyBasePath + ".disc.pic", binary: true)) ?? string.Empty;
|
||||
if (File.Exists(getKeyBasePath + ".getkey.log"))
|
||||
info.Artifacts["getkeylog"] = GetBase64(GetFullFile(getKeyBasePath + ".getkey.log")) ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
{
|
||||
var logFiles = new List<string>();
|
||||
string? getKeyBasePath = GetCFWBasePath(basePath);
|
||||
|
||||
if (this.System != RedumpSystem.SonyPlayStation3)
|
||||
return logFiles;
|
||||
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.BluRay:
|
||||
if (File.Exists($"{getKeyBasePath}.getkey.log"))
|
||||
logFiles.Add($"{getKeyBasePath}.getkey.log");
|
||||
if (File.Exists($"{getKeyBasePath}.disc.pic"))
|
||||
logFiles.Add($"{getKeyBasePath}.disc.pic");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return logFiles;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Information Extraction Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted datfile from the PS3 CFW output, if possible
|
||||
/// </summary>
|
||||
/// <param name="iso">Path to ISO file</param>
|
||||
/// <returns></returns>
|
||||
private static Datafile? GeneratePS3CFWDatafile(string iso)
|
||||
{
|
||||
// If the ISO file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(iso))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (HashTool.GetStandardHashes(iso, out long size, out string? crc, out string? md5, out string? sha1))
|
||||
{
|
||||
return new Datafile
|
||||
{
|
||||
Games = [new Game { Roms = [new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1, }] }]
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Don't hardcode 0x8320 and move this function to BaseParameters
|
||||
/// <summary>
|
||||
/// Get a isobuster-formatted PVD from a PS3 ISO, if possible
|
||||
/// </summary>
|
||||
/// <param name="isoPath">Path to ISO file</param>
|
||||
/// <param name="pvd">Formatted PVD string, otherwise null</param>
|
||||
/// <returns>True if PVD was successfully parsed, otherwise false</returns>
|
||||
private static bool GetPVD(string isoPath, out string? pvd)
|
||||
{
|
||||
pvd = null;
|
||||
try
|
||||
{
|
||||
// Get PVD bytes from ISO file
|
||||
var buf = new byte[96];
|
||||
using (FileStream iso = File.OpenRead(isoPath))
|
||||
{
|
||||
iso.Seek(0x8320, SeekOrigin.Begin);
|
||||
|
||||
int offset = 0;
|
||||
while (offset < 96)
|
||||
{
|
||||
int read = iso.Read(buf, offset, buf.Length - offset);
|
||||
if (read == 0)
|
||||
throw new EndOfStreamException();
|
||||
offset += read;
|
||||
}
|
||||
}
|
||||
|
||||
// Format PVD to isobuster standard
|
||||
char[] pvdCharArray = new char[96];
|
||||
for (int i = 0; i < 96; i++)
|
||||
{
|
||||
if (buf[i] >= 0x20 && buf[i] <= 0x7E)
|
||||
pvdCharArray[i] = (char)buf[i];
|
||||
else
|
||||
pvdCharArray[i] = '.';
|
||||
}
|
||||
string pvdASCII = new string(pvdCharArray, 0, 96);
|
||||
pvd = string.Empty;
|
||||
for (int i = 0; i < 96; i += 16)
|
||||
{
|
||||
pvd += $"{(0x0320+i):X4} : {buf[i]:X2} {buf[i+1]:X2} {buf[i+2]:X2} {buf[i+3]:X2} {buf[i+4]:X2} {buf[i+5]:X2} {buf[i+6]:X2} {buf[i+7]:X2} " +
|
||||
$"{buf[i+8]:X2} {buf[i+9]:X2} {buf[i+10]:X2} {buf[i+11]:X2} {buf[i+12]:X2} {buf[i+13]:X2} {buf[i+14]:X2} {buf[i+15]:X2} {pvdASCII.Substring(i, 16)}\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error is
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted datfile from the PS3 CFW output, if possible
|
||||
/// </summary>
|
||||
/// <param name="iso">Path to ISO file</param>
|
||||
/// <returns>Formatted datfile, null if not valid</returns>
|
||||
private static string? GetPS3CFWDatfile(string iso)
|
||||
{
|
||||
// If the files don't exist, we can't get info from it
|
||||
if (!File.Exists(iso))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
if (HashTool.GetStandardHashes(iso, out long size, out string? crc, out string? md5, out string? sha1))
|
||||
return $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Functions
|
||||
|
||||
/// <summary>
|
||||
/// Estimate the base filename of the .getkey.log file associated with the dump
|
||||
/// </summary>
|
||||
/// <param name="iso">Path to ISO file</param>
|
||||
/// <returns>Base filename, null if not found</returns>
|
||||
private string? GetCFWBasePath(string iso)
|
||||
{
|
||||
string? dir = Path.GetDirectoryName(iso);
|
||||
dir ??= ".";
|
||||
|
||||
string[] files = Directory.GetFiles(dir, "*.getkey.log");
|
||||
|
||||
if (files.Length != 1)
|
||||
return null;
|
||||
|
||||
return files[0].Substring(0, files[0].Length - 11);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF.Modules.Redumper
|
||||
namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
/// <summary>
|
||||
/// Top-level commands for Redumper
|
||||
@@ -10,14 +10,19 @@ namespace MPF.Modules.Redumper
|
||||
public const string DVD = "dvd"; // Synonym for CD
|
||||
public const string BluRay = "bd"; // Synonym for CD
|
||||
public const string SACD = "sacd"; // Synonym for CD
|
||||
public const string Rings = "rings";
|
||||
public const string Dump = "dump";
|
||||
public const string Info = "info";
|
||||
public const string Protection = "protection";
|
||||
public const string DumpNew = "dumpnew"; // Temporary command, to be removed later
|
||||
public const string Refine = "refine";
|
||||
public const string Split = "split";
|
||||
public const string RefineNew = "refinenew"; // Temporary command, to be removed later
|
||||
public const string Verify = "verify";
|
||||
public const string DVDKey = "dvdkey";
|
||||
public const string DVDIsoKey = "dvdisokey";
|
||||
public const string Protection = "protection";
|
||||
public const string Split = "split";
|
||||
public const string Hash = "hash";
|
||||
public const string Info = "info";
|
||||
public const string Skeleton = "skeleton";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,6 +33,7 @@ namespace MPF.Modules.Redumper
|
||||
// General
|
||||
public const string HelpLong = "--help";
|
||||
public const string HelpShort = "-h";
|
||||
public const string Version = "--version";
|
||||
public const string Verbose = "--verbose";
|
||||
public const string Debug = "--debug";
|
||||
public const string Drive = "--drive";
|
||||
@@ -46,7 +52,7 @@ namespace MPF.Modules.Redumper
|
||||
public const string DriveSectorOrder = "--drive-sector-order";
|
||||
|
||||
// Drive Specific
|
||||
public const string PlextorLeadinSkip = "--plextor-leadin-skip";
|
||||
public const string PlextorSkipLeadin = "--plextor-skip-leadin";
|
||||
public const string PlextorLeadinRetries = "--plextor-leadin-retries";
|
||||
public const string AsusSkipLeadout = "--asus-skip-leadout";
|
||||
|
||||
@@ -68,7 +74,10 @@ namespace MPF.Modules.Redumper
|
||||
public const string LBAEnd = "--lba-end";
|
||||
public const string RefineSubchannel = "--refine-subchannel";
|
||||
public const string Skip = "--skip";
|
||||
public const string DumpWriteOffset = "--dump-write-offset";
|
||||
public const string DumpReadSize = "--dump-read-size";
|
||||
public const string OverreadLeadout = "--overread-leadout";
|
||||
public const string LegacySubs = "--legacy-subs";
|
||||
public const string DisableCDText = "--disable-cdtext";
|
||||
}
|
||||
}
|
||||
28
MPF.Core/Modules/Redumper/Converters.cs
Normal file
28
MPF.Core/Modules/Redumper/Converters.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
#region Cross-enumeration conversions
|
||||
|
||||
/// <summary>
|
||||
/// Get the default extension for a given disc type
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType value to check</param>
|
||||
/// <returns>Valid extension (with leading '.'), null on error</returns>
|
||||
public static string? Extension(MediaType? type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
MediaType.CDROM => ".bin",
|
||||
MediaType.DVD
|
||||
or MediaType.HDDVD
|
||||
or MediaType.BluRay => ".iso",
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,9 +4,11 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.UmdImageCreator
|
||||
namespace MPF.Core.Modules.UmdImageCreator
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic set of UmdImageCreator parameters
|
||||
@@ -21,11 +23,11 @@ namespace MPF.Modules.UmdImageCreator
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(string parameters) : base(parameters) { }
|
||||
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)
|
||||
public Parameters(RedumpSystem? system, MediaType? type, string? drivePath, string filename, int? driveSpeed, Options options)
|
||||
: base(system, type, drivePath, filename, driveSpeed, options)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -34,7 +36,7 @@ namespace MPF.Modules.UmdImageCreator
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
List<string> missingFiles = new List<string>();
|
||||
var missingFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.UMD:
|
||||
@@ -61,34 +63,46 @@ namespace MPF.Modules.UmdImageCreator
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = Builder.EnsureAllSections(info);
|
||||
|
||||
// TODO: Determine if there's a UMDImageCreator version anywhere
|
||||
info.DumpingInfo.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
info.DumpingInfo.DumpingDate = GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
info.DumpingInfo.DumpingDate = InfoTool.GetFileModifiedDate(basePath + "_disc.txt")?.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.UMD:
|
||||
info.Extras.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
|
||||
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))
|
||||
if (HashTool.GetStandardHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
info.SizeAndChecksums.Size = filesize;
|
||||
// Get the Datafile information
|
||||
var datafile = new Datafile
|
||||
{
|
||||
Games = [new Game { Roms = [new Rom { Name = string.Empty, Size = filesize.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1, }] }]
|
||||
};
|
||||
|
||||
// Fill in the hash data
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
|
||||
info.SizeAndChecksums!.Size = filesize;
|
||||
info.SizeAndChecksums.CRC32 = crc32;
|
||||
info.SizeAndChecksums.MD5 = md5;
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
}
|
||||
|
||||
if (GetUMDAuxInfo(basePath + "_disc.txt", out string title, out DiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize))
|
||||
if (GetUMDAuxInfo(basePath + "_disc.txt", out var title, out DiscCategory? umdcat, out var umdversion, out var umdlayer, out long umdsize))
|
||||
{
|
||||
info.CommonDiscInfo.Title = title ?? string.Empty;
|
||||
info.CommonDiscInfo!.Title = title ?? string.Empty;
|
||||
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
|
||||
info.VersionAndEditions.Version = umdversion ?? string.Empty;
|
||||
info.SizeAndChecksums.Size = umdsize;
|
||||
info.VersionAndEditions!.Version = umdversion ?? string.Empty;
|
||||
info.SizeAndChecksums!.Size = umdsize;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(umdlayer))
|
||||
if (!string.IsNullOrEmpty(umdlayer))
|
||||
info.SizeAndChecksums.Layerbreak = Int64.Parse(umdlayer ?? "-1");
|
||||
}
|
||||
|
||||
@@ -98,23 +112,25 @@ namespace MPF.Modules.UmdImageCreator
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
info.Artifacts ??= [];
|
||||
|
||||
if (File.Exists(basePath + "_disc.txt"))
|
||||
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt"));
|
||||
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt")) ?? string.Empty;
|
||||
if (File.Exists(basePath + "_drive.txt"))
|
||||
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt"));
|
||||
info.Artifacts["drive"] = GetBase64(GetFullFile(basePath + "_drive.txt")) ?? string.Empty;
|
||||
if (File.Exists(basePath + "_mainError.txt"))
|
||||
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt"));
|
||||
info.Artifacts["mainError"] = GetBase64(GetFullFile(basePath + "_mainError.txt")) ?? string.Empty;
|
||||
if (File.Exists(basePath + "_mainInfo.txt"))
|
||||
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt"));
|
||||
info.Artifacts["mainInfo"] = GetBase64(GetFullFile(basePath + "_mainInfo.txt")) ?? string.Empty;
|
||||
if (File.Exists(basePath + "_volDesc.txt"))
|
||||
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt"));
|
||||
info.Artifacts["volDesc"] = GetBase64(GetFullFile(basePath + "_volDesc.txt")) ?? string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
{
|
||||
List<string> logFiles = new List<string>();
|
||||
var logFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.UMD:
|
||||
@@ -144,34 +160,32 @@ namespace MPF.Modules.UmdImageCreator
|
||||
/// </summary>
|
||||
/// <param name="mainInfo">_mainInfo.txt file location</param>
|
||||
/// <returns>Newline-deliminated PVD if possible, null on error</returns>
|
||||
private static string GetPVD(string mainInfo)
|
||||
private static string? GetPVD(string mainInfo)
|
||||
{
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(mainInfo))
|
||||
return null;
|
||||
|
||||
using (StreamReader sr = File.OpenText(mainInfo))
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
// Make sure we're in the right sector
|
||||
while (!sr.ReadLine().StartsWith("========== LBA[000016, 0x0000010]: Main Channel ==========")) ;
|
||||
// Make sure we're in the right sector
|
||||
using var sr = File.OpenText(mainInfo);
|
||||
while (sr.ReadLine()?.StartsWith("========== LBA[000016, 0x0000010]: Main Channel ==========") == false) ;
|
||||
|
||||
// Fast forward to the PVD
|
||||
while (!sr.ReadLine().StartsWith("0310")) ;
|
||||
// Fast forward to the PVD
|
||||
while (sr.ReadLine()?.StartsWith("0310") == false) ;
|
||||
|
||||
// Now that we're at the PVD, read each line in and concatenate
|
||||
string pvd = "";
|
||||
for (int i = 0; i < 6; i++)
|
||||
pvd += sr.ReadLine() + "\n"; // 320-370
|
||||
// Now that we're at the PVD, read each line in and concatenate
|
||||
string pvd = "";
|
||||
for (int i = 0; i < 6; i++)
|
||||
pvd += sr.ReadLine() + "\n"; // 320-370
|
||||
|
||||
return pvd;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
return pvd;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +194,7 @@ namespace MPF.Modules.UmdImageCreator
|
||||
/// </summary>
|
||||
/// <param name="disc">_disc.txt file location</param>
|
||||
/// <returns>True on successful extraction of info, false otherwise</returns>
|
||||
private static bool GetUMDAuxInfo(string disc, out string title, out DiscCategory? umdcat, out string umdversion, out string umdlayer, out long umdsize)
|
||||
private static bool GetUMDAuxInfo(string disc, out string? title, out DiscCategory? umdcat, out string? umdversion, out string? umdlayer, out long umdsize)
|
||||
{
|
||||
title = null; umdcat = null; umdversion = null; umdlayer = null; umdsize = -1;
|
||||
|
||||
@@ -188,39 +202,38 @@ namespace MPF.Modules.UmdImageCreator
|
||||
if (!File.Exists(disc))
|
||||
return false;
|
||||
|
||||
using (StreamReader sr = File.OpenText(disc))
|
||||
try
|
||||
{
|
||||
try
|
||||
// Loop through everything to get the first instance of each required field
|
||||
using var sr = File.OpenText(disc);
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
// Loop through everything to get the first instance of each required field
|
||||
string line = string.Empty;
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
line = sr.ReadLine().Trim();
|
||||
string? line = sr.ReadLine()?.Trim();
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
if (line.StartsWith("TITLE") && title == null)
|
||||
title = line.Substring("TITLE: ".Length);
|
||||
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
|
||||
umdversion = line.Split(' ')[1];
|
||||
else if (line.StartsWith("pspUmdTypes"))
|
||||
umdcat = GetUMDCategory(line.Split(' ')[1]);
|
||||
else if (line.StartsWith("L0 length"))
|
||||
umdlayer = line.Split(' ')[2];
|
||||
else if (line.StartsWith("FileSize:"))
|
||||
umdsize = Int64.Parse(line.Split(' ')[1]);
|
||||
}
|
||||
|
||||
// If the L0 length is the size of the full disc, there's no layerbreak
|
||||
if (Int64.Parse(umdlayer) * 2048 == umdsize)
|
||||
umdlayer = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return false;
|
||||
if (line.StartsWith("TITLE") && title == null)
|
||||
title = line.Substring("TITLE: ".Length);
|
||||
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
|
||||
umdversion = line.Split(' ')[1];
|
||||
else if (line.StartsWith("pspUmdTypes"))
|
||||
umdcat = InfoTool.GetUMDCategory(line.Split(' ')[1]);
|
||||
else if (line.StartsWith("L0 length"))
|
||||
umdlayer = line.Split(' ')[2];
|
||||
else if (line.StartsWith("FileSize:"))
|
||||
umdsize = Int64.Parse(line.Split(' ')[1]);
|
||||
}
|
||||
|
||||
// If the L0 length is the size of the full disc, there's no layerbreak
|
||||
if (Int64.TryParse(umdlayer, out long umdlayerValue) && umdlayerValue * 2048 == umdsize)
|
||||
umdlayer = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using BinaryObjectScanner.Protection;
|
||||
using BurnOutSharp;
|
||||
using psxt001z;
|
||||
|
||||
namespace MPF.Library
|
||||
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'.
|
||||
|
||||
namespace MPF.Core
|
||||
{
|
||||
public static class Protection
|
||||
{
|
||||
@@ -19,13 +19,14 @@ namespace MPF.Library
|
||||
/// <param name="options">Options object that determines what to scan</param>
|
||||
/// <param name="progress">Optional progress callback</param>
|
||||
/// <returns>Set of all detected copy protections with an optional error string</returns>
|
||||
public static async Task<(Dictionary<string, List<string>>, string)> RunProtectionScanOnPath(string path, Core.Data.Options options, IProgress<ProtectionProgress> progress = null)
|
||||
public static async Task<(Dictionary<string, List<string>>?, string?)> RunProtectionScanOnPath(string path, Data.Options options, IProgress<BinaryObjectScanner.ProtectionProgress>? progress = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var found = await Task.Run(() =>
|
||||
#if NET40
|
||||
var found = await Task.Factory.StartNew(() =>
|
||||
{
|
||||
var scanner = new Scanner(
|
||||
var scanner = new BinaryObjectScanner.Scanner(
|
||||
options.ScanArchivesForProtection,
|
||||
scanContents: true, // Hardcoded value to avoid issues
|
||||
scanGameEngines: false, // Hardcoded value to avoid issues
|
||||
@@ -36,14 +37,37 @@ namespace MPF.Library
|
||||
|
||||
return scanner.GetProtections(path);
|
||||
});
|
||||
#else
|
||||
var found = await Task.Run(() =>
|
||||
{
|
||||
var scanner = new BinaryObjectScanner.Scanner(
|
||||
options.ScanArchivesForProtection,
|
||||
scanContents: true, // Hardcoded value to avoid issues
|
||||
scanGameEngines: false, // Hardcoded value to avoid issues
|
||||
options.ScanPackersForProtection,
|
||||
scanPaths: true, // Hardcoded value to avoid issues
|
||||
options.IncludeDebugProtectionInformation,
|
||||
progress);
|
||||
|
||||
return scanner.GetProtections(path);
|
||||
});
|
||||
#endif
|
||||
|
||||
// If nothing was returned, return
|
||||
if (found == null || !found.Any())
|
||||
#if NET20 || NET35
|
||||
if (found == null || found.Count == 0)
|
||||
#else
|
||||
if (found == null || found.IsEmpty)
|
||||
#endif
|
||||
return (null, null);
|
||||
|
||||
// Filter out any empty protections
|
||||
var filteredProtections = found
|
||||
.Where(kvp => kvp.Value != null && kvp.Value.Any())
|
||||
#if NET20 || NET35
|
||||
.Where(kvp => kvp.Value != null && kvp.Value.Count > 0)
|
||||
#else
|
||||
.Where(kvp => kvp.Value != null && !kvp.Value.IsEmpty)
|
||||
#endif
|
||||
.ToDictionary(
|
||||
kvp => kvp.Key,
|
||||
kvp => kvp.Value.OrderBy(s => s).ToList());
|
||||
@@ -62,7 +86,7 @@ namespace MPF.Library
|
||||
/// </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)
|
||||
public static string? FormatProtections(Dictionary<string, List<string>>? protections)
|
||||
{
|
||||
// If the filtered list is empty in some way, return
|
||||
if (protections == null || !protections.Any())
|
||||
@@ -76,7 +100,7 @@ namespace MPF.Library
|
||||
|
||||
// Sanitize and join protections for writing
|
||||
string protectionString = SanitizeFoundProtections(orderedDistinctProtections);
|
||||
if (string.IsNullOrWhiteSpace(protectionString))
|
||||
if (string.IsNullOrEmpty(protectionString))
|
||||
return "None found [OMIT FROM SUBMISSION]";
|
||||
|
||||
return protectionString;
|
||||
@@ -87,20 +111,25 @@ namespace MPF.Library
|
||||
/// </summary>
|
||||
/// <param name="path">Path to scan for anti-modchip strings</param>
|
||||
/// <returns>Anti-modchip existence if possible, false on error</returns>
|
||||
public static async Task<bool> GetPlayStationAntiModchipDetected(string path)
|
||||
public static async Task<bool> GetPlayStationAntiModchipDetected(string? path)
|
||||
{
|
||||
return await Task.Run(() =>
|
||||
// If there is no valid path
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return false;
|
||||
|
||||
#if NET40
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var antiModchip = new PSXAntiModchip();
|
||||
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
|
||||
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] fileContent = File.ReadAllBytes(file);
|
||||
string protection = antiModchip.CheckContents(file, fileContent, false);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
var protection = antiModchip.CheckContents(file, fileContent, false);
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
return true;
|
||||
}
|
||||
catch { }
|
||||
@@ -110,6 +139,33 @@ namespace MPF.Library
|
||||
|
||||
return false;
|
||||
});
|
||||
#else
|
||||
return await Task.Run(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var antiModchip = new BinaryObjectScanner.Protection.PSXAntiModchip();
|
||||
#if NET20 || NET35
|
||||
foreach (string file in Directory.GetFiles(path, "*", SearchOption.AllDirectories))
|
||||
#else
|
||||
foreach (string file in Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories))
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
byte[] fileContent = File.ReadAllBytes(file);
|
||||
var protection = antiModchip.CheckContents(file, fileContent, false);
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
return true;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
return false;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -123,7 +179,7 @@ namespace MPF.Library
|
||||
if (!File.Exists(sub))
|
||||
return null;
|
||||
|
||||
return LibCrypt.CheckSubfile(sub);
|
||||
return LibCrypt.DetectLibCrypt([sub]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -136,9 +192,15 @@ namespace MPF.Library
|
||||
if (foundProtections.Any(p => p.StartsWith("[Exception opening file")))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("[Exception opening file"));
|
||||
#if NET20 || NET35 || NET40 || NET452 || NET462
|
||||
var tempList = new List<string> { "Exception occurred while scanning [RESCAN NEEDED]" };
|
||||
tempList.AddRange(foundProtections);
|
||||
foundProtections = tempList.OrderBy(p => p);
|
||||
#else
|
||||
foundProtections = foundProtections
|
||||
.Prepend("Exception occurred while scanning [RESCAN NEEDED]")
|
||||
.OrderBy(p => p);
|
||||
#endif
|
||||
}
|
||||
|
||||
// ActiveMARK
|
||||
@@ -146,7 +208,7 @@ namespace MPF.Library
|
||||
foundProtections = foundProtections.Where(p => p != "ActiveMARK");
|
||||
|
||||
// Cactus Data Shield
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"Cactus Data Shield [0-9]{3} .+")) && foundProtections.Any(p => p == "Cactus Data Shield 200"))
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"Cactus Data Shield [0-9]{3} .+", RegexOptions.Compiled)) && foundProtections.Any(p => p == "Cactus Data Shield 200"))
|
||||
foundProtections = foundProtections.Where(p => p != "Cactus Data Shield 200");
|
||||
|
||||
// CD-Check
|
||||
@@ -176,7 +238,7 @@ namespace MPF.Library
|
||||
// JoWood X-Prot
|
||||
if (foundProtections.Any(p => p.StartsWith("JoWood X-Prot")))
|
||||
{
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"JoWood X-Prot [0-9]\.[0-9]\.[0-9]\.[0-9]{2}")))
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"JoWood X-Prot [0-9]\.[0-9]\.[0-9]\.[0-9]{2}", RegexOptions.Compiled)))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "JoWood X-Prot")
|
||||
.Where(p => p != "JoWood X-Prot v1.0-v1.3")
|
||||
@@ -219,34 +281,43 @@ namespace MPF.Library
|
||||
.Where(p => p != "Cactus Data Shield 300 (Confirm presence of other CDS-300 files)");
|
||||
|
||||
if (foundProtections.Any(p => !p.StartsWith("SafeDisc")))
|
||||
{
|
||||
#if NET20 || NET35 || NET40 || NET452 || NET462
|
||||
var tempList = new List<string>();
|
||||
tempList.AddRange(foundProtections);
|
||||
tempList.Add("Cactus Data Shield 300");
|
||||
foundProtections = tempList;
|
||||
#else
|
||||
foundProtections = foundProtections.Append("Cactus Data Shield 300");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// SafeDisc
|
||||
if (foundProtections.Any(p => p.StartsWith("SafeDisc")))
|
||||
{
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}")))
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
.Where(p => !p.StartsWith("Macrovision Security Driver"))
|
||||
.Where(p => p != "SafeDisc")
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}")))
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
|
||||
.Where(p => p != "SafeDisc 1/Lite")
|
||||
.Where(p => p != "SafeDisc 2+");
|
||||
}
|
||||
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}")))
|
||||
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}-[0-9]\.[0-9]{2}\.[0-9]{3}", RegexOptions.Compiled)))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
.Where(p => !p.StartsWith("Macrovision Security Driver"))
|
||||
.Where(p => p != "SafeDisc")
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
|
||||
.Where(p => !(Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
|
||||
.Where(p => p != "SafeDisc 1/Lite")
|
||||
.Where(p => p != "SafeDisc 2+");
|
||||
}
|
||||
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+")))
|
||||
else if (foundProtections.Any(p => Regex.IsMatch(p, @"SafeDisc [0-9]\.[0-9]{2}\.[0-9]{3}/+", RegexOptions.Compiled)))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => !p.StartsWith("Macrovision Protected Application"))
|
||||
.Where(p => !p.StartsWith("Macrovision Protection File"))
|
||||
@@ -286,7 +357,7 @@ namespace MPF.Library
|
||||
// StarForce
|
||||
if (foundProtections.Any(p => p.StartsWith("StarForce")))
|
||||
{
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"StarForce [0-9]+\..+")))
|
||||
if (foundProtections.Any(p => Regex.IsMatch(p, @"StarForce [0-9]+\..+", RegexOptions.Compiled)))
|
||||
{
|
||||
foundProtections = foundProtections.Where(p => p != "StarForce")
|
||||
.Where(p => p != "StarForce 3-5")
|
||||
@@ -321,7 +392,7 @@ namespace MPF.Library
|
||||
if (foundProtections.Any(p => p == "XCP") && foundProtections.Any(p => p.StartsWith("XCP") && p.Length > "XCP".Length))
|
||||
foundProtections = foundProtections.Where(p => p != "XCP");
|
||||
|
||||
return string.Join(", ", foundProtections);
|
||||
return string.Join(", ", foundProtections.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
743
MPF.Core/SubmissionInfoTool.cs
Normal file
743
MPF.Core/SubmissionInfoTool.cs
Normal file
@@ -0,0 +1,743 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Modules;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace MPF.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Class to hold all SubmissionInfo-related methods
|
||||
/// </summary>
|
||||
internal static class SubmissionInfoTool
|
||||
{
|
||||
#region Extraction and Filling
|
||||
|
||||
/// <summary>
|
||||
/// Extract all of the possible information from a given input combination
|
||||
/// </summary>
|
||||
/// <param name="outputPath">Output path to write to</param>
|
||||
/// <param name="drive">Drive object representing the current drive</param>
|
||||
/// <param name="system">Currently selected system</param>
|
||||
/// <param name="mediaType">Currently selected media type</param>
|
||||
/// <param name="options">Options object representing user-defined options</param>
|
||||
/// <param name="parameters">Parameters object representing what to send to the internal program</param>
|
||||
/// <param name="resultProgress">Optional result progress callback</param>
|
||||
/// <param name="protectionProgress">Optional protection progress callback</param>
|
||||
/// <returns>SubmissionInfo populated based on outputs, null on error</returns>
|
||||
public static async Task<SubmissionInfo?> ExtractOutputInformation(
|
||||
string outputPath,
|
||||
Drive? drive,
|
||||
RedumpSystem? system,
|
||||
MediaType? mediaType,
|
||||
Options options,
|
||||
BaseParameters? parameters,
|
||||
IProgress<Result>? resultProgress = null,
|
||||
IProgress<BinaryObjectScanner.ProtectionProgress>? protectionProgress = null)
|
||||
{
|
||||
// Ensure the current disc combination should exist
|
||||
if (!system.MediaTypes().Contains(mediaType))
|
||||
return null;
|
||||
|
||||
// Split the output path for easier use
|
||||
var outputDirectory = Path.GetDirectoryName(outputPath);
|
||||
string outputFilename = Path.GetFileName(outputPath);
|
||||
|
||||
// Check that all of the relevant files are there
|
||||
(bool foundFiles, List<string> missingFiles) = parameters.FoundAllFiles(outputDirectory, outputFilename, false);
|
||||
if (!foundFiles)
|
||||
{
|
||||
resultProgress?.Report(Result.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
|
||||
resultProgress?.Report(Result.Failure($"This may indicate an issue with the hardware or media, including unsupported devices.\nPlease see dumping program documentation for more details."));
|
||||
return null;
|
||||
}
|
||||
|
||||
// Sanitize the output filename to strip off any potential extension
|
||||
outputFilename = Path.GetFileNameWithoutExtension(outputFilename);
|
||||
|
||||
// Create the SubmissionInfo object with all user-inputted values by default
|
||||
string combinedBase;
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
combinedBase = outputFilename;
|
||||
else
|
||||
combinedBase = Path.Combine(outputDirectory, outputFilename);
|
||||
|
||||
var info = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = new CommonDiscInfoSection()
|
||||
{
|
||||
System = system,
|
||||
Media = mediaType.ToDiscType(),
|
||||
Title = options.AddPlaceholders ? Template.RequiredValue : string.Empty,
|
||||
ForeignTitleNonLatin = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
|
||||
DiscNumberLetter = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
|
||||
DiscTitle = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
|
||||
Category = null,
|
||||
Region = null,
|
||||
Languages = null,
|
||||
Serial = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty,
|
||||
Barcode = options.AddPlaceholders ? Template.OptionalValue : string.Empty,
|
||||
Contents = string.Empty,
|
||||
},
|
||||
VersionAndEditions = new VersionAndEditionsSection()
|
||||
{
|
||||
Version = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty,
|
||||
OtherEditions = options.AddPlaceholders ? "(VERIFY THIS) Original" : string.Empty,
|
||||
},
|
||||
DumpingInfo = new DumpingInfoSection()
|
||||
{
|
||||
FrontendVersion = Utilities.Tools.GetCurrentVersion(),
|
||||
},
|
||||
};
|
||||
|
||||
// Ensure that required sections exist
|
||||
info = Builder.EnsureAllSections(info);
|
||||
|
||||
// Get specific tool output handling
|
||||
parameters?.GenerateSubmissionInfo(info, options, combinedBase, drive, options.IncludeArtifacts);
|
||||
|
||||
// Get a list of matching IDs for each line in the DAT
|
||||
if (!string.IsNullOrEmpty(info.TracksAndWriteOffsets!.ClrMameProData) && options.HasRedumpLogin)
|
||||
#if NET40
|
||||
_ = FillFromRedump(options, info, resultProgress);
|
||||
#else
|
||||
_ = await FillFromRedump(options, info, resultProgress);
|
||||
#endif
|
||||
|
||||
// If we have both ClrMamePro and Size and Checksums data, remove the ClrMamePro
|
||||
if (!string.IsNullOrEmpty(info.SizeAndChecksums?.CRC32))
|
||||
info.TracksAndWriteOffsets.ClrMameProData = null;
|
||||
|
||||
// Add the volume label to comments, if possible or necessary
|
||||
string? volLabels = FormatVolumeLabels(drive?.VolumeLabel, parameters?.VolumeLabels);
|
||||
if (volLabels != null)
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.VolumeLabel] = volLabels;
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (mediaType)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
case MediaType.GDROM:
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
break;
|
||||
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
|
||||
// If we have a single-layer disc
|
||||
if (info.SizeAndChecksums!.Layerbreak == default)
|
||||
{
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
}
|
||||
// If we have a dual-layer disc
|
||||
else if (info.SizeAndChecksums!.Layerbreak2 == default)
|
||||
{
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
}
|
||||
// If we have a triple-layer disc
|
||||
else if (info.SizeAndChecksums!.Layerbreak3 == default)
|
||||
{
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer2MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer2MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer2ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
}
|
||||
// If we have a quad-layer disc
|
||||
else
|
||||
{
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer2MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer2MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer2ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer3MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer3MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer3ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.Extras!.BCA ??= (options.AddPlaceholders ? Template.RequiredValue : string.Empty);
|
||||
break;
|
||||
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
|
||||
// If we have a single-layer disc
|
||||
if (info.SizeAndChecksums!.Layerbreak == default)
|
||||
{
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
}
|
||||
// If we have a dual-layer disc
|
||||
else
|
||||
{
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
}
|
||||
|
||||
info.Extras!.DiscKey = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
info.Extras.BCA = info.Extras.BCA ?? (options.AddPlaceholders ? Template.RequiredValue : string.Empty);
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.UMD:
|
||||
// Both single- and dual-layer discs have two "layers" for the ring
|
||||
info.CommonDiscInfo!.Layer0MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0MouldSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Layer1MasteringRing = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MasteringSID = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1ToolstampMasteringCode = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
|
||||
info.SizeAndChecksums!.CRC32 ??= (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty);
|
||||
info.SizeAndChecksums.MD5 ??= (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty);
|
||||
info.SizeAndChecksums.SHA1 ??= (options.AddPlaceholders ? Template.RequiredValue + " [Not automatically generated for UMD]" : string.Empty);
|
||||
info.TracksAndWriteOffsets.ClrMameProData = null;
|
||||
break;
|
||||
}
|
||||
|
||||
// Extract info based specifically on RedumpSystem
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.AcornArchimedes:
|
||||
info.CommonDiscInfo!.Region ??= Region.UnitedKingdom;
|
||||
break;
|
||||
|
||||
case RedumpSystem.AppleMacintosh:
|
||||
case RedumpSystem.EnhancedCD:
|
||||
case RedumpSystem.IBMPCcompatible:
|
||||
case RedumpSystem.PalmOS:
|
||||
case RedumpSystem.PocketPC:
|
||||
case RedumpSystem.RainbowDisc:
|
||||
case RedumpSystem.SonyElectronicBook:
|
||||
resultProgress?.Report(Result.Success("Running copy protection scan... this might take a while!"));
|
||||
var (protectionString, fullProtections) = await InfoTool.GetCopyProtection(drive, options, protectionProgress);
|
||||
|
||||
info.CopyProtection!.Protection += protectionString;
|
||||
info.CopyProtection.FullProtections = fullProtections as Dictionary<string, List<string>?> ?? [];
|
||||
resultProgress?.Report(Result.Success("Copy protection scan complete!"));
|
||||
|
||||
break;
|
||||
|
||||
case RedumpSystem.AudioCD:
|
||||
case RedumpSystem.DVDAudio:
|
||||
case RedumpSystem.SuperAudioCD:
|
||||
info.CommonDiscInfo!.Category ??= DiscCategory.Audio;
|
||||
break;
|
||||
|
||||
case RedumpSystem.BandaiPlaydiaQuickInteractiveSystem:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? Region.Japan;
|
||||
break;
|
||||
|
||||
case RedumpSystem.BDVideo:
|
||||
info.CommonDiscInfo!.Category ??= DiscCategory.BonusDiscs;
|
||||
info.CopyProtection!.Protection = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.CommodoreAmigaCD:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.CommodoreAmigaCD32:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
info.CommonDiscInfo.Region ??= Region.Europe;
|
||||
break;
|
||||
|
||||
case RedumpSystem.CommodoreAmigaCDTV:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
info.CommonDiscInfo.Region ??= Region.Europe;
|
||||
break;
|
||||
|
||||
case RedumpSystem.DVDVideo:
|
||||
info.CommonDiscInfo!.Category ??= DiscCategory.BonusDiscs;
|
||||
break;
|
||||
|
||||
case RedumpSystem.FujitsuFMTownsseries:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? Region.Japan;
|
||||
break;
|
||||
|
||||
case RedumpSystem.FujitsuFMTownsMarty:
|
||||
info.CommonDiscInfo!.Region ??= Region.Japan;
|
||||
break;
|
||||
|
||||
case RedumpSystem.IncredibleTechnologiesEagle:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamieAmusement:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiFireBeat:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiSystemGV:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiSystem573:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiTwinkle:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.MattelHyperScan:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXboxOne:
|
||||
if (drive?.Name != null)
|
||||
{
|
||||
string xboxOneMsxcPath = Path.Combine(drive.Name, "MSXC");
|
||||
if (drive != null && Directory.Exists(xboxOneMsxcPath))
|
||||
{
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = string.Join("\n",
|
||||
Directory.GetFiles(xboxOneMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXboxSeriesXS:
|
||||
if (drive?.Name != null)
|
||||
{
|
||||
string xboxSeriesXMsxcPath = Path.Combine(drive.Name, "MSXC");
|
||||
if (drive != null && Directory.Exists(xboxSeriesXMsxcPath))
|
||||
{
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = string.Join("\n",
|
||||
Directory.GetFiles(xboxSeriesXMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RedumpSystem.NamcoSegaNintendoTriforce:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.NavisoftNaviken21:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
info.CommonDiscInfo.Region ??= Region.Japan;
|
||||
break;
|
||||
|
||||
case RedumpSystem.NECPC88series:
|
||||
info.CommonDiscInfo!.Region ??= Region.Japan;
|
||||
break;
|
||||
|
||||
case RedumpSystem.NECPC98series:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
info.CommonDiscInfo!.Region ??= Region.Japan;
|
||||
break;
|
||||
|
||||
case RedumpSystem.NECPCFXPCFXGA:
|
||||
info.CommonDiscInfo!.Region ??= Region.Japan;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaChihiro:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaDreamcast:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaNaomi:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaNaomi2:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaTitanVideo:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SharpX68000:
|
||||
info.CommonDiscInfo!.Region ??= Region.Japan;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SNKNeoGeoCD:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
// Only check the disc if the dumping program couldn't detect
|
||||
if (drive != null && info.CopyProtection!.AntiModchip == YesNo.NULL)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Checking for anti-modchip strings... this might take a while!"));
|
||||
info.CopyProtection.AntiModchip = await InfoTool.GetAntiModchipDetected(drive) ? YesNo.Yes : YesNo.No;
|
||||
resultProgress?.Report(Result.Success("Anti-modchip string scan complete!"));
|
||||
}
|
||||
|
||||
// Special case for DIC only
|
||||
if (parameters?.InternalProgram == InternalProgram.DiscImageCreator)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Checking for LibCrypt status... this might take a while!"));
|
||||
InfoTool.GetLibCryptDetected(info, combinedBase);
|
||||
resultProgress?.Report(Result.Success("LibCrypt status checking complete!"));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
info.CommonDiscInfo!.LanguageSelection = [LanguageSelection.BiosSettings, LanguageSelection.LanguageSelector, LanguageSelection.OptionsMenu];
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
info.Extras!.DiscKey ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
info.Extras.DiscID ??= options.AddPlaceholders ? Template.RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.TomyKissSite:
|
||||
info.CommonDiscInfo!.Region ??= Region.Japan;
|
||||
break;
|
||||
|
||||
case RedumpSystem.ZAPiTGamesGameWaveFamilyEntertainmentSystem:
|
||||
info.CopyProtection!.Protection = options.AddPlaceholders ? Template.RequiredIfExistsValue : string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set the category if it's not overriden
|
||||
info.CommonDiscInfo!.Category ??= DiscCategory.Games;
|
||||
|
||||
// Comments and contents have odd handling
|
||||
if (string.IsNullOrEmpty(info.CommonDiscInfo.Comments))
|
||||
info.CommonDiscInfo.Comments = options.AddPlaceholders ? Template.OptionalValue : string.Empty;
|
||||
if (string.IsNullOrEmpty(info.CommonDiscInfo.Contents))
|
||||
info.CommonDiscInfo.Contents = options.AddPlaceholders ? Template.OptionalValue : string.Empty;
|
||||
|
||||
// Normalize the disc type with all current information
|
||||
Validator.NormalizeDiscType(info);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill in a SubmissionInfo object from Redump, if possible
|
||||
/// </summary>
|
||||
/// <param name="options">Options object representing user-defined options</param>
|
||||
/// <param name="info">Existing SubmissionInfo object to fill</param>
|
||||
/// <param name="resultProgress">Optional result progress callback</param>
|
||||
#if NET40
|
||||
public static bool FillFromRedump(Options options, SubmissionInfo info, IProgress<Result>? resultProgress = null)
|
||||
#else
|
||||
public async static Task<bool> FillFromRedump(Options options, SubmissionInfo info, IProgress<Result>? resultProgress = null)
|
||||
#endif
|
||||
{
|
||||
// If no username is provided
|
||||
if (string.IsNullOrEmpty(options.RedumpUsername) || string.IsNullOrEmpty(options.RedumpPassword))
|
||||
return false;
|
||||
|
||||
// Set the current dumper based on username
|
||||
info.DumpersAndStatus ??= new DumpersAndStatusSection();
|
||||
info.DumpersAndStatus.Dumpers = [options.RedumpUsername!];
|
||||
info.PartiallyMatchedIDs = [];
|
||||
|
||||
// Login to Redump
|
||||
#if NETFRAMEWORK
|
||||
using var wc = new RedumpWebClient();
|
||||
bool? loggedIn = wc.Login(options.RedumpUsername!, options.RedumpPassword!);
|
||||
#else
|
||||
using var wc = new RedumpHttpClient();
|
||||
bool? loggedIn = await wc.Login(options.RedumpUsername, options.RedumpPassword);
|
||||
#endif
|
||||
if (loggedIn == null)
|
||||
{
|
||||
resultProgress?.Report(Result.Failure("There was an unknown error connecting to Redump"));
|
||||
return false;
|
||||
}
|
||||
else if (loggedIn == false)
|
||||
{
|
||||
// Don't log the as a failure or error
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup the full-track checks
|
||||
bool allFound = true;
|
||||
List<int>? fullyMatchedIDs = null;
|
||||
|
||||
// Loop through all of the hashdata to find matching IDs
|
||||
resultProgress?.Report(Result.Success("Finding disc matches on Redump..."));
|
||||
var splitData = info.TracksAndWriteOffsets?.ClrMameProData?.TrimEnd('\n')?.Split('\n');
|
||||
int trackCount = splitData?.Length ?? 0;
|
||||
foreach (string hashData in splitData ?? [])
|
||||
{
|
||||
// Catch any errant blank lines
|
||||
if (string.IsNullOrEmpty(hashData))
|
||||
{
|
||||
trackCount--;
|
||||
resultProgress?.Report(Result.Success("Blank line found, skipping!"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the line ends in a known extra track names, skip them for checking
|
||||
if (hashData.Contains("(Track 0).bin")
|
||||
|| hashData.Contains("(Track 0.2).bin")
|
||||
|| hashData.Contains("(Track 00).bin")
|
||||
|| hashData.Contains("(Track 00.2).bin")
|
||||
|| hashData.Contains("(Track A).bin")
|
||||
|| hashData.Contains("(Track A.2).bin")
|
||||
|| hashData.Contains("(Track AA).bin")
|
||||
|| hashData.Contains("(Track AA.2).bin"))
|
||||
{
|
||||
trackCount--;
|
||||
resultProgress?.Report(Result.Success("Extra track found, skipping!"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the SHA-1 hash
|
||||
if (!InfoTool.GetISOHashValues(hashData, out _, out _, out _, out string? sha1))
|
||||
{
|
||||
resultProgress?.Report(Result.Failure($"Line could not be parsed: {hashData}"));
|
||||
continue;
|
||||
}
|
||||
|
||||
#if NET40
|
||||
var validateTask = Validator.ValidateSingleTrack(wc, info, sha1);
|
||||
validateTask.Wait();
|
||||
(bool singleFound, var foundIds, string? result) = validateTask.Result;
|
||||
#else
|
||||
(bool singleFound, var foundIds, string? result) = await Validator.ValidateSingleTrack(wc, info, sha1);
|
||||
#endif
|
||||
if (singleFound)
|
||||
resultProgress?.Report(Result.Success(result));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(result));
|
||||
|
||||
// Ensure that all tracks are found
|
||||
allFound &= singleFound;
|
||||
|
||||
// If we found a track, only keep track of distinct found tracks
|
||||
if (singleFound && foundIds != null)
|
||||
{
|
||||
if (fullyMatchedIDs == null)
|
||||
fullyMatchedIDs = foundIds;
|
||||
else
|
||||
fullyMatchedIDs = fullyMatchedIDs.Intersect(foundIds).ToList();
|
||||
}
|
||||
// If no tracks were found, remove all fully matched IDs found so far
|
||||
else
|
||||
{
|
||||
fullyMatchedIDs = [];
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have any matches but we have a universal hash
|
||||
if (!info.PartiallyMatchedIDs.Any() && info.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) == true)
|
||||
{
|
||||
#if NET40
|
||||
var validateTask = Validator.ValidateUniversalHash(wc, info);
|
||||
validateTask.Wait();
|
||||
(bool singleFound, var foundIds, string? result) = validateTask.Result;
|
||||
#else
|
||||
(bool singleFound, var foundIds, string? result) = await Validator.ValidateUniversalHash(wc, info);
|
||||
#endif
|
||||
if (singleFound)
|
||||
resultProgress?.Report(Result.Success(result));
|
||||
else
|
||||
resultProgress?.Report(Result.Failure(result));
|
||||
|
||||
// Ensure that the hash is found
|
||||
allFound = singleFound;
|
||||
|
||||
// If we found a track, only keep track of distinct found tracks
|
||||
if (singleFound && foundIds != null)
|
||||
{
|
||||
fullyMatchedIDs = foundIds;
|
||||
}
|
||||
// If no tracks were found, remove all fully matched IDs found so far
|
||||
else
|
||||
{
|
||||
fullyMatchedIDs = [];
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we only have unique IDs
|
||||
info.PartiallyMatchedIDs = [.. info.PartiallyMatchedIDs.Distinct().OrderBy(id => id)];
|
||||
|
||||
resultProgress?.Report(Result.Success("Match finding complete! " + (fullyMatchedIDs != null && fullyMatchedIDs.Count > 0
|
||||
? "Fully Matched IDs: " + string.Join(",", fullyMatchedIDs.Select(i => i.ToString()).ToArray())
|
||||
: "No matches found")));
|
||||
|
||||
// Exit early if one failed or there are no matched IDs
|
||||
if (!allFound || fullyMatchedIDs == null || fullyMatchedIDs.Count == 0)
|
||||
return false;
|
||||
|
||||
// Find the first matched ID where the track count matches, we can grab a bunch of info from it
|
||||
int totalMatchedIDsCount = fullyMatchedIDs.Count;
|
||||
for (int i = 0; i < totalMatchedIDsCount; i++)
|
||||
{
|
||||
// Skip if the track count doesn't match
|
||||
#if NET40
|
||||
var validateTask = Validator.ValidateTrackCount(wc, fullyMatchedIDs[i], trackCount);
|
||||
validateTask.Wait();
|
||||
if (!validateTask.Result)
|
||||
#else
|
||||
if (!await Validator.ValidateTrackCount(wc, fullyMatchedIDs[i], trackCount))
|
||||
#endif
|
||||
continue;
|
||||
|
||||
// Fill in the fields from the existing ID
|
||||
resultProgress?.Report(Result.Success($"Filling fields from existing ID {fullyMatchedIDs[i]}..."));
|
||||
#if NET40
|
||||
var fillTask = Task.Factory.StartNew(() => Builder.FillFromId(wc, info, fullyMatchedIDs[i], options.PullAllInformation));
|
||||
fillTask.Wait();
|
||||
_ = fillTask.Result;
|
||||
#else
|
||||
_ = await Builder.FillFromId(wc, info, fullyMatchedIDs[i], options.PullAllInformation);
|
||||
#endif
|
||||
resultProgress?.Report(Result.Success("Information filling complete!"));
|
||||
|
||||
// Set the fully matched ID to the current
|
||||
info.FullyMatchedID = fullyMatchedIDs[i];
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear out fully matched IDs from the partial list
|
||||
if (info.FullyMatchedID.HasValue)
|
||||
{
|
||||
if (info.PartiallyMatchedIDs.Count == 1)
|
||||
info.PartiallyMatchedIDs = null;
|
||||
else
|
||||
info.PartiallyMatchedIDs.Remove(info.FullyMatchedID.Value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helper Functions
|
||||
|
||||
/// <summary>
|
||||
/// Formats a list of volume labels and their corresponding filesystems
|
||||
/// </summary>
|
||||
/// <param name="labels">Dictionary of volume labels and their filesystems</param>
|
||||
/// <returns>Formatted string of volume labels and their filesystems</returns>
|
||||
private static string? FormatVolumeLabels(string? driveLabel, Dictionary<string, List<string>>? labels)
|
||||
{
|
||||
// Must have at least one label to format
|
||||
if (driveLabel == null && (labels == null || labels.Count == 0))
|
||||
return null;
|
||||
|
||||
// If no labels given, use drive label
|
||||
if (labels == null || labels.Count == 0)
|
||||
{
|
||||
// Ignore common volume labels
|
||||
if (Drive.GetRedumpSystemFromVolumeLabel(driveLabel) != null)
|
||||
return null;
|
||||
|
||||
return driveLabel;
|
||||
}
|
||||
|
||||
// If only one label, don't mention fs
|
||||
string firstLabel = labels.First().Key;
|
||||
if (labels.Count == 1 && (firstLabel == driveLabel || driveLabel == null))
|
||||
{
|
||||
// Ignore common volume labels
|
||||
if (Drive.GetRedumpSystemFromVolumeLabel(firstLabel) != null)
|
||||
return null;
|
||||
|
||||
return firstLabel;
|
||||
}
|
||||
|
||||
// Otherwise, state filesystem for each label
|
||||
List<string> volLabels = [];
|
||||
|
||||
// Begin formatted output with the label from Windows, if it is unique and not a common volume label
|
||||
if (driveLabel != null && !labels.TryGetValue(driveLabel, out List<string>? value) && Drive.GetRedumpSystemFromVolumeLabel(driveLabel) == null)
|
||||
volLabels.Add(driveLabel);
|
||||
|
||||
// Add remaining labels with their corresponding filesystems
|
||||
foreach (KeyValuePair<string, List<string>> label in labels)
|
||||
{
|
||||
// Ignore common volume labels
|
||||
if (Drive.GetRedumpSystemFromVolumeLabel(label.Key) == null)
|
||||
volLabels.Add($"{label.Key} ({string.Join(", ", [.. label.Value])})");
|
||||
}
|
||||
|
||||
// Ensure that no labels are empty
|
||||
volLabels = volLabels.Where(l => !string.IsNullOrEmpty(l?.Trim())).ToList();
|
||||
|
||||
// Print each label separated by a comma and a space
|
||||
if (volLabels.Count == 0)
|
||||
return null;
|
||||
|
||||
return string.Join(", ", [.. volLabels]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,13 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
|
||||
namespace MPF.UI.Core.ComboBoxItems
|
||||
namespace MPF.Core.UI.ComboBoxItems
|
||||
{
|
||||
/// <summary>
|
||||
/// A generic combo box element
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Enum type representing the possible values</typeparam>
|
||||
public class Element<T> : IElement where T : struct, Enum
|
||||
public class Element<T> : IEquatable<Element<T>>, IElement where T : struct, Enum
|
||||
{
|
||||
private readonly T Data;
|
||||
|
||||
@@ -47,5 +47,23 @@ namespace MPF.UI.Core.ComboBoxItems
|
||||
.OfType<T>()
|
||||
.Select(e => new Element<T>(e));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return Equals(obj as Element<T>);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(Element<T>? other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return Name == other.Name;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode() => base.GetHashCode();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace MPF.UI.Core.ComboBoxItems
|
||||
namespace MPF.Core.UI.ComboBoxItems
|
||||
{
|
||||
public interface IElement
|
||||
{
|
||||
@@ -1,18 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.UI.Core.ComboBoxItems;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.UI.Core.ComboBoxItems
|
||||
namespace MPF.Core.UI.ComboBoxItems
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single item in the System combo box
|
||||
/// </summary>
|
||||
public class RedumpSystemComboBoxItem : IElement
|
||||
public class RedumpSystemComboBoxItem : IEquatable<RedumpSystemComboBoxItem>, IElement
|
||||
{
|
||||
private readonly object Data;
|
||||
private readonly object? Data;
|
||||
|
||||
public RedumpSystemComboBoxItem(RedumpSystem? system) => Data = system;
|
||||
public RedumpSystemComboBoxItem(SystemCategory? category) => Data = category;
|
||||
@@ -81,5 +79,23 @@ namespace MPF.UI.Core.ComboBoxItems
|
||||
|
||||
return systemsValues;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return Equals(obj as RedumpSystemComboBoxItem);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Equals(RedumpSystemComboBoxItem? other)
|
||||
{
|
||||
if (other == null)
|
||||
return false;
|
||||
|
||||
return Value == other.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int GetHashCode() => base.GetHashCode();
|
||||
}
|
||||
}
|
||||
484
MPF.Core/UI/ViewModels/CheckDumpViewModel.cs
Normal file
484
MPF.Core/UI/ViewModels/CheckDumpViewModel.cs
Normal file
@@ -0,0 +1,484 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ComboBoxItems;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public class CheckDumpViewModel : INotifyPropertyChanged
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Access to the current options
|
||||
/// </summary>
|
||||
public Data.Options Options
|
||||
{
|
||||
get => _options;
|
||||
}
|
||||
private readonly Data.Options _options;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if SelectionChanged events can be executed
|
||||
/// </summary>
|
||||
public bool CanExecuteSelectionChanged { get; private set; } = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected system value
|
||||
/// </summary>
|
||||
public RedumpSystem? CurrentSystem
|
||||
{
|
||||
get => _currentSystem;
|
||||
set
|
||||
{
|
||||
_currentSystem = value;
|
||||
TriggerPropertyChanged(nameof(CurrentSystem));
|
||||
}
|
||||
}
|
||||
private RedumpSystem? _currentSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the system type combo box
|
||||
/// </summary>
|
||||
public bool SystemTypeComboBoxEnabled
|
||||
{
|
||||
get => _systemTypeComboBoxEnabled;
|
||||
set
|
||||
{
|
||||
_systemTypeComboBoxEnabled = value;
|
||||
TriggerPropertyChanged(nameof(SystemTypeComboBoxEnabled));
|
||||
}
|
||||
}
|
||||
private bool _systemTypeComboBoxEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected media type value
|
||||
/// </summary>
|
||||
public MediaType? CurrentMediaType
|
||||
{
|
||||
get => _currentMediaType;
|
||||
set
|
||||
{
|
||||
_currentMediaType = value;
|
||||
TriggerPropertyChanged(nameof(CurrentMediaType));
|
||||
}
|
||||
}
|
||||
private MediaType? _currentMediaType;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the media type combo box
|
||||
/// </summary>
|
||||
public bool MediaTypeComboBoxEnabled
|
||||
{
|
||||
get => _mediaTypeComboBoxEnabled;
|
||||
set
|
||||
{
|
||||
_mediaTypeComboBoxEnabled = value;
|
||||
TriggerPropertyChanged(nameof(MediaTypeComboBoxEnabled));
|
||||
}
|
||||
}
|
||||
private bool _mediaTypeComboBoxEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Currently provided input path
|
||||
/// </summary>
|
||||
public string? InputPath
|
||||
{
|
||||
get => _inputPath;
|
||||
set
|
||||
{
|
||||
_inputPath = value;
|
||||
TriggerPropertyChanged(nameof(InputPath));
|
||||
}
|
||||
}
|
||||
private string? _inputPath;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the input path text box
|
||||
/// </summary>
|
||||
public bool InputPathTextBoxEnabled
|
||||
{
|
||||
get => _inputPathTextBoxEnabled;
|
||||
set
|
||||
{
|
||||
_inputPathTextBoxEnabled = value;
|
||||
TriggerPropertyChanged(nameof(InputPathTextBoxEnabled));
|
||||
}
|
||||
}
|
||||
private bool _inputPathTextBoxEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the input path browse button
|
||||
/// </summary>
|
||||
public bool InputPathBrowseButtonEnabled
|
||||
{
|
||||
get => _inputPathBrowseButtonEnabled;
|
||||
set
|
||||
{
|
||||
_inputPathBrowseButtonEnabled = value;
|
||||
TriggerPropertyChanged(nameof(InputPathBrowseButtonEnabled));
|
||||
}
|
||||
}
|
||||
private bool _inputPathBrowseButtonEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected dumping program
|
||||
/// </summary>
|
||||
public InternalProgram CurrentProgram
|
||||
{
|
||||
get => _currentProgram;
|
||||
set
|
||||
{
|
||||
_currentProgram = value;
|
||||
TriggerPropertyChanged(nameof(CurrentProgram));
|
||||
}
|
||||
}
|
||||
private InternalProgram _currentProgram;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the dumping program combo box
|
||||
/// </summary>
|
||||
public bool DumpingProgramComboBoxEnabled
|
||||
{
|
||||
get => _dumpingProgramComboBoxEnabled;
|
||||
set
|
||||
{
|
||||
_dumpingProgramComboBoxEnabled = value;
|
||||
TriggerPropertyChanged(nameof(DumpingProgramComboBoxEnabled));
|
||||
}
|
||||
}
|
||||
private bool _dumpingProgramComboBoxEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the check dump button
|
||||
/// </summary>
|
||||
public bool CheckDumpButtonEnabled
|
||||
{
|
||||
get => _checkDumpButtonEnabled;
|
||||
set
|
||||
{
|
||||
_checkDumpButtonEnabled = value;
|
||||
TriggerPropertyChanged(nameof(CheckDumpButtonEnabled));
|
||||
}
|
||||
}
|
||||
private bool _checkDumpButtonEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the cancel button
|
||||
/// </summary>
|
||||
public bool CancelButtonEnabled
|
||||
{
|
||||
get => _cancelButtonEnabled;
|
||||
set
|
||||
{
|
||||
_cancelButtonEnabled = value;
|
||||
TriggerPropertyChanged(nameof(CancelButtonEnabled));
|
||||
}
|
||||
}
|
||||
private bool _cancelButtonEnabled;
|
||||
|
||||
#endregion
|
||||
|
||||
#region List Properties
|
||||
|
||||
/// <summary>
|
||||
/// Current list of supported media types
|
||||
/// </summary>
|
||||
public List<Element<MediaType>>? MediaTypes
|
||||
{
|
||||
get => _mediaTypes;
|
||||
set
|
||||
{
|
||||
_mediaTypes = value;
|
||||
TriggerPropertyChanged(nameof(MediaTypes));
|
||||
}
|
||||
}
|
||||
private List<Element<MediaType>>? _mediaTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Current list of supported system profiles
|
||||
/// </summary>
|
||||
public List<RedumpSystemComboBoxItem> Systems
|
||||
{
|
||||
get => _systems;
|
||||
set
|
||||
{
|
||||
_systems = value;
|
||||
TriggerPropertyChanged(nameof(Systems));
|
||||
}
|
||||
}
|
||||
private List<RedumpSystemComboBoxItem> _systems;
|
||||
|
||||
/// <summary>
|
||||
/// List of available internal programs
|
||||
/// </summary>
|
||||
public List<Element<InternalProgram>> InternalPrograms
|
||||
{
|
||||
get => _internalPrograms;
|
||||
set
|
||||
{
|
||||
_internalPrograms = value;
|
||||
TriggerPropertyChanged(nameof(InternalPrograms));
|
||||
}
|
||||
}
|
||||
private List<Element<InternalProgram>> _internalPrograms;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for pure view model
|
||||
/// </summary>
|
||||
public CheckDumpViewModel()
|
||||
{
|
||||
_options = OptionsLoader.LoadFromConfig();
|
||||
_internalPrograms = [];
|
||||
_inputPath = string.Empty;
|
||||
_systems = [];
|
||||
|
||||
SystemTypeComboBoxEnabled = true;
|
||||
InputPathTextBoxEnabled = true;
|
||||
InputPathBrowseButtonEnabled = true;
|
||||
MediaTypeComboBoxEnabled = true;
|
||||
DumpingProgramComboBoxEnabled = true;
|
||||
CheckDumpButtonEnabled = false;
|
||||
CancelButtonEnabled = true;
|
||||
|
||||
MediaTypes = [];
|
||||
Systems = RedumpSystemComboBoxItem.GenerateElements().ToList();
|
||||
InternalPrograms = [];
|
||||
|
||||
PopulateMediaType();
|
||||
PopulateInternalPrograms();
|
||||
EnableEventHandlers();
|
||||
}
|
||||
|
||||
#region Property Updates
|
||||
|
||||
/// <summary>
|
||||
/// Trigger a property changed event
|
||||
/// </summary>
|
||||
private void TriggerPropertyChanged(string propertyName)
|
||||
{
|
||||
// Disable event handlers temporarily
|
||||
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
|
||||
DisableEventHandlers();
|
||||
|
||||
// If the property change event is initialized
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
|
||||
// Reenable event handlers, if necessary
|
||||
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Commands
|
||||
|
||||
/// <summary>
|
||||
/// Change the currently selected system
|
||||
/// </summary>
|
||||
public void ChangeSystem()
|
||||
{
|
||||
PopulateMediaType();
|
||||
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the currently selected media type
|
||||
/// </summary>
|
||||
public void ChangeMediaType()
|
||||
{
|
||||
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the currently selected dumping program
|
||||
/// </summary>
|
||||
public void ChangeDumpingProgram()
|
||||
{
|
||||
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change the currently selected input path
|
||||
/// </summary>
|
||||
public void ChangeInputPath()
|
||||
{
|
||||
this.CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Control
|
||||
|
||||
/// <summary>
|
||||
/// Enables all UI elements that should be enabled
|
||||
/// </summary>
|
||||
private void EnableUIElements()
|
||||
{
|
||||
SystemTypeComboBoxEnabled = true;
|
||||
InputPathTextBoxEnabled = true;
|
||||
InputPathBrowseButtonEnabled = true;
|
||||
DumpingProgramComboBoxEnabled = true;
|
||||
PopulateMediaType();
|
||||
CheckDumpButtonEnabled = ShouldEnableCheckDumpButton();
|
||||
CancelButtonEnabled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disables all UI elements
|
||||
/// </summary>
|
||||
private void DisableUIElements()
|
||||
{
|
||||
SystemTypeComboBoxEnabled = false;
|
||||
InputPathTextBoxEnabled = false;
|
||||
InputPathBrowseButtonEnabled = false;
|
||||
MediaTypeComboBoxEnabled = false;
|
||||
DumpingProgramComboBoxEnabled = false;
|
||||
CheckDumpButtonEnabled = false;
|
||||
CancelButtonEnabled = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Population
|
||||
|
||||
/// <summary>
|
||||
/// Populate media type according to system type
|
||||
/// </summary>
|
||||
private void PopulateMediaType()
|
||||
{
|
||||
// Disable other UI updates
|
||||
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
|
||||
DisableEventHandlers();
|
||||
|
||||
if (this.CurrentSystem != null)
|
||||
{
|
||||
var mediaTypeValues = this.CurrentSystem.MediaTypes();
|
||||
int index = mediaTypeValues.FindIndex(m => m == this.CurrentMediaType);
|
||||
|
||||
MediaTypes = mediaTypeValues.Select(m => new Element<MediaType>(m ?? MediaType.NONE)).ToList();
|
||||
this.MediaTypeComboBoxEnabled = MediaTypes.Count > 1;
|
||||
this.CurrentMediaType = (index > -1 ? MediaTypes[index] : MediaTypes[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.MediaTypeComboBoxEnabled = false;
|
||||
this.MediaTypes = null;
|
||||
this.CurrentMediaType = null;
|
||||
}
|
||||
|
||||
// Reenable event handlers, if necessary
|
||||
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate media type according to system type
|
||||
/// </summary>
|
||||
private void PopulateInternalPrograms()
|
||||
{
|
||||
// Disable other UI updates
|
||||
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
|
||||
DisableEventHandlers();
|
||||
|
||||
// Get the current internal program
|
||||
InternalProgram internalProgram = this.Options.InternalProgram;
|
||||
|
||||
// Create a static list of supported Check programs, not everything
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.Aaru, InternalProgram.DiscImageCreator, InternalProgram.CleanRip, InternalProgram.PS3CFW, InternalProgram.UmdImageCreator };
|
||||
InternalPrograms = internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
|
||||
|
||||
// Select the current default dumping program
|
||||
int currentIndex = InternalPrograms.FindIndex(m => m == internalProgram);
|
||||
this.CurrentProgram = (currentIndex > -1 ? InternalPrograms[currentIndex].Value : InternalPrograms[0].Value);
|
||||
|
||||
// Reenable event handlers, if necessary
|
||||
if (cachedCanExecuteSelectionChanged) EnableEventHandlers();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Functionality
|
||||
|
||||
private bool ShouldEnableCheckDumpButton()
|
||||
{
|
||||
return this.CurrentSystem != null && this.CurrentMediaType != null && !string.IsNullOrEmpty(this.InputPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable all textbox and combobox event handlers
|
||||
/// </summary>
|
||||
private void EnableEventHandlers()
|
||||
{
|
||||
CanExecuteSelectionChanged = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disable all textbox and combobox event handlers
|
||||
/// </summary>
|
||||
private void DisableEventHandlers()
|
||||
{
|
||||
CanExecuteSelectionChanged = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region MPF.Check
|
||||
|
||||
/// <summary>
|
||||
/// Performs MPF.Check functionality
|
||||
/// </summary>
|
||||
/// <returns>An error message if failed, otherwise string.Empty/null</returns>
|
||||
public async Task<string?> CheckDump(Func<SubmissionInfo?, (bool?, SubmissionInfo?)> processUserInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(InputPath))
|
||||
return "Invalid Input path";
|
||||
|
||||
if (!File.Exists(this.InputPath!.Trim('"')))
|
||||
return "Input Path is not a valid file";
|
||||
|
||||
// Disable UI while Check is running
|
||||
DisableUIElements();
|
||||
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
|
||||
DisableEventHandlers();
|
||||
|
||||
// Populate an environment
|
||||
var env = new DumpEnvironment(Options, Path.GetFullPath(this.InputPath.Trim('"')), null, this.CurrentSystem, this.CurrentMediaType, this.CurrentProgram, parameters: null);
|
||||
|
||||
// Make new Progress objects
|
||||
var resultProgress = new Progress<Result>();
|
||||
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
var protectionProgress = new Progress<ProtectionProgress>();
|
||||
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
var result = await env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress, processUserInfo);
|
||||
|
||||
// Reenable UI and event handlers, if necessary
|
||||
EnableUIElements();
|
||||
if (cachedCanExecuteSelectionChanged)
|
||||
EnableEventHandlers();
|
||||
|
||||
return result.Message;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1069
MPF.Core/UI/ViewModels/CreateIRDViewModel.cs
Normal file
1069
MPF.Core/UI/ViewModels/CreateIRDViewModel.cs
Normal file
File diff suppressed because it is too large
Load Diff
247
MPF.Core/UI/ViewModels/DiscInformationViewModel.cs
Normal file
247
MPF.Core/UI/ViewModels/DiscInformationViewModel.cs
Normal file
@@ -0,0 +1,247 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ComboBoxItems;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
public class DiscInformationViewModel
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Application-level Options object
|
||||
/// </summary>
|
||||
public Options Options { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SubmissionInfo object to fill and save
|
||||
/// </summary>
|
||||
public SubmissionInfo SubmissionInfo { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lists
|
||||
|
||||
/// <summary>
|
||||
/// List of available disc categories
|
||||
/// </summary>
|
||||
public List<Element<DiscCategory>> Categories { get; private set; } = Element<DiscCategory>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of available regions
|
||||
/// </summary>
|
||||
public List<Element<Region>> Regions { get; private set; } = Element<Region>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of Redump-supported Regions
|
||||
/// </summary>
|
||||
private static readonly List<Region> RedumpRegions = new()
|
||||
{
|
||||
Region.Argentina,
|
||||
Region.Asia,
|
||||
Region.AsiaEurope,
|
||||
Region.AsiaUSA,
|
||||
Region.Australia,
|
||||
Region.AustraliaGermany,
|
||||
Region.AustraliaNewZealand,
|
||||
Region.Austria,
|
||||
Region.AustriaSwitzerland,
|
||||
Region.Belarus,
|
||||
Region.Belgium,
|
||||
Region.BelgiumNetherlands,
|
||||
Region.Brazil,
|
||||
Region.Bulgaria,
|
||||
Region.Canada,
|
||||
Region.China,
|
||||
Region.Croatia,
|
||||
Region.Czechia,
|
||||
Region.Denmark,
|
||||
Region.Estonia,
|
||||
Region.Europe,
|
||||
Region.EuropeAsia,
|
||||
Region.EuropeAustralia,
|
||||
Region.EuropeCanada,
|
||||
Region.EuropeGermany,
|
||||
Region.Export,
|
||||
Region.Finland,
|
||||
Region.France,
|
||||
Region.FranceSpain,
|
||||
Region.Germany,
|
||||
Region.GreaterChina,
|
||||
Region.Greece,
|
||||
Region.Hungary,
|
||||
Region.Iceland,
|
||||
Region.India,
|
||||
Region.Ireland,
|
||||
Region.Israel,
|
||||
Region.Italy,
|
||||
Region.Japan,
|
||||
Region.JapanAsia,
|
||||
Region.JapanEurope,
|
||||
Region.JapanKorea,
|
||||
Region.JapanUSA,
|
||||
Region.SouthKorea,
|
||||
Region.LatinAmerica,
|
||||
Region.Lithuania,
|
||||
Region.Netherlands,
|
||||
Region.NewZealand,
|
||||
Region.Norway,
|
||||
Region.Poland,
|
||||
Region.Portugal,
|
||||
Region.Romania,
|
||||
Region.RussianFederation,
|
||||
Region.Scandinavia,
|
||||
Region.Serbia,
|
||||
Region.Singapore,
|
||||
Region.Slovakia,
|
||||
Region.SouthAfrica,
|
||||
Region.Spain,
|
||||
Region.SpainPortugal,
|
||||
Region.Sweden,
|
||||
Region.Switzerland,
|
||||
Region.Taiwan,
|
||||
Region.Thailand,
|
||||
Region.Turkey,
|
||||
Region.UnitedArabEmirates,
|
||||
Region.UnitedKingdom,
|
||||
Region.UKAustralia,
|
||||
Region.Ukraine,
|
||||
Region.UnitedStatesOfAmerica,
|
||||
Region.USAAsia,
|
||||
Region.USAAustralia,
|
||||
Region.USABrazil,
|
||||
Region.USACanada,
|
||||
Region.USAEurope,
|
||||
Region.USAGermany,
|
||||
Region.USAJapan,
|
||||
Region.USAKorea,
|
||||
Region.World,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// List of available languages
|
||||
/// </summary>
|
||||
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of Redump-supported Languages
|
||||
/// </summary>
|
||||
private static readonly List<Language> RedumpLanguages = new()
|
||||
{
|
||||
Language.Afrikaans,
|
||||
Language.Albanian,
|
||||
Language.Arabic,
|
||||
Language.Armenian,
|
||||
Language.Basque,
|
||||
Language.Belarusian,
|
||||
Language.Bulgarian,
|
||||
Language.Catalan,
|
||||
Language.Chinese,
|
||||
Language.Croatian,
|
||||
Language.Czech,
|
||||
Language.Danish,
|
||||
Language.Dutch,
|
||||
Language.English,
|
||||
Language.Estonian,
|
||||
Language.Finnish,
|
||||
Language.French,
|
||||
Language.Gaelic,
|
||||
Language.German,
|
||||
Language.Greek,
|
||||
Language.Hebrew,
|
||||
Language.Hindi,
|
||||
Language.Hungarian,
|
||||
Language.Icelandic,
|
||||
Language.Indonesian,
|
||||
Language.Italian,
|
||||
Language.Japanese,
|
||||
Language.Korean,
|
||||
Language.Latin,
|
||||
Language.Latvian,
|
||||
Language.Lithuanian,
|
||||
Language.Macedonian,
|
||||
Language.Norwegian,
|
||||
Language.Polish,
|
||||
Language.Portuguese,
|
||||
Language.Panjabi,
|
||||
Language.Romanian,
|
||||
Language.Russian,
|
||||
Language.Serbian,
|
||||
Language.Slovak,
|
||||
Language.Slovenian,
|
||||
Language.Spanish,
|
||||
Language.Swedish,
|
||||
Language.Tamil,
|
||||
Language.Thai,
|
||||
Language.Turkish,
|
||||
Language.Ukrainian,
|
||||
Language.Vietnamese,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// List of available languages
|
||||
/// </summary>
|
||||
public List<Element<LanguageSelection>> LanguageSelections { get; private set; } = Element<LanguageSelection>.GenerateElements().ToList();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public DiscInformationViewModel(Options options, SubmissionInfo? submissionInfo)
|
||||
{
|
||||
Options = options;
|
||||
SubmissionInfo = submissionInfo?.Clone() as SubmissionInfo ?? new SubmissionInfo();
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Load the current contents of the base SubmissionInfo to the UI
|
||||
/// </summary>
|
||||
/// TODO: Convert selected list item to binding
|
||||
public void Load()
|
||||
{
|
||||
if (SubmissionInfo.CommonDiscInfo?.Languages != null)
|
||||
Languages.ForEach(l => l.IsChecked = SubmissionInfo.CommonDiscInfo.Languages.Contains(l));
|
||||
if (SubmissionInfo.CommonDiscInfo?.LanguageSelection != null)
|
||||
LanguageSelections.ForEach(ls => ls.IsChecked = SubmissionInfo.CommonDiscInfo.LanguageSelection.Contains(ls));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current contents of the UI to the base SubmissionInfo
|
||||
/// </summary>
|
||||
/// TODO: Convert selected list item to binding
|
||||
public void Save()
|
||||
{
|
||||
if (SubmissionInfo.CommonDiscInfo == null)
|
||||
SubmissionInfo.CommonDiscInfo = new CommonDiscInfoSection();
|
||||
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
|
||||
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
|
||||
SubmissionInfo.CommonDiscInfo.Languages = [null];
|
||||
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
|
||||
SubmissionInfo.CommonDiscInfo.Title = InfoTool.NormalizeDiscTitle(SubmissionInfo.CommonDiscInfo.Title, SubmissionInfo.CommonDiscInfo.Languages);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulate the list of Languages based on Redump support
|
||||
/// </summary>
|
||||
public void SetRedumpLanguages()
|
||||
{
|
||||
this.Languages = RedumpLanguages.Select(l => new Element<Language>(l)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulate the list of Regions based on Redump support
|
||||
/// </summary>
|
||||
public void SetRedumpRegions()
|
||||
{
|
||||
this.Regions = RedumpRegions.Select(r => new Element<Region>(r)).ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
1975
MPF.Core/UI/ViewModels/MainViewModel.cs
Normal file
1975
MPF.Core/UI/ViewModels/MainViewModel.cs
Normal file
File diff suppressed because it is too large
Load Diff
125
MPF.Core/UI/ViewModels/OptionsViewModel.cs
Normal file
125
MPF.Core/UI/ViewModels/OptionsViewModel.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ComboBoxItems;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public class OptionsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Title for the window
|
||||
/// </summary>
|
||||
public string? Title
|
||||
{
|
||||
get => _title;
|
||||
set
|
||||
{
|
||||
_title = value;
|
||||
TriggerPropertyChanged(nameof(Title));
|
||||
}
|
||||
}
|
||||
private string? _title;
|
||||
|
||||
/// <summary>
|
||||
/// Current set of options
|
||||
/// </summary>
|
||||
public Options Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag for if settings were saved or not
|
||||
/// </summary>
|
||||
public bool SavedSettings { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lists
|
||||
|
||||
/// <summary>
|
||||
/// List of available internal programs
|
||||
/// </summary>
|
||||
public static List<Element<InternalProgram>> InternalPrograms => PopulateInternalPrograms();
|
||||
|
||||
/// <summary>
|
||||
/// Current list of supported system profiles
|
||||
/// </summary>
|
||||
public static List<RedumpSystemComboBoxItem> Systems => RedumpSystemComboBoxItem.GenerateElements().ToList();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for pure view model
|
||||
/// </summary>
|
||||
public OptionsViewModel()
|
||||
{
|
||||
Options = new Options();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for in-code
|
||||
/// </summary>
|
||||
public OptionsViewModel(Options baseOptions)
|
||||
{
|
||||
Options = new Options(baseOptions);
|
||||
}
|
||||
|
||||
#region Population
|
||||
|
||||
/// <summary>
|
||||
/// Get a complete list of supported internal programs
|
||||
/// </summary>
|
||||
private static List<Element<InternalProgram>> PopulateInternalPrograms()
|
||||
{
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.DiscImageCreator, InternalProgram.Aaru };
|
||||
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Commands
|
||||
|
||||
/// <summary>
|
||||
/// Test Redump login credentials
|
||||
/// </summary>
|
||||
#if NET40
|
||||
public static Task<(bool?, string?)> TestRedumpLogin(string username, string password)
|
||||
#else
|
||||
public static async Task<(bool?, string?)> TestRedumpLogin(string username, string password)
|
||||
#endif
|
||||
{
|
||||
#if NET40
|
||||
return Task.Factory.StartNew(() => RedumpWebClient.ValidateCredentials(username, password));
|
||||
#elif NETFRAMEWORK
|
||||
return await Task.Run(() => RedumpWebClient.ValidateCredentials(username, password));
|
||||
#else
|
||||
return await RedumpHttpClient.ValidateCredentials(username, password);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Property Updates
|
||||
|
||||
/// <summary>
|
||||
/// Trigger a property changed event
|
||||
/// </summary>
|
||||
private void TriggerPropertyChanged(string propertyName)
|
||||
{
|
||||
// If the property change event is initialized
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -8,35 +8,6 @@ namespace MPF.Core.Utilities
|
||||
{
|
||||
public static class EnumExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determine if a system is okay if it's not detected by Windows
|
||||
/// </summary>
|
||||
/// <param name="system">RedumpSystem value to check</param>
|
||||
/// <returns>True if Windows show see a disc when dumping, false otherwise</returns>
|
||||
public static bool DetectedByWindows(this RedumpSystem? system)
|
||||
{
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.AmericanLaserGames3DO:
|
||||
case RedumpSystem.AppleMacintosh:
|
||||
case RedumpSystem.Atari3DO:
|
||||
case RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem:
|
||||
case RedumpSystem.NewJatreCDi:
|
||||
case RedumpSystem.NintendoGameCube:
|
||||
case RedumpSystem.NintendoWii:
|
||||
case RedumpSystem.NintendoWiiU:
|
||||
case RedumpSystem.PhilipsCDi:
|
||||
case RedumpSystem.PhilipsCDiDigitalVideo:
|
||||
case RedumpSystem.Panasonic3DOInteractiveMultiplayer:
|
||||
case RedumpSystem.PanasonicM2:
|
||||
case RedumpSystem.PioneerLaserActive:
|
||||
case RedumpSystem.SuperAudioCD:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the media supports drive speeds
|
||||
/// </summary>
|
||||
@@ -44,88 +15,17 @@ namespace MPF.Core.Utilities
|
||||
/// <returns>True if the media has variable dumping speeds, false otherwise</returns>
|
||||
public static bool DoesSupportDriveSpeed(this MediaType? type)
|
||||
{
|
||||
switch (type)
|
||||
return type switch
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
case MediaType.DVD:
|
||||
case MediaType.GDROM:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a system has reversed ringcodes
|
||||
/// </summary>
|
||||
/// <param name="system">RedumpSystem value to check</param>
|
||||
/// <returns>True if the system has reversed ringcodes, false otherwise</returns>
|
||||
public static bool HasReversedRingcodes(this RedumpSystem? system)
|
||||
{
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
//case RedumpSystem.SonyPlayStation5:
|
||||
case RedumpSystem.SonyPlayStationPortable:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a system is considered audio-only
|
||||
/// </summary>
|
||||
/// <param name="system">RedumpSystem value to check</param>
|
||||
/// <returns>True if the system is audio-only, false otherwise</returns>
|
||||
/// <remarks>
|
||||
/// Philips CD-i should NOT be in this list. It's being included until there's a
|
||||
/// reasonable distinction between CD-i and CD-i ready on the database side.
|
||||
/// </remarks>
|
||||
public static bool IsAudio(this RedumpSystem? system)
|
||||
{
|
||||
switch (system)
|
||||
{
|
||||
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:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if a system is considered XGD
|
||||
/// </summary>
|
||||
/// <param name="system">RedumpSystem value to check</param>
|
||||
/// <returns>True if the system is XGD, false otherwise</returns>
|
||||
public static bool IsXGD(this RedumpSystem? system)
|
||||
{
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.MicrosoftXbox:
|
||||
case RedumpSystem.MicrosoftXbox360:
|
||||
case RedumpSystem.MicrosoftXboxOne:
|
||||
case RedumpSystem.MicrosoftXboxSeriesXS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
MediaType.CDROM
|
||||
or MediaType.DVD
|
||||
or MediaType.GDROM
|
||||
or MediaType.HDDVD
|
||||
or MediaType.BluRay
|
||||
or MediaType.NintendoGameCubeGameDisc
|
||||
or MediaType.NintendoWiiOpticalDisc => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -137,7 +37,7 @@ namespace MPF.Core.Utilities
|
||||
|
||||
foreach (var val in Enum.GetValues(typeof(InternalProgram)))
|
||||
{
|
||||
if (((InternalProgram)val) == InternalProgram.NONE)
|
||||
if (((InternalProgram)val!) == InternalProgram.NONE)
|
||||
continue;
|
||||
|
||||
programs.Add($"{((InternalProgram?)val).LongName()}");
|
||||
|
||||
@@ -14,7 +14,13 @@ namespace MPF.Core.Utilities
|
||||
/// <param name="reader">TextReader representing the input</param>
|
||||
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
|
||||
/// <param name="handler">Event handler to be invoked to write to log</param>
|
||||
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string> handler)
|
||||
#if NET20 || NET35
|
||||
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
|
||||
#elif NET40
|
||||
public static void OutputToLog(TextReader reader, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
|
||||
#else
|
||||
public static async Task OutputToLog(TextReader reader, object baseClass, EventHandler<string>? handler)
|
||||
#endif
|
||||
{
|
||||
// Initialize the required variables
|
||||
char[] buffer = new char[256];
|
||||
@@ -26,7 +32,15 @@ namespace MPF.Core.Utilities
|
||||
while (true)
|
||||
{
|
||||
// Try to read the next chunk of characters
|
||||
#if NET20 || NET35
|
||||
read = await Task.Run(() => reader.Read(buffer, 0, buffer.Length));
|
||||
#elif NET40
|
||||
var readTask = Task.Factory.StartNew(() => reader.Read(buffer, 0, buffer.Length));
|
||||
readTask.Wait();
|
||||
read = readTask.Result;
|
||||
#else
|
||||
read = await reader.ReadAsync(buffer, 0, buffer.Length);
|
||||
#endif
|
||||
if (read == 0)
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
@@ -34,25 +48,41 @@ namespace MPF.Core.Utilities
|
||||
}
|
||||
|
||||
// Convert the characters into a string
|
||||
string line = new string(buffer, 0, read);
|
||||
string line = new(buffer, 0, read);
|
||||
|
||||
// If we have no newline characters, store in the string builder
|
||||
#if NETFRAMEWORK
|
||||
if (!line.Contains("\r") && !line.Contains("\n"))
|
||||
#else
|
||||
if (!line.Contains('\r') && !line.Contains('\n'))
|
||||
#endif
|
||||
sb.Append(line);
|
||||
|
||||
// If we have a newline, append and log
|
||||
#if NETFRAMEWORK
|
||||
else if (line.Contains("\n") || line.Contains("\r\n"))
|
||||
#else
|
||||
else if (line.Contains('\n') || line.Contains("\r\n"))
|
||||
#endif
|
||||
ProcessNewLines(sb, line, baseClass, handler);
|
||||
|
||||
// If we have a carriage return only, append and log first and last instances
|
||||
#if NETFRAMEWORK
|
||||
else if (line.Contains("\r"))
|
||||
#else
|
||||
else if (line.Contains('\r'))
|
||||
#endif
|
||||
ProcessCarriageReturns(sb, line, baseClass, handler);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
|
||||
#else
|
||||
handler?.Invoke(baseClass, sb.ToString());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,14 +93,22 @@ namespace MPF.Core.Utilities
|
||||
/// <param name="line">Current line to process</param>
|
||||
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
|
||||
/// <param name="handler">Event handler to be invoked to write to log</param>
|
||||
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
|
||||
#if NET20 || NET35 || NET40
|
||||
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
|
||||
#else
|
||||
private static void ProcessNewLines(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
|
||||
#endif
|
||||
{
|
||||
line = line.Replace("\r\n", "\n");
|
||||
var split = line.Split('\n');
|
||||
for (int i = 0; i < split.Length; i++)
|
||||
{
|
||||
// If the chunk contains a carriage return, handle it like a separate line
|
||||
#if NETFRAMEWORK
|
||||
if (split[i].Contains("\r"))
|
||||
#else
|
||||
if (split[i].Contains('\r'))
|
||||
#endif
|
||||
{
|
||||
ProcessCarriageReturns(sb, split[i], baseClass, handler);
|
||||
continue;
|
||||
@@ -80,8 +118,13 @@ namespace MPF.Core.Utilities
|
||||
if (i == 0)
|
||||
{
|
||||
sb.Append(split[i]);
|
||||
#if NET20 || NET35 || NET40
|
||||
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
|
||||
sb = new();
|
||||
#else
|
||||
handler?.Invoke(baseClass, sb.ToString());
|
||||
sb.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
// For the last item, just append so it's dealt with the next time
|
||||
@@ -93,7 +136,11 @@ namespace MPF.Core.Utilities
|
||||
// For everything else, directly write out
|
||||
else
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = split[i] });
|
||||
#else
|
||||
handler?.Invoke(baseClass, split[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,16 +152,25 @@ namespace MPF.Core.Utilities
|
||||
/// <param name="line">Current line to process</param>
|
||||
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
|
||||
/// <param name="handler">Event handler to be invoked to write to log</param>
|
||||
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string> handler)
|
||||
#if NET20 || NET35 || NET40
|
||||
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<Modules.BaseParameters.StringEventArgs>? handler)
|
||||
#else
|
||||
private static void ProcessCarriageReturns(StringBuilder sb, string line, object baseClass, EventHandler<string>? handler)
|
||||
#endif
|
||||
{
|
||||
var split = line.Split('\r');
|
||||
|
||||
// Append and log the first
|
||||
sb.Append(split[0]);
|
||||
#if NET20 || NET35 || NET40
|
||||
handler?.Invoke(baseClass, new Modules.BaseParameters.StringEventArgs { Value = sb.ToString() });
|
||||
sb = new();
|
||||
#else
|
||||
handler?.Invoke(baseClass, sb.ToString());
|
||||
sb.Clear();
|
||||
#endif
|
||||
|
||||
// Append the last
|
||||
sb.Clear();
|
||||
sb.Append($"\r{split[split.Length - 1]}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Utilities
|
||||
{
|
||||
@@ -12,10 +15,78 @@ namespace MPF.Core.Utilities
|
||||
|
||||
#region Arguments
|
||||
|
||||
/// <summary>
|
||||
/// Process any standalone arguments for the program
|
||||
/// </summary>
|
||||
/// <returns>True if one of the arguments was processed, false otherwise</returns>
|
||||
public static bool? ProcessStandaloneArguments(string[] args)
|
||||
{
|
||||
// Help options
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "-?")
|
||||
return false;
|
||||
|
||||
// List options
|
||||
if (args[0] == "-lm" || args[0] == "--listmedia")
|
||||
{
|
||||
Console.WriteLine("Supported Media Types:");
|
||||
foreach (string mediaType in Extensions.ListMediaTypes())
|
||||
{
|
||||
Console.WriteLine(mediaType);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-lp" || args[0] == "--listprograms")
|
||||
{
|
||||
Console.WriteLine("Supported Programs:");
|
||||
foreach (string program in EnumExtensions.ListPrograms())
|
||||
{
|
||||
Console.WriteLine(program);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
else if (args[0] == "-ls" || args[0] == "--listsystems")
|
||||
{
|
||||
Console.WriteLine("Supported Systems:");
|
||||
foreach (string system in Extensions.ListSystems())
|
||||
{
|
||||
Console.WriteLine(system);
|
||||
}
|
||||
Console.ReadLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process common arguments for all functionality
|
||||
/// </summary>
|
||||
/// <returns>True if all arguments pass, false otherwise</returns>
|
||||
public static (bool, MediaType, RedumpSystem?, string?) ProcessCommonArguments(string[] args)
|
||||
{
|
||||
// All other use requires at least 3 arguments
|
||||
if (args.Length < 3)
|
||||
return (false, MediaType.NONE, null, "Invalid number of arguments");
|
||||
|
||||
// Check the MediaType
|
||||
var mediaType = EnumConverter.ToMediaType(args[0].Trim('"'));
|
||||
if (mediaType == MediaType.NONE)
|
||||
return (false, MediaType.NONE, null, $"{args[0]} is not a recognized media type");
|
||||
|
||||
// Check the RedumpSystem
|
||||
var knownSystem = Extensions.ToRedumpSystem(args[1].Trim('"'));
|
||||
if (knownSystem == null)
|
||||
return (false, MediaType.NONE, null, $"{args[1]} is not a recognized system");
|
||||
|
||||
return (true, mediaType, knownSystem, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from application arguments
|
||||
/// </summary>
|
||||
public static (Options, string, int) LoadFromArguments(string[] args, int startIndex = 0)
|
||||
public static (Options, SubmissionInfo?, string?, int) LoadFromArguments(string[] args, int startIndex = 0)
|
||||
{
|
||||
// Create the output values with defaults
|
||||
var options = new Options()
|
||||
@@ -23,28 +94,45 @@ namespace MPF.Core.Utilities
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
AddFilenameSuffix = false,
|
||||
OutputSubmissionJSON = false,
|
||||
CompressLogFiles = false,
|
||||
DeleteUnnecessaryFiles = false,
|
||||
};
|
||||
|
||||
string parsedPath = null;
|
||||
// Create the submission info to return, if necessary
|
||||
SubmissionInfo? info = null;
|
||||
string? parsedPath = null;
|
||||
|
||||
// These values require multiple parts to be active
|
||||
bool scan = false, protectFile = false;
|
||||
bool scan = false, protectFile = false, hideDriveLetters = false;
|
||||
|
||||
// If we have no arguments, just return
|
||||
if (args == null || args.Length == 0)
|
||||
return (options, null, 0);
|
||||
return (options, null, null, 0);
|
||||
|
||||
// If we have an invalid start index, just return
|
||||
if (startIndex < 0 || startIndex >= args.Length)
|
||||
return (options, null, startIndex);
|
||||
return (options, null, null, startIndex);
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
{
|
||||
// Use specific program
|
||||
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
string internalProgram = args[startIndex].Split('=')[1];
|
||||
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
string internalProgram = args[startIndex + 1];
|
||||
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Redump login
|
||||
if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
{
|
||||
string[] credentials = args[startIndex].Split('=')[1].Split(';');
|
||||
options.RedumpUsername = credentials[0];
|
||||
@@ -57,17 +145,10 @@ namespace MPF.Core.Utilities
|
||||
startIndex += 2;
|
||||
}
|
||||
|
||||
// Use specific program
|
||||
else if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
// Pull all information (requires Redump login)
|
||||
else if (args[startIndex].Equals("-a") || args[startIndex].Equals("--pull-all"))
|
||||
{
|
||||
string internalProgram = args[startIndex].Split('=')[1];
|
||||
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
string internalProgram = args[startIndex + 1];
|
||||
options.InternalProgram = EnumConverter.ToInternalProgram(internalProgram);
|
||||
startIndex++;
|
||||
options.PullAllInformation = true;
|
||||
}
|
||||
|
||||
// Use a device path for physical checks
|
||||
@@ -93,6 +174,31 @@ namespace MPF.Core.Utilities
|
||||
protectFile = true;
|
||||
}
|
||||
|
||||
// Hide drive letters from scan output (requires --protect-file)
|
||||
else if (args[startIndex].Equals("-g") || args[startIndex].Equals("--hide-drive-letters"))
|
||||
{
|
||||
hideDriveLetters = true;
|
||||
}
|
||||
|
||||
// Include seed info file
|
||||
else if (args[startIndex].StartsWith("-l=") || args[startIndex].StartsWith("--load-seed="))
|
||||
{
|
||||
string seedInfo = args[startIndex].Split('=')[1];
|
||||
info = Builder.CreateFromFile(seedInfo);
|
||||
}
|
||||
else if (args[startIndex] == "-l" || args[startIndex] == "--load-seed")
|
||||
{
|
||||
string seedInfo = args[startIndex + 1];
|
||||
info = Builder.CreateFromFile(seedInfo);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Add filename suffix
|
||||
else if (args[startIndex].Equals("-x") || args[startIndex].Equals("--suffix"))
|
||||
{
|
||||
options.AddFilenameSuffix = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
|
||||
{
|
||||
@@ -105,6 +211,12 @@ namespace MPF.Core.Utilities
|
||||
options.CompressLogFiles = true;
|
||||
}
|
||||
|
||||
// Delete unnecessary files files
|
||||
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
|
||||
{
|
||||
options.DeleteUnnecessaryFiles = true;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
@@ -113,10 +225,11 @@ namespace MPF.Core.Utilities
|
||||
}
|
||||
|
||||
// Now deal with the complex options
|
||||
options.ScanForProtection = scan && !string.IsNullOrWhiteSpace(parsedPath);
|
||||
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrWhiteSpace(parsedPath);
|
||||
options.ScanForProtection = scan && !string.IsNullOrEmpty(parsedPath);
|
||||
options.OutputSeparateProtectionFile = scan && protectFile && !string.IsNullOrEmpty(parsedPath);
|
||||
options.HideDriveLetters = hideDriveLetters && scan && protectFile && !string.IsNullOrEmpty(parsedPath);
|
||||
|
||||
return (options, parsedPath, startIndex);
|
||||
return (options, info, parsedPath, startIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -124,15 +237,21 @@ namespace MPF.Core.Utilities
|
||||
/// </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");
|
||||
var supportedArguments = new List<string>
|
||||
{
|
||||
"-u, --use <program> Dumping program output type [REQUIRED]",
|
||||
"-c, --credentials <user> <pw> Redump username and password",
|
||||
"-a, --pull-all Pull all information from Redump (requires --credentials)",
|
||||
"-p, --path <drivepath> Physical drive path for additional checks",
|
||||
"-s, --scan Enable copy protection scan (requires --path)",
|
||||
"-f, --protect-file Output protection to separate file (requires --scan)",
|
||||
"-g, --hide-drive-letters Hide drive letters from scan output (requires --protect-file)",
|
||||
"-l, --load-seed <path> Load a seed submission JSON for user information",
|
||||
"-x, --suffix Enable adding filename suffix",
|
||||
"-j, --json Enable submission JSON output",
|
||||
"-z, --zip Enable log file compression",
|
||||
"-d, --delete Enable unnecessary file deletion",
|
||||
};
|
||||
|
||||
return supportedArguments;
|
||||
}
|
||||
@@ -153,8 +272,9 @@ namespace MPF.Core.Utilities
|
||||
}
|
||||
|
||||
var serializer = JsonSerializer.Create();
|
||||
var reader = new StreamReader(ConfigurationPath);
|
||||
var settings = serializer.Deserialize(reader, typeof(Dictionary<string, string>)) as Dictionary<string, string>;
|
||||
var stream = File.Open(ConfigurationPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
var reader = new StreamReader(stream);
|
||||
var settings = serializer.Deserialize(reader, typeof(Dictionary<string, string?>)) as Dictionary<string, string?>;
|
||||
return new Options(settings);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using MPF.Core.Data;
|
||||
using Newtonsoft.Json.Linq;
|
||||
@@ -70,6 +72,36 @@ namespace MPF.Core.Utilities
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a hex string into a byte array
|
||||
/// </summary>
|
||||
/// <param name="hex">Hex string</param>
|
||||
/// <returns>Converted byte array, or null if invalid hex string</returns>
|
||||
public static byte[]? HexStringToByteArray(string? hexString)
|
||||
{
|
||||
// Valid hex string must be an even number of characters
|
||||
if (string.IsNullOrEmpty(hexString) || hexString!.Length % 2 == 1)
|
||||
return null;
|
||||
|
||||
// Convert ASCII to byte via lookup table
|
||||
int[] hexLookup = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F];
|
||||
byte[] byteArray = new byte[hexString.Length / 2];
|
||||
for (int i = 0; i < hexString.Length; i += 2)
|
||||
{
|
||||
// Convert next two chars to ASCII value relative to '0'
|
||||
int a = Char.ToUpper(hexString[i]) - '0';
|
||||
int b = Char.ToUpper(hexString[i + 1]) - '0';
|
||||
|
||||
// Ensure hex string only has '0' through '9' and 'A' through 'F' (case insensitive)
|
||||
if ((a < 0 || b < 0 || a > 22 || b > 22) || (a > 10 && a < 17) || (b > 10 && b < 17))
|
||||
return null;
|
||||
byteArray[i / 2] = (byte)(hexLookup[a] << 4 | hexLookup[b]);
|
||||
}
|
||||
|
||||
return byteArray;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Support
|
||||
@@ -84,38 +116,84 @@ namespace MPF.Core.Utilities
|
||||
return Result.Failure("Please select a valid system");
|
||||
|
||||
// If we're on an unsupported type, update the status accordingly
|
||||
switch (type)
|
||||
return type switch
|
||||
{
|
||||
// Fully supported types
|
||||
case MediaType.BluRay:
|
||||
case MediaType.CDROM:
|
||||
case MediaType.DVD:
|
||||
case MediaType.FloppyDisk:
|
||||
case MediaType.HardDisk:
|
||||
case MediaType.CompactFlash:
|
||||
case MediaType.SDCard:
|
||||
case MediaType.FlashDrive:
|
||||
case MediaType.HDDVD:
|
||||
return Result.Success($"{type.LongName()} ready to dump");
|
||||
MediaType.BluRay
|
||||
or MediaType.CDROM
|
||||
or MediaType.DVD
|
||||
or MediaType.FloppyDisk
|
||||
or MediaType.HardDisk
|
||||
or MediaType.CompactFlash
|
||||
or MediaType.SDCard
|
||||
or MediaType.FlashDrive
|
||||
or MediaType.HDDVD => Result.Success($"{type.LongName()} ready to dump"),
|
||||
|
||||
// Partially supported types
|
||||
case MediaType.GDROM:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return Result.Success($"{type.LongName()} partially supported for dumping");
|
||||
MediaType.GDROM
|
||||
or MediaType.NintendoGameCubeGameDisc
|
||||
or MediaType.NintendoWiiOpticalDisc => Result.Success($"{type.LongName()} partially supported for dumping"),
|
||||
|
||||
// Special case for other supported tools
|
||||
case MediaType.UMD:
|
||||
return Result.Failure($"{type.LongName()} supported for submission info parsing");
|
||||
MediaType.UMD => Result.Failure($"{type.LongName()} supported for submission info parsing"),
|
||||
|
||||
// Specifically unknown type
|
||||
case MediaType.NONE:
|
||||
return Result.Failure($"Please select a valid media type");
|
||||
MediaType.NONE => Result.Failure($"Please select a valid media type"),
|
||||
|
||||
// Undumpable but recognized types
|
||||
default:
|
||||
return Result.Failure($"{type.LongName()} media are not supported for dumping");
|
||||
}
|
||||
_ => Result.Failure($"{type.LongName()} media are not supported for dumping"),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns false if a given InternalProgram does not support a given MediaType
|
||||
/// </summary>
|
||||
public static bool ProgramSupportsMedia(InternalProgram program, MediaType? type)
|
||||
{
|
||||
// If the media type is not set, return false
|
||||
if (type == null || type == MediaType.NONE)
|
||||
return false;
|
||||
|
||||
return (program) switch
|
||||
{
|
||||
// Aaru
|
||||
InternalProgram.Aaru when type == MediaType.BluRay => true,
|
||||
InternalProgram.Aaru when type == MediaType.CDROM => true,
|
||||
InternalProgram.Aaru when type == MediaType.CompactFlash => true,
|
||||
InternalProgram.Aaru when type == MediaType.DVD => true,
|
||||
InternalProgram.Aaru when type == MediaType.GDROM => true,
|
||||
InternalProgram.Aaru when type == MediaType.FlashDrive => true,
|
||||
InternalProgram.Aaru when type == MediaType.FloppyDisk => true,
|
||||
InternalProgram.Aaru when type == MediaType.HardDisk => true,
|
||||
InternalProgram.Aaru when type == MediaType.HDDVD => true,
|
||||
InternalProgram.Aaru when type == MediaType.NintendoGameCubeGameDisc => true,
|
||||
InternalProgram.Aaru when type == MediaType.NintendoWiiOpticalDisc => true,
|
||||
InternalProgram.Aaru when type == MediaType.SDCard => true,
|
||||
|
||||
// DiscImageCreator
|
||||
InternalProgram.DiscImageCreator when type == MediaType.BluRay => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.CDROM => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.CompactFlash => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.DVD => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.GDROM => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.FlashDrive => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.FloppyDisk => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.HardDisk => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.HDDVD => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.NintendoGameCubeGameDisc => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.NintendoWiiOpticalDisc => true,
|
||||
InternalProgram.DiscImageCreator when type == MediaType.SDCard => true,
|
||||
|
||||
// Redumper
|
||||
InternalProgram.Redumper when type == MediaType.BluRay => true,
|
||||
InternalProgram.Redumper when type == MediaType.CDROM => true,
|
||||
InternalProgram.Redumper when type == MediaType.DVD => true,
|
||||
InternalProgram.Redumper when type == MediaType.GDROM => true,
|
||||
InternalProgram.Redumper when type == MediaType.HDDVD => true,
|
||||
|
||||
// Default
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -130,17 +208,20 @@ namespace MPF.Core.Utilities
|
||||
/// String representing the message to display the the user.
|
||||
/// String representing the new release URL.
|
||||
/// </returns>
|
||||
public static (bool different, string message, string url) CheckForNewVersion()
|
||||
public static (bool different, string message, string? url) CheckForNewVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Get current assembly version
|
||||
var assemblyVersion = Assembly.GetEntryAssembly().GetName().Version;
|
||||
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}" + (assemblyVersion.Build != 0 ? $".{assemblyVersion.Build}" : string.Empty);
|
||||
var assemblyVersion = Assembly.GetEntryAssembly()?.GetName()?.Version;
|
||||
if (assemblyVersion == null)
|
||||
return (false, "Assembly version could not be determined", null);
|
||||
|
||||
string version = $"{assemblyVersion.Major}.{assemblyVersion.Minor}.{assemblyVersion.Build}";
|
||||
|
||||
// Get the latest tag from GitHub
|
||||
(string tag, string url) = GetRemoteVersionAndUrl();
|
||||
bool different = version != tag;
|
||||
var (tag, url) = GetRemoteVersionAndUrl();
|
||||
bool different = version != tag && tag != null;
|
||||
|
||||
string message = $"Local version: {version}"
|
||||
+ $"{Environment.NewLine}Remote version: {tag}"
|
||||
@@ -159,12 +240,16 @@ namespace MPF.Core.Utilities
|
||||
/// <summary>
|
||||
/// Get the current informational version formatted as a string
|
||||
/// </summary>
|
||||
public static string GetCurrentVersion()
|
||||
public static string? GetCurrentVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
var assemblyVersion = Attribute.GetCustomAttribute(Assembly.GetEntryAssembly(), typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
|
||||
return assemblyVersion.InformationalVersion;
|
||||
var assembly = Assembly.GetEntryAssembly();
|
||||
if (assembly == null)
|
||||
return null;
|
||||
|
||||
var assemblyVersion = Attribute.GetCustomAttribute(assembly, typeof(AssemblyInformationalVersionAttribute)) as AssemblyInformationalVersionAttribute;
|
||||
return assemblyVersion?.InformationalVersion;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -175,37 +260,385 @@ namespace MPF.Core.Utilities
|
||||
/// <summary>
|
||||
/// Get the latest version of MPF from GitHub and the release URL
|
||||
/// </summary>
|
||||
private static (string tag, string url) GetRemoteVersionAndUrl()
|
||||
private static (string? tag, string? url) GetRemoteVersionAndUrl()
|
||||
{
|
||||
#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";
|
||||
|
||||
// TODO: Figure out a better way than having this hardcoded...
|
||||
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
|
||||
string latestReleaseJsonString = wc.DownloadString(url);
|
||||
var latestReleaseJson = JObject.Parse(latestReleaseJsonString);
|
||||
string latestTag = latestReleaseJson["tag_name"].ToString();
|
||||
string releaseUrl = latestReleaseJson["html_url"].ToString();
|
||||
|
||||
return (latestTag, releaseUrl);
|
||||
}
|
||||
#if NET20 || NET35 || NET40
|
||||
// Not supported in .NET Frameworks 2.0, 3.5, or 4.0
|
||||
return (null, null);
|
||||
#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);
|
||||
}
|
||||
using var hc = new System.Net.Http.HttpClient();
|
||||
#if NET452
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
|
||||
#endif
|
||||
|
||||
// TODO: Figure out a better way than having this hardcoded...
|
||||
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
|
||||
var message = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
|
||||
message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
|
||||
var latestReleaseJsonString = hc.SendAsync(message)?.ConfigureAwait(false).GetAwaiter().GetResult()
|
||||
.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
if (latestReleaseJsonString == null)
|
||||
return (null, null);
|
||||
|
||||
var latestReleaseJson = JObject.Parse(latestReleaseJsonString);
|
||||
if (latestReleaseJson == null)
|
||||
return (null, null);
|
||||
|
||||
var latestTag = latestReleaseJson["tag_name"]?.ToString();
|
||||
var releaseUrl = latestReleaseJson["html_url"]?.ToString();
|
||||
|
||||
return (latestTag, releaseUrl);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PlayStation 3 specific tools
|
||||
|
||||
/// <summary>
|
||||
/// Validates a getkey log to check for presence of valid PS3 key
|
||||
/// </summary>
|
||||
/// <param name="logPath">Path to getkey log file</param>
|
||||
/// <param name="key">Output key string, null if not valid</param>
|
||||
/// <param name="id">Output disc ID string, null if not valid</param>
|
||||
/// <param name="pic">Output PIC string, null if not valid</param>
|
||||
/// <returns>True if path to log file contains valid key, false otherwise</returns>
|
||||
public static bool ParseGetKeyLog(string? logPath, out string? key, out string? id, out string? pic)
|
||||
{
|
||||
key = id = pic = null;
|
||||
|
||||
if (string.IsNullOrEmpty(logPath))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(logPath))
|
||||
return false;
|
||||
|
||||
// Protect from attempting to read from really long files
|
||||
FileInfo logFile = new(logPath);
|
||||
if (logFile.Length > 65536)
|
||||
return false;
|
||||
|
||||
// Read from .getkey.log file
|
||||
using StreamReader sr = File.OpenText(logPath);
|
||||
|
||||
// Determine whether GetKey was successful
|
||||
string? line;
|
||||
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("get_dec_key succeeded!") == false) ;
|
||||
if (line == null)
|
||||
return false;
|
||||
|
||||
// Look for Disc Key in log
|
||||
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("disc_key = ") == false) ;
|
||||
|
||||
// If end of file reached, no key found
|
||||
if (line == null)
|
||||
return false;
|
||||
|
||||
// Get Disc Key from log
|
||||
string discKeyStr = line.Substring("disc_key = ".Length);
|
||||
|
||||
// Validate Disc Key from log
|
||||
if (discKeyStr.Length != 32)
|
||||
return false;
|
||||
|
||||
// Convert Disc Key to byte array
|
||||
key = discKeyStr;
|
||||
if (key == null)
|
||||
return false;
|
||||
|
||||
// Read Disc ID
|
||||
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("disc_id = ") == false) ;
|
||||
|
||||
// If end of file reached, no ID found
|
||||
if (line == null)
|
||||
return false;
|
||||
|
||||
// Get Disc ID from log
|
||||
string discIDStr = line.Substring("disc_id = ".Length);
|
||||
|
||||
// Validate Disc ID from log
|
||||
if (discIDStr.Length != 32)
|
||||
return false;
|
||||
|
||||
// Replace X's in Disc ID with 00000001
|
||||
discIDStr = discIDStr.Substring(0, 24) + "00000001";
|
||||
|
||||
// Convert Disc ID to byte array
|
||||
id = discIDStr;
|
||||
if (id == null)
|
||||
return false;
|
||||
|
||||
// Look for PIC in log
|
||||
while ((line = sr.ReadLine()) != null && line.Trim().StartsWith("PIC:") == false) ;
|
||||
|
||||
// If end of file reached, no PIC found
|
||||
if (line == null)
|
||||
return false;
|
||||
|
||||
// Get PIC from log
|
||||
string discPICStr = "";
|
||||
for (int i = 0; i < 9; i++)
|
||||
discPICStr += sr.ReadLine();
|
||||
if (discPICStr == null)
|
||||
return false;
|
||||
|
||||
// Validate PIC from log
|
||||
if (discPICStr.Length != 264)
|
||||
return false;
|
||||
|
||||
// Convert PIC to byte array
|
||||
pic = discPICStr;
|
||||
if (pic == null)
|
||||
return false;
|
||||
|
||||
// Double check for warnings in .getkey.log
|
||||
while ((line = sr.ReadLine()) != null)
|
||||
{
|
||||
string t = line.Trim();
|
||||
if (t.StartsWith("WARNING"))
|
||||
return false;
|
||||
else if (t.StartsWith("SUCCESS"))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We are not concerned with the error
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a getkey log to check for presence of valid PS3 key
|
||||
/// </summary>
|
||||
/// <param name="logPath">Path to getkey log file</param>
|
||||
/// <param name="key">Output 16 byte disc key, null if not valid</param>
|
||||
/// <param name="id">Output 16 byte disc ID, null if not valid</param>
|
||||
/// <param name="pic">Output 230 byte PIC, null if not valid</param>
|
||||
/// <returns>True if path to log file contains valid key, false otherwise</returns>
|
||||
public static bool ParseGetKeyLog(string? logPath, out byte[]? key, out byte[]? id, out byte[]? pic)
|
||||
{
|
||||
key = id = pic = null;
|
||||
if (ParseGetKeyLog(logPath, out string? keyString, out string? idString, out string? picString))
|
||||
{
|
||||
if (string.IsNullOrEmpty(keyString) || string.IsNullOrEmpty(idString) || string.IsNullOrEmpty(picString) || picString!.Length < 230)
|
||||
return false;
|
||||
|
||||
key = Tools.HexStringToByteArray(keyString);
|
||||
id = Tools.HexStringToByteArray(idString);
|
||||
pic = Tools.HexStringToByteArray(picString.Substring(0, 230));
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a hexadecimal disc ID
|
||||
/// </summary>
|
||||
/// <param name="discID">String representing hexadecimal disc ID</param>
|
||||
/// <returns>True if string is a valid disc ID, false otherwise</returns>
|
||||
public static byte[]? ParseDiscID(string? discID)
|
||||
{
|
||||
if (string.IsNullOrEmpty(discID))
|
||||
return null;
|
||||
|
||||
string cleandiscID = discID!.Trim().Replace("\n", string.Empty);
|
||||
|
||||
if (discID!.Length != 32)
|
||||
return null;
|
||||
|
||||
// Censor last 4 bytes by replacing with 0x00000001
|
||||
cleandiscID = cleandiscID.Substring(0, 24) + "00000001";
|
||||
|
||||
// Convert to byte array, null if invalid hex string
|
||||
byte[]? id = Tools.HexStringToByteArray(cleandiscID);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a key file to check for presence of valid PS3 key
|
||||
/// </summary>
|
||||
/// <param name="keyPath">Path to key file</param>
|
||||
/// <returns>Output 16 byte key, null if not valid</returns>
|
||||
public static byte[]? ParseKeyFile(string? keyPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(keyPath))
|
||||
return null;
|
||||
|
||||
// Try read from key file
|
||||
try
|
||||
{
|
||||
if (!File.Exists(keyPath))
|
||||
return null;
|
||||
|
||||
// Key file must be exactly 16 bytes long
|
||||
FileInfo keyFile = new(keyPath);
|
||||
if (keyFile.Length != 16)
|
||||
return null;
|
||||
byte[] key = new byte[16];
|
||||
|
||||
// Read 16 bytes from Key file
|
||||
using FileStream fs = new(keyPath, FileMode.Open, FileAccess.Read);
|
||||
using BinaryReader reader = new(fs);
|
||||
int numBytes = reader.Read(key, 0, 16);
|
||||
if (numBytes != 16)
|
||||
return null;
|
||||
|
||||
return key;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Not concerned with error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a hexadecimal key
|
||||
/// </summary>
|
||||
/// <param name="hexKey">String representing hexadecimal key</param>
|
||||
/// <returns>Output 16 byte key, null if not valid</returns>
|
||||
public static byte[]? ParseHexKey(string? hexKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hexKey))
|
||||
return null;
|
||||
|
||||
string cleanHexKey = hexKey!.Trim().Replace("\n", string.Empty);
|
||||
|
||||
if (cleanHexKey.Length != 32)
|
||||
return null;
|
||||
|
||||
// Convert to byte array, null if invalid hex string
|
||||
byte[]? key = Tools.HexStringToByteArray(cleanHexKey);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a PIC file path
|
||||
/// </summary>
|
||||
/// <param name="picPath">Path to PIC file</param>
|
||||
/// <returns>Output PIC byte array, null if not valid</returns>
|
||||
public static byte[]? ParsePICFile(string? picPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(picPath))
|
||||
return null;
|
||||
|
||||
// Try read from PIC file
|
||||
try
|
||||
{
|
||||
if (!File.Exists(picPath))
|
||||
return null;
|
||||
|
||||
// PIC file must be at least 115 bytes long
|
||||
FileInfo picFile = new(picPath);
|
||||
if (picFile.Length < 115)
|
||||
return null;
|
||||
byte[] pic = new byte[115];
|
||||
|
||||
// Read 115 bytes from PIC file
|
||||
using FileStream fs = new(picPath, FileMode.Open, FileAccess.Read);
|
||||
using BinaryReader reader = new(fs);
|
||||
int numBytes = reader.Read(pic, 0, 115);
|
||||
if (numBytes != 115)
|
||||
return null;
|
||||
|
||||
// Validate that a PIC was read by checking first 6 bytes
|
||||
if (pic[0] != 0x10 ||
|
||||
pic[1] != 0x02 ||
|
||||
pic[2] != 0x00 ||
|
||||
pic[3] != 0x00 ||
|
||||
pic[4] != 0x44 ||
|
||||
pic[5] != 0x49)
|
||||
return null;
|
||||
|
||||
return pic;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Not concerned with error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a PIC
|
||||
/// </summary>
|
||||
/// <param name="inputPIC">String representing PIC</param>
|
||||
/// <returns>Output PIC byte array, null if not valid</returns>
|
||||
public static byte[]? ParsePIC(string? inputPIC)
|
||||
{
|
||||
if (string.IsNullOrEmpty(inputPIC))
|
||||
return null;
|
||||
|
||||
string cleanPIC = inputPIC!.Trim().Replace("\n", string.Empty);
|
||||
|
||||
if (cleanPIC.Length < 230)
|
||||
return null;
|
||||
|
||||
// Convert to byte array, null if invalid hex string
|
||||
byte[]? pic = Tools.HexStringToByteArray(cleanPIC.Substring(0, 230));
|
||||
|
||||
return pic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a string representing a layerbreak value (in sectors)
|
||||
/// </summary>
|
||||
/// <param name="inputLayerbreak">String representing layerbreak value</param>
|
||||
/// <param name="layerbreak">Output layerbreak value, null if not valid</param>
|
||||
/// <returns>True if layerbreak is valid, false otherwise</returns>
|
||||
public static long? ParseLayerbreak(string? inputLayerbreak)
|
||||
{
|
||||
if (string.IsNullOrEmpty(inputLayerbreak))
|
||||
return null;
|
||||
|
||||
if (!long.TryParse(inputLayerbreak, out long layerbreak))
|
||||
return null;
|
||||
|
||||
return ParseLayerbreak(layerbreak);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates a layerbreak value (in sectors)
|
||||
/// </summary>
|
||||
/// <param name="inputLayerbreak">Number representing layerbreak value</param>
|
||||
/// <param name="layerbreak">Output layerbreak value, null if not valid</param>
|
||||
/// <returns>True if layerbreak is valid, false otherwise</returns>
|
||||
public static long? ParseLayerbreak(long? layerbreak)
|
||||
{
|
||||
// Check that layerbreak is positive number and smaller than largest disc size (in sectors)
|
||||
if (layerbreak <= 0 || layerbreak > 24438784)
|
||||
return null;
|
||||
|
||||
return layerbreak;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a CRC32 hash hex string into uint32 representation
|
||||
/// </summary>
|
||||
/// <param name="inputLayerbreak">Hex string representing CRC32 hash</param>
|
||||
/// <param name="layerbreak">Output CRC32 value, null if not valid</param>
|
||||
/// <returns>True if CRC32 hash string is valid, false otherwise</returns>
|
||||
public static uint? ParseCRC32(string? inputCRC32)
|
||||
{
|
||||
if (string.IsNullOrEmpty(inputCRC32))
|
||||
return null;
|
||||
|
||||
byte[]? crc32 = Tools.HexStringToByteArray(inputCRC32);
|
||||
|
||||
if (crc32 == null || crc32.Length != 4)
|
||||
return null;
|
||||
|
||||
return (uint)(0x01000000 * crc32[0] + 0x00010000 * crc32[1] + 0x00000100 * crc32[2] + 0x00000001 * crc32[3]);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Anything marked as internal can be used by the test methods
|
||||
[assembly: InternalsVisibleTo("MPF.Test")]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,43 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<Version>2.6.6</Version>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
|
||||
</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="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.1.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="System.IO.Compression" Version="4.3.0" />
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBurnOutSharp)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,4 +0,0 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
// Anything marked as internal can be used by the test methods
|
||||
[assembly: InternalsVisibleTo("MPF.Test")]
|
||||
@@ -1,377 +0,0 @@
|
||||
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 SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.CleanRip
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a generic set of CleanRip parameters
|
||||
/// </summary>
|
||||
public class Parameters : BaseParameters
|
||||
{
|
||||
#region Metadata
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override InternalProgram InternalProgram => InternalProgram.CleanRip;
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(string parameters) : base(parameters) { }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Parameters(RedumpSystem? system, MediaType? type, 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)
|
||||
{
|
||||
List<string> missingFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.DVD: // Only added here to help users; not strictly correct
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}-dumpinfo.txt"))
|
||||
missingFiles.Add($"{basePath}-dumpinfo.txt");
|
||||
if (!File.Exists($"{basePath}.bca"))
|
||||
missingFiles.Add($"{basePath}.bca");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
missingFiles.Add("Media and system combination not supported for CleanRip");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive drive, bool includeArtifacts)
|
||||
{
|
||||
// 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(datafile, out long size, out string crc32, out string md5, out string sha1))
|
||||
{
|
||||
info.SizeAndChecksums.Size = size;
|
||||
info.SizeAndChecksums.CRC32 = crc32;
|
||||
info.SizeAndChecksums.MD5 = md5;
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
|
||||
// Dual-layer discs have the same size and layerbreak
|
||||
if (size == 8511160320)
|
||||
info.SizeAndChecksums.Layerbreak = 2084960;
|
||||
}
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.DVD: // Only added here to help users; not strictly correct
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
info.Extras.BCA = GetBCA(basePath + ".bca");
|
||||
|
||||
if (GetGameCubeWiiInformation(basePath + "-dumpinfo.txt", out Region? gcRegion, out 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;
|
||||
}
|
||||
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true));
|
||||
if (File.Exists(basePath + "-dumpinfo.txt"))
|
||||
info.Artifacts["dumpinfo"] = GetBase64(GetFullFile(basePath + "-dumpinfo.txt"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
{
|
||||
List<string> logFiles = new List<string>();
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.DVD: // Only added here to help users; not strictly correct
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (File.Exists($"{basePath}-dumpinfo.txt"))
|
||||
logFiles.Add($"{basePath}-dumpinfo.txt");
|
||||
if (File.Exists($"{basePath}.bca"))
|
||||
logFiles.Add($"{basePath}.bca");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return logFiles;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Information Extraction Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted datfile from the cleanrip output, if possible
|
||||
/// </summary>
|
||||
/// <param name="iso">Path to ISO file</param>
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <returns></returns>
|
||||
private static Datafile GenerateCleanripDatafile(string iso, string dumpinfo)
|
||||
{
|
||||
// If the 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>
|
||||
/// <param name="bcaPath">Path to the BCA file associated with the dump</param>
|
||||
/// <returns>BCA data as a hex string if possible, null on error</returns>
|
||||
/// <remarks>https://stackoverflow.com/questions/9932096/add-separator-to-string-at-every-n-characters</remarks>
|
||||
private static string GetBCA(string bcaPath)
|
||||
{
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(bcaPath))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
string hex = GetFullFile(bcaPath, true);
|
||||
return Regex.Replace(hex, ".{32}", "$0\n");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a formatted datfile from the cleanrip output, if possible
|
||||
/// </summary>
|
||||
/// <param name="iso">Path to ISO file</param>
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <returns></returns>
|
||||
private static string GetCleanripDatfile(string iso, string dumpinfo)
|
||||
{
|
||||
// If the 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 $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the extracted GC and Wii version
|
||||
/// </summary>
|
||||
/// <param name="dumpinfo">Path to discinfo file</param>
|
||||
/// <param name="region">Output region, if possible</param>
|
||||
/// <param name="version">Output internal version of the game</param>
|
||||
/// <param name="name">Output internal name of the game</param>
|
||||
/// <returns></returns>
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string version, out string name)
|
||||
{
|
||||
region = null; version = null; name = null;
|
||||
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(dumpinfo))
|
||||
return false;
|
||||
|
||||
using (StreamReader sr = File.OpenText(dumpinfo))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Make sure this file is a dumpinfo
|
||||
if (!sr.ReadLine().Contains("--File Generated by CleanRip"))
|
||||
return false;
|
||||
|
||||
// Read all lines and gather dat information
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string line = sr.ReadLine().Trim();
|
||||
if (line.StartsWith("Version"))
|
||||
{
|
||||
version = line.Substring("Version: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("Internal Name"))
|
||||
{
|
||||
name = line.Substring("Internal Name: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("Filename"))
|
||||
{
|
||||
string serial = line.Substring("Filename: ".Length);
|
||||
|
||||
// char gameType = serial[0];
|
||||
// string gameid = serial[1] + serial[2];
|
||||
// string version = serial[4] + serial[5]
|
||||
|
||||
switch (serial[3])
|
||||
{
|
||||
case 'A':
|
||||
region = Region.World;
|
||||
break;
|
||||
case 'D':
|
||||
region = Region.Germany;
|
||||
break;
|
||||
case 'E':
|
||||
region = Region.UnitedStatesOfAmerica;
|
||||
break;
|
||||
case 'F':
|
||||
region = Region.France;
|
||||
break;
|
||||
case 'I':
|
||||
region = Region.Italy;
|
||||
break;
|
||||
case 'J':
|
||||
region = Region.Japan;
|
||||
break;
|
||||
case 'K':
|
||||
region = Region.SouthKorea;
|
||||
break;
|
||||
case 'L':
|
||||
region = Region.Europe; // Japanese import to Europe
|
||||
break;
|
||||
case 'M':
|
||||
region = Region.Europe; // American import to Europe
|
||||
break;
|
||||
case 'N':
|
||||
region = Region.UnitedStatesOfAmerica; // Japanese import to USA
|
||||
break;
|
||||
case 'P':
|
||||
region = Region.Europe;
|
||||
break;
|
||||
case 'R':
|
||||
region = Region.RussianFederation;
|
||||
break;
|
||||
case 'S':
|
||||
region = Region.Spain;
|
||||
break;
|
||||
case 'Q':
|
||||
region = Region.SouthKorea; // Korea with Japanese language
|
||||
break;
|
||||
case 'T':
|
||||
region = Region.SouthKorea; // Korea with English language
|
||||
break;
|
||||
case 'X':
|
||||
region = null; // Not a real region code
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.DiscImageCreator
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
#region Cross-enumeration conversions
|
||||
|
||||
/// <summary>
|
||||
/// Get the most common known system for a given MediaType
|
||||
/// </summary>
|
||||
/// <param name="baseCommand">Command value to check</param>
|
||||
/// <returns>RedumpSystem if possible, null on error</returns>
|
||||
public static RedumpSystem? ToRedumpSystem(string baseCommand)
|
||||
{
|
||||
switch (baseCommand)
|
||||
{
|
||||
case CommandStrings.Audio:
|
||||
return RedumpSystem.AudioCD;
|
||||
case CommandStrings.CompactDisc:
|
||||
case CommandStrings.Data:
|
||||
case CommandStrings.DigitalVideoDisc:
|
||||
case CommandStrings.Disk:
|
||||
case CommandStrings.Floppy:
|
||||
case CommandStrings.Tape:
|
||||
return RedumpSystem.IBMPCcompatible;
|
||||
case CommandStrings.GDROM:
|
||||
case CommandStrings.Swap:
|
||||
return RedumpSystem.SegaDreamcast;
|
||||
case CommandStrings.BluRay:
|
||||
return RedumpSystem.SonyPlayStation3;
|
||||
case CommandStrings.SACD:
|
||||
return RedumpSystem.SuperAudioCD;
|
||||
case CommandStrings.XBOX:
|
||||
case CommandStrings.XBOXSwap:
|
||||
return RedumpSystem.MicrosoftXbox;
|
||||
case CommandStrings.XGD2Swap:
|
||||
case CommandStrings.XGD3Swap:
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the MediaType associated with a given base command
|
||||
/// </summary>
|
||||
/// <param name="baseCommand">Command value to check</param>
|
||||
/// <returns>MediaType if possible, null on error</returns>
|
||||
/// <remarks>This takes the "safe" route by assuming the larger of any given format</remarks>
|
||||
public static MediaType? ToMediaType(string baseCommand)
|
||||
{
|
||||
switch (baseCommand)
|
||||
{
|
||||
case CommandStrings.Audio:
|
||||
case CommandStrings.CompactDisc:
|
||||
case CommandStrings.Data:
|
||||
case CommandStrings.SACD:
|
||||
return MediaType.CDROM;
|
||||
case CommandStrings.GDROM:
|
||||
case CommandStrings.Swap:
|
||||
return MediaType.GDROM;
|
||||
case CommandStrings.DigitalVideoDisc:
|
||||
case CommandStrings.XBOX:
|
||||
case CommandStrings.XBOXSwap:
|
||||
case CommandStrings.XGD2Swap:
|
||||
case CommandStrings.XGD3Swap:
|
||||
return MediaType.DVD;
|
||||
case CommandStrings.BluRay:
|
||||
return MediaType.BluRay;
|
||||
|
||||
// Non-optical
|
||||
case CommandStrings.Floppy:
|
||||
return MediaType.FloppyDisk;
|
||||
case CommandStrings.Disk:
|
||||
return MediaType.HardDisk;
|
||||
case CommandStrings.Tape:
|
||||
return MediaType.DataCartridge;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default extension for a given disc type
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType value to check</param>
|
||||
/// <returns>Valid extension (with leading '.'), null on error</returns>
|
||||
public static string Extension(MediaType? type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
case MediaType.GDROM:
|
||||
case MediaType.Cartridge:
|
||||
case MediaType.HardDisk:
|
||||
case MediaType.CompactFlash:
|
||||
case MediaType.MMC:
|
||||
case MediaType.SDCard:
|
||||
case MediaType.FlashDrive:
|
||||
return ".bin";
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
return ".iso";
|
||||
case MediaType.LaserDisc:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
return ".raw";
|
||||
case MediaType.NintendoWiiUOpticalDisc:
|
||||
return ".wud";
|
||||
case MediaType.FloppyDisk:
|
||||
return ".img";
|
||||
case MediaType.Cassette:
|
||||
return ".wav";
|
||||
case MediaType.NONE:
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x64;linux-x64;osx-x64</RuntimeIdentifiers>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<Version>2.6.6</Version>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.Models" Version="1.1.4" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.1.6" />
|
||||
<PackageReference Include="System.Memory" Version="4.5.5" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
<PackageReference Include="Unclassified.NetRevisionTask" Version="0.4.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,32 +0,0 @@
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Modules.Redumper
|
||||
{
|
||||
public static class Converters
|
||||
{
|
||||
#region Cross-enumeration conversions
|
||||
|
||||
/// <summary>
|
||||
/// Get the default extension for a given disc type
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType value to check</param>
|
||||
/// <returns>Valid extension (with leading '.'), null on error</returns>
|
||||
public static string Extension(MediaType? type)
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NETFRAMEWORK
|
||||
using IMAPI2;
|
||||
#endif
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using Xunit;
|
||||
@@ -18,41 +15,12 @@ namespace MPF.Test.Core.Converters
|
||||
/// <summary>
|
||||
/// DiscType values that map to InternalDriveType
|
||||
/// </summary>
|
||||
private static readonly DriveType[] _mappableDriveTypes = new DriveType[]
|
||||
{
|
||||
private static readonly DriveType[] _mappableDriveTypes =
|
||||
[
|
||||
DriveType.CDRom,
|
||||
DriveType.Fixed,
|
||||
DriveType.Removable,
|
||||
};
|
||||
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// IMAPI_MEDIA_PHYSICAL_TYPE values that map to MediaType
|
||||
/// </summary>
|
||||
private static readonly IMAPI_MEDIA_PHYSICAL_TYPE[] _mappableImapiTypes = new IMAPI_MEDIA_PHYSICAL_TYPE[]
|
||||
{
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_UNKNOWN,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDROM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDR,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_CDRW,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDROM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDRAM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSR_DUALLAYER,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHRW,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDDASHR_DUALLAYER,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DISK,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_DVDPLUSRW_DUALLAYER,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDROM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDR,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_HDDVDRAM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDROM,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDR,
|
||||
IMAPI_MEDIA_PHYSICAL_TYPE.IMAPI_MEDIA_TYPE_BDRE,
|
||||
};
|
||||
#endif
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Check that every supported DriveType maps to an InternalDriveType
|
||||
@@ -71,63 +39,24 @@ namespace MPF.Test.Core.Converters
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Check that every supported IMAPI_MEDIA_PHYSICAL_TYPE maps to an MediaType
|
||||
/// </summary>
|
||||
/// <param name="imapiType">IMAPI_MEDIA_PHYSICAL_TYPE value to check</param>
|
||||
/// <param name="expectNull">True to expect a null mapping, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateImapiTypeMappingTestData))]
|
||||
public void IMAPIToMediaTypeTest(IMAPI_MEDIA_PHYSICAL_TYPE imapiType, bool expectNull)
|
||||
{
|
||||
var actual = imapiType.IMAPIToMediaType();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of DriveType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DriveType values</returns>
|
||||
public static List<object[]> GenerateDriveTypeMappingTestData()
|
||||
public static List<object?[]> GenerateDriveTypeMappingTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (DriveType driveType in Enum.GetValues(typeof(DriveType)))
|
||||
{
|
||||
if (_mappableDriveTypes.Contains(driveType))
|
||||
testData.Add(new object[] { driveType, false });
|
||||
testData.Add([driveType, false]);
|
||||
else
|
||||
testData.Add(new object[] { driveType, true });
|
||||
testData.Add([driveType, true]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#if NETFRAMEWORK
|
||||
/// <summary>
|
||||
/// Generate a test set of IMAPI_MEDIA_PHYSICAL_TYPE values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of IMAPI_MEDIA_PHYSICAL_TYPE values</returns>
|
||||
public static List<object[]> GenerateImapiTypeMappingTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
foreach (IMAPI_MEDIA_PHYSICAL_TYPE imapiType in Enum.GetValues(typeof(IMAPI_MEDIA_PHYSICAL_TYPE)))
|
||||
{
|
||||
if (_mappableImapiTypes.Contains(imapiType))
|
||||
testData.Add(new object[] { imapiType, false });
|
||||
else
|
||||
testData.Add(new object[] { imapiType, true });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Convert to Long Name
|
||||
@@ -150,12 +79,12 @@ namespace MPF.Test.Core.Converters
|
||||
/// Generate a test set of InternalProgram values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of InternalProgram values</returns>
|
||||
public static List<object[]> GenerateInternalProgramTestData()
|
||||
public static List<object?[]> GenerateInternalProgramTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null } };
|
||||
var testData = new List<object?[]>() { new object?[] { null } };
|
||||
foreach (InternalProgram? internalProgram in Enum.GetValues(typeof(InternalProgram)))
|
||||
{
|
||||
testData.Add(new object[] { internalProgram });
|
||||
testData.Add([internalProgram]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
using MPF.Core.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Core.Data
|
||||
{
|
||||
public class XgdInfoTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("")]
|
||||
[InlineData("1234567")]
|
||||
[InlineData("1234567\0")]
|
||||
[InlineData("123456789")]
|
||||
public void UnmatchedStringTests(string invalidString)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(invalidString);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("AV00100W", "AV", "001", "00", 'W')]
|
||||
[InlineData("AV00100W\0", "AV", "001", "00", 'W')]
|
||||
public void XGD1ValidTests(string validString, string publisher, string gameId, string version, char regionIdentifier)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(validString);
|
||||
|
||||
Assert.True(xgdInfo.Initialized);
|
||||
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]
|
||||
// Invalid publisher identifier
|
||||
[InlineData("ZZ000000")]
|
||||
[InlineData("ZZ000000\0")]
|
||||
// Invalid region identifier
|
||||
[InlineData("AV00000Z")]
|
||||
[InlineData("AV00000Z\0")]
|
||||
public void XGD1InvalidTests(string invalidString)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(invalidString);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("AV200100W0F11", "AV", "001", "00", 'W', "0", 'F', "11", null)]
|
||||
[InlineData("AV200100W0F11\0", "AV", "001", "00", 'W', "0", 'F', "11", null)]
|
||||
[InlineData("AV200100W01F11", "AV", "001", "00", 'W', "01", 'F', "11", null)]
|
||||
[InlineData("AV200100W01F11\0", "AV", "001", "00", 'W', "01", 'F', "11", null)]
|
||||
[InlineData("AV200100W0F11DEADBEEF", "AV", "001", "00", 'W', "0", 'F', "11", "DEADBEEF")]
|
||||
[InlineData("AV200100W0F11DEADBEEF\0", "AV", "001", "00", 'W', "0", 'F', "11", "DEADBEEF")]
|
||||
[InlineData("AV200100W01F11DEADBEEF", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
|
||||
[InlineData("AV200100W01F11DEADBEEF\0", "AV", "001", "00", 'W', "01", 'F', "11", "DEADBEEF")]
|
||||
public void XGD23ValidTests(string validString, string publisher, string gameId, string sku, char regionIdentifier, string baseVersion, char mediaSubtype, string discNumber, string cert)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(validString);
|
||||
|
||||
Assert.True(xgdInfo.Initialized);
|
||||
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]
|
||||
// Invalid publisher identifier
|
||||
[InlineData("ZZ00000000000")]
|
||||
[InlineData("ZZ00000000000\0")]
|
||||
[InlineData("ZZ000000000000")]
|
||||
[InlineData("ZZ000000000000\0")]
|
||||
[InlineData("ZZ0000000000000000000")]
|
||||
[InlineData("ZZ0000000000000000000\0")]
|
||||
[InlineData("ZZ00000000000000000000")]
|
||||
[InlineData("ZZ00000000000000000000\0")]
|
||||
// Invalid platform identifier
|
||||
[InlineData("AV90000000000")]
|
||||
[InlineData("AV90000000000\0")]
|
||||
[InlineData("AV900000000000")]
|
||||
[InlineData("AV900000000000\0")]
|
||||
[InlineData("AV9000000000000000000")]
|
||||
[InlineData("AV9000000000000000000\0")]
|
||||
[InlineData("AV90000000000000000000")]
|
||||
[InlineData("AV90000000000000000000\0")]
|
||||
// Invalid region identifier
|
||||
[InlineData("AV200000Z0000")]
|
||||
[InlineData("AV200000Z0000\0")]
|
||||
[InlineData("AV200000Z00000")]
|
||||
[InlineData("AV200000Z00000\0")]
|
||||
[InlineData("AV200000Z000000000000")]
|
||||
[InlineData("AV200000Z000000000000\0")]
|
||||
[InlineData("AV200000Z0000000000000")]
|
||||
[InlineData("AV200000Z0000000000000\0")]
|
||||
// Invalid media subtype identifier
|
||||
[InlineData("AV200000W0A00")]
|
||||
[InlineData("AV200000W0A00\0")]
|
||||
[InlineData("AV200000W00A00")]
|
||||
[InlineData("AV200000W00A00\0")]
|
||||
[InlineData("AV200000W00A000000000")]
|
||||
[InlineData("AV200000W00A000000000\0")]
|
||||
[InlineData("AV200000W00A0000000000")]
|
||||
[InlineData("AV200000W00A0000000000\0")]
|
||||
public void XGD23InvalidTests(string invalidString)
|
||||
{
|
||||
XgdInfo xgdInfo = new XgdInfo(invalidString);
|
||||
Assert.False(xgdInfo.Initialized);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,8 @@ namespace MPF.Test.Core.Utilities
|
||||
/// <summary>
|
||||
/// MediaType values that support drive speeds
|
||||
/// </summary>
|
||||
private static readonly MediaType?[] _supportDriveSpeeds = new MediaType?[]
|
||||
{
|
||||
private static readonly MediaType?[] _supportDriveSpeeds =
|
||||
[
|
||||
MediaType.CDROM,
|
||||
MediaType.DVD,
|
||||
MediaType.GDROM,
|
||||
@@ -21,13 +21,13 @@ namespace MPF.Test.Core.Utilities
|
||||
MediaType.BluRay,
|
||||
MediaType.NintendoGameCubeGameDisc,
|
||||
MediaType.NintendoWiiOpticalDisc,
|
||||
};
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// RedumpSystem values that are considered Audio
|
||||
/// </summary>
|
||||
private static readonly RedumpSystem?[] _audioSystems = new RedumpSystem?[]
|
||||
{
|
||||
private static readonly RedumpSystem?[] _audioSystems =
|
||||
[
|
||||
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem,
|
||||
RedumpSystem.AudioCD,
|
||||
RedumpSystem.DVDAudio,
|
||||
@@ -39,40 +39,41 @@ namespace MPF.Test.Core.Utilities
|
||||
RedumpSystem.PlayStationGameSharkUpdates,
|
||||
RedumpSystem.PhilipsCDi,
|
||||
RedumpSystem.SuperAudioCD,
|
||||
};
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// RedumpSystem values that are considered markers
|
||||
/// </summary>
|
||||
private static readonly RedumpSystem?[] _markerSystems = new RedumpSystem?[]
|
||||
{
|
||||
private static readonly RedumpSystem?[] _markerSystems =
|
||||
[
|
||||
RedumpSystem.MarkerArcadeEnd,
|
||||
RedumpSystem.MarkerComputerEnd,
|
||||
RedumpSystem.MarkerDiscBasedConsoleEnd,
|
||||
RedumpSystem.MarkerOtherEnd,
|
||||
};
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// RedumpSystem values that are have reversed ringcodes
|
||||
/// </summary>
|
||||
private static readonly RedumpSystem?[] _reverseRingcodeSystems = new RedumpSystem?[]
|
||||
{
|
||||
private static readonly RedumpSystem?[] _reverseRingcodeSystems =
|
||||
[
|
||||
RedumpSystem.SonyPlayStation2,
|
||||
RedumpSystem.SonyPlayStation3,
|
||||
RedumpSystem.SonyPlayStation4,
|
||||
RedumpSystem.SonyPlayStation5,
|
||||
RedumpSystem.SonyPlayStationPortable,
|
||||
};
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// RedumpSystem values that are considered XGD
|
||||
/// </summary>
|
||||
private static readonly RedumpSystem?[] _xgdSystems = new RedumpSystem?[]
|
||||
{
|
||||
private static readonly RedumpSystem?[] _xgdSystems =
|
||||
[
|
||||
RedumpSystem.MicrosoftXbox,
|
||||
RedumpSystem.MicrosoftXbox360,
|
||||
RedumpSystem.MicrosoftXboxOne,
|
||||
RedumpSystem.MicrosoftXboxSeriesXS,
|
||||
};
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Check that all optical media support drive speeds
|
||||
@@ -143,15 +144,15 @@ namespace MPF.Test.Core.Utilities
|
||||
/// Generate a test set of MediaType values that support drive speeds
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of MediaType values</returns>
|
||||
public static List<object[]> GenerateSupportDriveSpeedsTestData()
|
||||
public static List<object?[]> GenerateSupportDriveSpeedsTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (MediaType mediaType in Enum.GetValues(typeof(MediaType)))
|
||||
{
|
||||
if (_supportDriveSpeeds.Contains(mediaType))
|
||||
testData.Add(new object[] { mediaType, true });
|
||||
testData.Add([mediaType, true]);
|
||||
else
|
||||
testData.Add(new object[] { mediaType, false });
|
||||
testData.Add([mediaType, false]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -161,15 +162,15 @@ namespace MPF.Test.Core.Utilities
|
||||
/// Generate a test set of RedumpSystem values that are considered Audio
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object[]> GenerateAudioSystemsTestData()
|
||||
public static List<object?[]> GenerateAudioSystemsTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_audioSystems.Contains(redumpSystem))
|
||||
testData.Add(new object[] { redumpSystem, true });
|
||||
testData.Add([redumpSystem, true]);
|
||||
else
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
testData.Add([redumpSystem, false]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -179,15 +180,15 @@ namespace MPF.Test.Core.Utilities
|
||||
/// Generate a test set of RedumpSystem values that are considered markers
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object[]> GenerateMarkerSystemsTestData()
|
||||
public static List<object?[]> GenerateMarkerSystemsTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_markerSystems.Contains(redumpSystem))
|
||||
testData.Add(new object[] { redumpSystem, true });
|
||||
testData.Add([redumpSystem, true]);
|
||||
else
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
testData.Add([redumpSystem, false]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -197,15 +198,15 @@ namespace MPF.Test.Core.Utilities
|
||||
/// Generate a test set of RedumpSystem values that are considered markers
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object[]> GenerateReversedRingcodeSystemsTestData()
|
||||
public static List<object?[]> GenerateReversedRingcodeSystemsTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_reverseRingcodeSystems.Contains(redumpSystem))
|
||||
testData.Add(new object[] { redumpSystem, true });
|
||||
testData.Add([redumpSystem, true]);
|
||||
else
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
testData.Add([redumpSystem, false]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -215,15 +216,15 @@ namespace MPF.Test.Core.Utilities
|
||||
/// Generate a test set of RedumpSystem values that are considered XGD
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object[]> GenerateXGDSystemsTestData()
|
||||
public static List<object?[]> GenerateXGDSystemsTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_xgdSystems.Contains(redumpSystem))
|
||||
testData.Add(new object[] { redumpSystem, true });
|
||||
testData.Add([redumpSystem, true]);
|
||||
else
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
testData.Add([redumpSystem, false]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.IO;
|
||||
using MPF.Core;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Library;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
@@ -15,7 +14,7 @@ namespace MPF.Test.Library
|
||||
[InlineData("fd A test.img", 'A', true, MediaType.FloppyDisk, true)]
|
||||
[InlineData("dvd X test.iso 8 /raw", 'X', false, MediaType.FloppyDisk, false)]
|
||||
[InlineData("stop D", 'D', false, MediaType.DVD, true)]
|
||||
public void ParametersValidTest(string parameters, char letter, bool isFloppy, MediaType? mediaType, bool expected)
|
||||
public void ParametersValidTest(string? parameters, char letter, bool isFloppy, MediaType? mediaType, bool expected)
|
||||
{
|
||||
var options = new Options() { InternalProgram = InternalProgram.DiscImageCreator };
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MPF.Library;
|
||||
using MPF.Core;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
@@ -8,59 +9,18 @@ namespace MPF.Test.Library
|
||||
{
|
||||
public class InfoToolTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null, 0, 0, 0, 0, null)]
|
||||
[InlineData(null, 12345, 0, 0, 0, null)]
|
||||
[InlineData(null, 12345, 1, 0, 0, null)]
|
||||
[InlineData(null, 12345, 1, 2, 0, null)]
|
||||
[InlineData(null, 12345, 1, 2, 3, null)]
|
||||
[InlineData(MediaType.CDROM, 0, 0, 0, 0, "CD-ROM")]
|
||||
[InlineData(MediaType.CDROM, 12345, 0, 0, 0, "CD-ROM")]
|
||||
[InlineData(MediaType.CDROM, 12345, 1, 0, 0, "CD-ROM")]
|
||||
[InlineData(MediaType.CDROM, 12345, 1, 2, 0, "CD-ROM")]
|
||||
[InlineData(MediaType.CDROM, 12345, 1, 2, 3, "CD-ROM")]
|
||||
[InlineData(MediaType.DVD, 0, 0, 0, 0, "DVD-ROM-5")]
|
||||
[InlineData(MediaType.DVD, 12345, 0, 0, 0, "DVD-ROM-5")]
|
||||
[InlineData(MediaType.DVD, 12345, 1, 0, 0, "DVD-ROM-9")]
|
||||
[InlineData(MediaType.DVD, 12345, 1, 2, 0, "DVD-ROM-9")]
|
||||
[InlineData(MediaType.DVD, 12345, 1, 2, 3, "DVD-ROM-9")]
|
||||
[InlineData(MediaType.BluRay, 0, 0, 0, 0, "BD-ROM-25")]
|
||||
[InlineData(MediaType.BluRay, 12345, 0, 0, 0, "BD-ROM-25")]
|
||||
[InlineData(MediaType.BluRay, 26_843_531_857, 0, 0, 0, "BD-ROM-33")]
|
||||
[InlineData(MediaType.BluRay, 12345, 1, 0, 0, "BD-ROM-50")]
|
||||
[InlineData(MediaType.BluRay, 53_687_063_713, 1, 0, 0, "BD-ROM-66")]
|
||||
[InlineData(MediaType.BluRay, 12345, 1, 2, 0, "BD-ROM-100")]
|
||||
[InlineData(MediaType.BluRay, 12345, 1, 2, 3, "BD-ROM-128")]
|
||||
[InlineData(MediaType.UMD, 0, 0, 0, 0, "UMD-SL")]
|
||||
[InlineData(MediaType.UMD, 12345, 0, 0, 0, "UMD-SL")]
|
||||
[InlineData(MediaType.UMD, 12345, 1, 0, 0, "UMD-DL")]
|
||||
[InlineData(MediaType.UMD, 12345, 1, 2, 0, "UMD-DL")]
|
||||
[InlineData(MediaType.UMD, 12345, 1, 2, 3, "UMD-DL")]
|
||||
public void GetFixedMediaTypeTest(
|
||||
MediaType? mediaType,
|
||||
long size,
|
||||
long layerbreak,
|
||||
long layerbreak2,
|
||||
long layerbreak3,
|
||||
string expected)
|
||||
{
|
||||
// TODO: Add tests around BDU
|
||||
string actual = InfoTool.GetFixedMediaType(mediaType, null, size, layerbreak, layerbreak2, layerbreak3);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, "")]
|
||||
[InlineData(" ", "")]
|
||||
[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)
|
||||
public void NormalizeOutputPathsTest(string? outputPath, string? expectedPath)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(expectedPath))
|
||||
if (!string.IsNullOrEmpty(expectedPath))
|
||||
expectedPath = Path.GetFullPath(expectedPath);
|
||||
|
||||
string actualPath = InfoTool.NormalizeOutputPaths(outputPath, true);
|
||||
@@ -71,26 +31,18 @@ namespace MPF.Test.Library
|
||||
public void ProcessSpecialFieldsCompleteTest()
|
||||
{
|
||||
// Create a new SubmissionInfo object
|
||||
SubmissionInfo info = new SubmissionInfo()
|
||||
var info = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = new CommonDiscInfoSection()
|
||||
{
|
||||
Comments = "This is a comments line\n[T:ISBN] ISBN Value",
|
||||
#if NET48
|
||||
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
CommentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
|
||||
},
|
||||
|
||||
Contents = "This is a contents line\n[T:GF] Game Footage",
|
||||
#if NET48
|
||||
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
ContentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.Patches] = "1.04 patch",
|
||||
},
|
||||
@@ -98,7 +50,7 @@ namespace MPF.Test.Library
|
||||
};
|
||||
|
||||
// Process the special fields
|
||||
InfoTool.ProcessSpecialFields(info);
|
||||
Formatter.ProcessSpecialFields(info);
|
||||
|
||||
// Validate the basics
|
||||
Assert.NotNull(info.CommonDiscInfo.Comments);
|
||||
@@ -119,13 +71,13 @@ namespace MPF.Test.Library
|
||||
public void ProcessSpecialFieldsNullObjectTest()
|
||||
{
|
||||
// Create a new SubmissionInfo object
|
||||
SubmissionInfo info = new SubmissionInfo()
|
||||
var info = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = null,
|
||||
};
|
||||
|
||||
// Process the special fields
|
||||
InfoTool.ProcessSpecialFields(info);
|
||||
Formatter.ProcessSpecialFields(info);
|
||||
|
||||
// Validate
|
||||
Assert.Null(info.CommonDiscInfo);
|
||||
@@ -135,26 +87,18 @@ namespace MPF.Test.Library
|
||||
public void ProcessSpecialFieldsNullCommentsContentsTest()
|
||||
{
|
||||
// Create a new SubmissionInfo object
|
||||
SubmissionInfo info = new SubmissionInfo()
|
||||
var info = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = new CommonDiscInfoSection()
|
||||
{
|
||||
Comments = null,
|
||||
#if NET48
|
||||
CommentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
CommentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.VolumeLabel] = "VOLUME_LABEL",
|
||||
},
|
||||
|
||||
Contents = null,
|
||||
#if NET48
|
||||
ContentsSpecialFields = new Dictionary<SiteCode?, string>()
|
||||
#else
|
||||
ContentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
#endif
|
||||
{
|
||||
[SiteCode.Patches] = "1.04 patch",
|
||||
},
|
||||
@@ -162,7 +106,7 @@ namespace MPF.Test.Library
|
||||
};
|
||||
|
||||
// Process the special fields
|
||||
InfoTool.ProcessSpecialFields(info);
|
||||
Formatter.ProcessSpecialFields(info);
|
||||
|
||||
// Validate the basics
|
||||
Assert.NotNull(info.CommonDiscInfo.Comments);
|
||||
@@ -183,7 +127,7 @@ namespace MPF.Test.Library
|
||||
public void ProcessSpecialFieldsNullDictionariesTest()
|
||||
{
|
||||
// Create a new SubmissionInfo object
|
||||
SubmissionInfo info = new SubmissionInfo()
|
||||
var info = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = new CommonDiscInfoSection()
|
||||
{
|
||||
@@ -196,7 +140,7 @@ namespace MPF.Test.Library
|
||||
};
|
||||
|
||||
// Process the special fields
|
||||
InfoTool.ProcessSpecialFields(info);
|
||||
Formatter.ProcessSpecialFields(info);
|
||||
|
||||
// Validate the basics
|
||||
Assert.NotNull(info.CommonDiscInfo.Comments);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using MPF.Library;
|
||||
using MPF.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.Library
|
||||
@@ -11,11 +11,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsActiveMARKTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"ActiveMARK",
|
||||
"ActiveMARK 5",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("ActiveMARK 5", sanitized);
|
||||
@@ -24,11 +24,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsCactusDataShieldTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"Cactus Data Shield 200",
|
||||
"Cactus Data Shield 200 (Build 3.0.100a)",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("Cactus Data Shield 200 (Build 3.0.100a)", sanitized);
|
||||
@@ -37,11 +37,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsCDCheckTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"Anything Else Protection",
|
||||
"Executable-Based CD Check",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("Anything Else Protection", sanitized);
|
||||
@@ -50,11 +50,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsCDCopsTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"CD-Cops",
|
||||
"CD-Cops v1.2.0",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("CD-Cops v1.2.0", sanitized);
|
||||
@@ -63,11 +63,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsCDKeyTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"Anything Else Protection",
|
||||
"CD-Key / Serial",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("Anything Else Protection", sanitized);
|
||||
@@ -76,11 +76,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsEACdKeyTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"EA CdKey Registration Module",
|
||||
"EA CdKey Registration Module v1.2.0",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("EA CdKey Registration Module v1.2.0", sanitized);
|
||||
@@ -89,11 +89,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsEADRMTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"EA DRM Protection",
|
||||
"EA DRM Protection v1.2.0",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("EA DRM Protection v1.2.0", sanitized);
|
||||
@@ -102,11 +102,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsGFWLTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"Games for Windows LIVE",
|
||||
"Games for Windows LIVE v1.2.0",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("Games for Windows LIVE v1.2.0", sanitized);
|
||||
@@ -115,11 +115,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsGFWLZDPPTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"Games for Windows LIVE",
|
||||
"Games for Windows LIVE Zero Day Piracy Protection",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("Games for Windows LIVE, Games for Windows LIVE Zero Day Piracy Protection", sanitized);
|
||||
@@ -128,11 +128,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsImpulseReactorTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"Impulse Reactor",
|
||||
"Impulse Reactor Core Module v1.2.0",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("Impulse Reactor Core Module v1.2.0", sanitized);
|
||||
@@ -145,14 +145,14 @@ namespace MPF.Test.Library
|
||||
[InlineData(3)]
|
||||
public void SanitizeFoundProtectionsJoWoodXProtTest(int skip)
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"JoWood X-Prot 1.2.0.00",
|
||||
"JoWood X-Prot v2",
|
||||
"JoWood X-Prot v1.4+",
|
||||
"JoWood X-Prot v1.0-v1.3",
|
||||
"JoWood X-Prot",
|
||||
};
|
||||
];
|
||||
|
||||
// Safeguard for the future
|
||||
if (skip >= protections.Count)
|
||||
@@ -168,11 +168,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsOnlineRegistrationTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"Anything Else Protection",
|
||||
"Executable-Based Online Registration",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("Anything Else Protection", sanitized);
|
||||
@@ -185,14 +185,14 @@ namespace MPF.Test.Library
|
||||
[InlineData(3)]
|
||||
public void SanitizeFoundProtectionStarForceTest(int skip)
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"StarForce 1.20.000.000",
|
||||
"StarForce 5 [Protected Module]",
|
||||
"StarForce 5",
|
||||
"StarForce 3-5",
|
||||
"StarForce",
|
||||
};
|
||||
];
|
||||
|
||||
// Safeguard for the future
|
||||
if (skip >= protections.Count)
|
||||
@@ -208,11 +208,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsSysiphusTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"Sysiphus",
|
||||
"Sysiphus v1.2.0",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("Sysiphus v1.2.0", sanitized);
|
||||
@@ -221,11 +221,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsXCPTest()
|
||||
{
|
||||
List<string> protections = new List<string>()
|
||||
{
|
||||
List<string> protections =
|
||||
[
|
||||
"XCP",
|
||||
"XCP v1.2.0",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("XCP v1.2.0", sanitized);
|
||||
|
||||
@@ -1,54 +1,35 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<TargetFrameworks>net6.0-windows;net8.0-windows</TargetFrameworks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<COMReference Include="IMAPI2">
|
||||
<Guid>{2735412F-7F64-5B0F-8F00-5D77AFBE261E}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
<COMReference Include="IMAPI2FS">
|
||||
<Guid>{2C941FD0-975B-59BE-A960-9A2A262853A5}</Guid>
|
||||
<VersionMajor>1</VersionMajor>
|
||||
<VersionMinor>0</VersionMinor>
|
||||
<Lcid>0</Lcid>
|
||||
<WrapperTool>tlbimp</WrapperTool>
|
||||
<Isolated>False</Isolated>
|
||||
<EmbedInteropTypes>True</EmbedInteropTypes>
|
||||
</COMReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF\MPF.csproj" />
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
|
||||
<ProjectReference Include="..\MPF.Modules\MPF.Modules.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.7.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.10.0-preview-24080-01" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-preview-24080-01" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.1.1" />
|
||||
<PackageReference Include="xunit" Version="2.5.1" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
|
||||
<PackageReference Include="xunit" Version="2.7.0" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="1.3.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.5.1" />
|
||||
<PackageReference Include="xunit.core" Version="2.5.1" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.5.1" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.5.1" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.5.1">
|
||||
<PackageReference Include="xunit.analyzers" Version="1.11.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.7.0" />
|
||||
<PackageReference Include="xunit.core" Version="2.7.0" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.7.0" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.7.0" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.7.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.1">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Modules.DiscImageCreator;
|
||||
using MPF.Core.Modules.DiscImageCreator;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
@@ -17,10 +18,10 @@ namespace MPF.Test.Modules
|
||||
[InlineData(RedumpSystem.SonyPlayStation3, MediaType.BluRay, CommandStrings.BluRay)]
|
||||
[InlineData(RedumpSystem.AppleMacintosh, MediaType.FloppyDisk, CommandStrings.Floppy)]
|
||||
[InlineData(RedumpSystem.RawThrillsVarious, MediaType.GDROM, null)]
|
||||
public void ParametersFromSystemAndTypeTest(RedumpSystem? knownSystem, MediaType? mediaType, string expected)
|
||||
public void ParametersFromSystemAndTypeTest(RedumpSystem? knownSystem, MediaType? mediaType, string? expected)
|
||||
{
|
||||
var options = new Options();
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
Assert.Equal(expected, actual.BaseCommand);
|
||||
}
|
||||
|
||||
@@ -28,12 +29,12 @@ namespace MPF.Test.Modules
|
||||
[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)
|
||||
public void ParametersFromOptionsSpecialDefaultTest(RedumpSystem? knownSystem, MediaType? mediaType, string[]? expected)
|
||||
{
|
||||
var options = new Options();
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
}
|
||||
@@ -44,9 +45,9 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsC2RereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadC2, string[] expected)
|
||||
{
|
||||
var options = new Options { DICRereadCount = rereadC2 };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
@@ -64,9 +65,9 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsDVDRereadTest(RedumpSystem? knownSystem, MediaType? mediaType, int rereadDVDBD, string[] expected)
|
||||
{
|
||||
var options = new Options { DICDVDRereadCount = rereadDVDBD };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
@@ -88,9 +89,9 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsMultiSectorReadTest(RedumpSystem? knownSystem, MediaType? mediaType, bool multiSectorRead, string[] expected)
|
||||
{
|
||||
var options = new Options { DICMultiSectorRead = multiSectorRead };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (expectedSet.Count != 1 && multiSectorRead)
|
||||
@@ -109,9 +110,9 @@ namespace MPF.Test.Modules
|
||||
public void ParametersFromOptionsParanoidModeTest(RedumpSystem? knownSystem, MediaType? mediaType, bool paranoidMode, string[] expected)
|
||||
{
|
||||
var options = new Options { DICParanoidMode = paranoidMode };
|
||||
var actual = new Parameters(knownSystem, mediaType, 'D', "disc.bin", 16, options);
|
||||
var actual = new Parameters(knownSystem, mediaType, "D:\\", "disc.bin", 16, options);
|
||||
|
||||
HashSet<string> expectedSet = new HashSet<string>(expected ?? new string[0]);
|
||||
var expectedSet = new HashSet<string>(expected ?? Array.Empty<string>());
|
||||
HashSet<string> actualSet = GenerateUsedKeys(actual);
|
||||
Assert.Equal(expectedSet, actualSet);
|
||||
if (paranoidMode)
|
||||
@@ -146,7 +147,7 @@ namespace MPF.Test.Modules
|
||||
[InlineData("bd D longer\\path_test.iso 16", true)]
|
||||
[InlineData("stop D", true)]
|
||||
[InlineData("ls", false)]
|
||||
public void ValidateParametersTest(string parameters, bool expected)
|
||||
public void ValidateParametersTest(string? parameters, bool expected)
|
||||
{
|
||||
var actual = new Parameters(parameters);
|
||||
Assert.Equal(expected, actual.IsValid());
|
||||
@@ -160,9 +161,9 @@ namespace MPF.Test.Modules
|
||||
[InlineData(MediaType.FloppyDisk, ".img")]
|
||||
[InlineData(MediaType.Cassette, ".wav")]
|
||||
[InlineData(MediaType.NONE, null)]
|
||||
public void MediaTypeToExtensionTest(MediaType? mediaType, string expected)
|
||||
public void MediaTypeToExtensionTest(MediaType? mediaType, string? expected)
|
||||
{
|
||||
string actual = Converters.Extension(mediaType);
|
||||
var actual = Converters.Extension(mediaType);
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
@@ -226,7 +227,7 @@ namespace MPF.Test.Modules
|
||||
Assert.NotNull(parametersObject);
|
||||
|
||||
// Validate that the same set of parameters are generated on the output
|
||||
string newParameters = parametersObject.GenerateParameters();
|
||||
var newParameters = parametersObject.GenerateParameters();
|
||||
Assert.NotNull(newParameters);
|
||||
Assert.Equal(originalParameters, newParameters);
|
||||
}
|
||||
@@ -241,7 +242,7 @@ namespace MPF.Test.Modules
|
||||
Assert.NotNull(parametersObject);
|
||||
|
||||
// Validate that the same set of parameters are generated on the output
|
||||
string newParameters = parametersObject.GenerateParameters();
|
||||
var newParameters = parametersObject.GenerateParameters();
|
||||
Assert.NotNull(newParameters);
|
||||
Assert.Equal(originalParameters, newParameters);
|
||||
}
|
||||
@@ -253,7 +254,7 @@ namespace MPF.Test.Modules
|
||||
/// <returns>HashSet representing the strings</returns>
|
||||
private static HashSet<string> GenerateUsedKeys(Parameters parameters)
|
||||
{
|
||||
HashSet<string> usedKeys = new HashSet<string>();
|
||||
var usedKeys = new HashSet<string>();
|
||||
if (parameters?.Keys == null)
|
||||
return usedKeys;
|
||||
|
||||
|
||||
@@ -95,15 +95,15 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of DiscType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DiscType values</returns>
|
||||
public static List<object[]> GenerateDiscTypeMappingTestData()
|
||||
public static List<object?[]> GenerateDiscTypeMappingTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (DiscType? discType in Enum.GetValues(typeof(DiscType)))
|
||||
{
|
||||
if (_mappableDiscTypes.Contains(discType))
|
||||
testData.Add(new object[] { discType, false });
|
||||
testData.Add(new object?[] { discType, false });
|
||||
else
|
||||
testData.Add(new object[] { discType, true });
|
||||
testData.Add(new object?[] { discType, true });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -113,12 +113,12 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of RedumpSystem values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object[]> GenerateRedumpSystemMappingTestData()
|
||||
public static List<object?[]> GenerateRedumpSystemMappingTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null } };
|
||||
var testData = new List<object?[]>() { new object?[] { null } };
|
||||
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
testData.Add(new object[] { redumpSystem });
|
||||
testData.Add(new object?[] { redumpSystem });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -128,16 +128,16 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of mappable media types
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of MediaTypes</returns>
|
||||
public static List<object[]> GenerateMediaTypeMappingTestData()
|
||||
public static List<object?[]> GenerateMediaTypeMappingTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
|
||||
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
|
||||
{
|
||||
if (_mappableMediaTypes.Contains(mediaType))
|
||||
testData.Add(new object[] { mediaType, false });
|
||||
testData.Add(new object?[] { mediaType, false });
|
||||
else
|
||||
testData.Add(new object[] { mediaType, true });
|
||||
testData.Add(new object?[] { mediaType, true });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -156,7 +156,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateDiscCategoryTestData))]
|
||||
public void DiscCategoryLongNameTest(DiscCategory? discCategory, bool expectNull)
|
||||
{
|
||||
string actual = discCategory.LongName();
|
||||
var actual = discCategory.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -168,12 +168,12 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of DiscCategory values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DiscCategory values</returns>
|
||||
public static List<object[]> GenerateDiscCategoryTestData()
|
||||
public static List<object?[]> GenerateDiscCategoryTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (DiscCategory? discCategory in Enum.GetValues(typeof(DiscCategory)))
|
||||
{
|
||||
testData.Add(new object[] { discCategory, false });
|
||||
testData.Add(new object?[] { discCategory, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -192,7 +192,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateDiscTypeTestData))]
|
||||
public void DiscTypeLongNameTest(DiscType? discType, bool expectNull)
|
||||
{
|
||||
string actual = discType.LongName();
|
||||
var actual = discType.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -204,15 +204,15 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of DiscType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DiscType values</returns>
|
||||
public static List<object[]> GenerateDiscTypeTestData()
|
||||
public static List<object?[]> GenerateDiscTypeTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (DiscType? discType in Enum.GetValues(typeof(DiscType)))
|
||||
{
|
||||
if (discType == DiscType.NONE)
|
||||
testData.Add(new object[] { discType, true });
|
||||
testData.Add(new object?[] { discType, true });
|
||||
else
|
||||
testData.Add(new object[] { discType, false });
|
||||
testData.Add(new object?[] { discType, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -231,7 +231,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateLanguageTestData))]
|
||||
public void LanguageLongNameTest(Language? language, bool expectNull)
|
||||
{
|
||||
string actual = language.LongName();
|
||||
var actual = language.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -248,7 +248,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateLanguageTestData))]
|
||||
public void LanguageShortNameTest(Language? language, bool expectNull)
|
||||
{
|
||||
string actual = language.ShortName();
|
||||
var actual = language.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -268,8 +268,8 @@ namespace MPF.Test.RedumpLib
|
||||
int totalCount = 0;
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
string code = language.TwoLetterCode();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
var code = language.TwoLetterCode();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
@@ -295,8 +295,8 @@ namespace MPF.Test.RedumpLib
|
||||
int totalCount = 0;
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
string code = language.ThreeLetterCode();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
var code = language.ThreeLetterCode();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
@@ -322,8 +322,8 @@ namespace MPF.Test.RedumpLib
|
||||
int totalCount = 0;
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
string code = language.ThreeLetterCodeAlt();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
var code = language.ThreeLetterCodeAlt();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
@@ -341,12 +341,12 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of Language values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of Language values</returns>
|
||||
public static List<object[]> GenerateLanguageTestData()
|
||||
public static List<object?[]> GenerateLanguageTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (Language? language in Enum.GetValues(typeof(Language)))
|
||||
{
|
||||
testData.Add(new object[] { language, false });
|
||||
testData.Add(new object?[] { language, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -365,7 +365,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateLanguageSelectionTestData))]
|
||||
public void LanguageSelectionLongNameTest(LanguageSelection? languageSelection, bool expectNull)
|
||||
{
|
||||
string actual = languageSelection.LongName();
|
||||
var actual = languageSelection.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -377,12 +377,12 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of LanguageSelection values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of LanguageSelection values</returns>
|
||||
public static List<object[]> GenerateLanguageSelectionTestData()
|
||||
public static List<object?[]> GenerateLanguageSelectionTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (LanguageSelection? languageSelection in Enum.GetValues(typeof(LanguageSelection)))
|
||||
{
|
||||
testData.Add(new object[] { languageSelection, false });
|
||||
testData.Add(new object?[] { languageSelection, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -401,7 +401,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateMediaTypeTestData))]
|
||||
public void MediaTypeLongNameTest(MediaType? mediaType, bool expectNull)
|
||||
{
|
||||
string actual = mediaType.LongName();
|
||||
var actual = mediaType.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -418,7 +418,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateMediaTypeTestData))]
|
||||
public void MediaTypeShortNameTest(MediaType? mediaType, bool expectNull)
|
||||
{
|
||||
string actual = mediaType.ShortName();
|
||||
var actual = mediaType.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -430,12 +430,12 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of MediaType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of MediaType values</returns>
|
||||
public static List<object[]> GenerateMediaTypeTestData()
|
||||
public static List<object?[]> GenerateMediaTypeTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
|
||||
{
|
||||
testData.Add(new object[] { mediaType, false });
|
||||
testData.Add(new object?[] { mediaType, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -454,7 +454,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateRegionTestData))]
|
||||
public void RegionLongNameTest(Region? region, bool expectNull)
|
||||
{
|
||||
string actual = region.LongName();
|
||||
var actual = region.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -471,7 +471,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateRegionTestData))]
|
||||
public void RegionShortNameTest(Region? region, bool expectNull)
|
||||
{
|
||||
string actual = region.ShortName();
|
||||
var actual = region.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -491,8 +491,8 @@ namespace MPF.Test.RedumpLib
|
||||
int totalCount = 0;
|
||||
foreach (Region? region in fullRegions)
|
||||
{
|
||||
string code = region.ShortName();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
var code = region.ShortName();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
@@ -510,12 +510,12 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of Region values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of Region values</returns>
|
||||
public static List<object[]> GenerateRegionTestData()
|
||||
public static List<object?[]> GenerateRegionTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (Region? region in Enum.GetValues(typeof(Region)))
|
||||
{
|
||||
testData.Add(new object[] { region, false });
|
||||
testData.Add(new object?[] { region, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -534,7 +534,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateSiteCodeTestData))]
|
||||
public void SiteCodeLongNameTest(SiteCode? siteCode, bool expectNull)
|
||||
{
|
||||
string actual = siteCode.LongName();
|
||||
var actual = siteCode.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -551,7 +551,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateSiteCodeTestData))]
|
||||
public void SiteCodeShortNameTest(SiteCode? siteCode, bool expectNull)
|
||||
{
|
||||
string actual = siteCode.ShortName();
|
||||
var actual = siteCode.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -563,12 +563,12 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of SiteCode values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of SiteCode values</returns>
|
||||
public static List<object[]> GenerateSiteCodeTestData()
|
||||
public static List<object?[]> GenerateSiteCodeTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (SiteCode? siteCode in Enum.GetValues(typeof(SiteCode)))
|
||||
{
|
||||
testData.Add(new object[] { siteCode, false });
|
||||
testData.Add(new object?[] { siteCode, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -587,7 +587,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateRedumpSystemTestData))]
|
||||
public void RedumpSystemLongNameTest(RedumpSystem? redumpSystem, bool expectNull)
|
||||
{
|
||||
string actual = redumpSystem.LongName();
|
||||
var actual = redumpSystem.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -622,16 +622,16 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of RedumpSystem values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object[]> GenerateRedumpSystemTestData()
|
||||
public static List<object?[]> GenerateRedumpSystemTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
// We want to skip all markers for this
|
||||
if (redumpSystem.IsMarker())
|
||||
continue;
|
||||
|
||||
testData.Add(new object[] { redumpSystem, false });
|
||||
testData.Add(new object?[] { redumpSystem, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -650,7 +650,7 @@ namespace MPF.Test.RedumpLib
|
||||
[MemberData(nameof(GenerateSystemCategoryTestData))]
|
||||
public void SystemCategoryLongNameTest(SystemCategory? systemCategory, bool expectNull)
|
||||
{
|
||||
string actual = systemCategory.LongName();
|
||||
var actual = systemCategory.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
@@ -662,15 +662,15 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of SystemCategory values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of SystemCategory values</returns>
|
||||
public static List<object[]> GenerateSystemCategoryTestData()
|
||||
public static List<object?[]> GenerateSystemCategoryTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, true } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (SystemCategory? systemCategory in Enum.GetValues(typeof(SystemCategory)))
|
||||
{
|
||||
if (systemCategory == SystemCategory.NONE)
|
||||
testData.Add(new object[] { systemCategory, true });
|
||||
testData.Add(new object?[] { systemCategory, true });
|
||||
else
|
||||
testData.Add(new object[] { systemCategory, false });
|
||||
testData.Add(new object?[] { systemCategory, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
@@ -701,12 +701,12 @@ namespace MPF.Test.RedumpLib
|
||||
/// Generate a test set of YesNo values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of YesNo values</returns>
|
||||
public static List<object[]> GenerateYesNoTestData()
|
||||
public static List<object?[]> GenerateYesNoTestData()
|
||||
{
|
||||
var testData = new List<object[]>() { new object[] { null, false } };
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (YesNo? yesNo in Enum.GetValues(typeof(YesNo)))
|
||||
{
|
||||
testData.Add(new object[] { yesNo, false });
|
||||
testData.Add(new object?[] { yesNo, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using psxt001z;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
@@ -82,20 +81,12 @@ 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",
|
||||
},
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Linq;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Data;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
@@ -16,7 +15,7 @@ namespace MPF.Test.Data
|
||||
public void GetAllowedDriveSpeedForMediaTypeTest(MediaType? mediaType, int maxExpected)
|
||||
{
|
||||
var actual = Interface.GetSpeedsForMediaType(mediaType);
|
||||
Assert.Equal(maxExpected, actual.Last());
|
||||
Assert.Equal(maxExpected, actual[actual.Count - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
using static MPF.Core.Data.Interface;
|
||||
|
||||
namespace MPF.UI.Core
|
||||
{
|
||||
@@ -10,18 +11,17 @@ namespace MPF.UI.Core
|
||||
/// </summary>
|
||||
public static class Constants
|
||||
{
|
||||
// Private lists of known drive speed ranges
|
||||
private static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
private static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
private static IReadOnlyList<int> 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);
|
||||
|
||||
#if NET20 || NET35 || NET40
|
||||
private static DoubleCollection GetDoubleCollectionFromIntList(IList<int> list)
|
||||
#else
|
||||
private static DoubleCollection GetDoubleCollectionFromIntList(IReadOnlyList<int> list)
|
||||
=> new DoubleCollection(list.Select(i => Convert.ToDouble(i)).ToList());
|
||||
#endif
|
||||
=> new(list.Select(i => Convert.ToDouble(i)).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
44
MPF.UI.Core/ElementConverter.cs
Normal file
44
MPF.UI.Core/ElementConverter.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ComboBoxItems;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.UI.Core
|
||||
{
|
||||
internal class ElementConverter: IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return value switch
|
||||
{
|
||||
DiscCategory discCategory => new Element<DiscCategory>(discCategory),
|
||||
InternalProgram internalProgram => new Element<InternalProgram>(internalProgram),
|
||||
MediaType mediaType => new Element<MediaType>(mediaType),
|
||||
RedumpSystem redumpSystem => new RedumpSystemComboBoxItem(redumpSystem),
|
||||
Region region => new Element<Region>(region),
|
||||
|
||||
// Null values are treated as a system value
|
||||
_ => new RedumpSystemComboBoxItem((RedumpSystem?)null),
|
||||
};
|
||||
}
|
||||
|
||||
public object? ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
// If it's an IElement but ends up null
|
||||
if (value is not IElement element)
|
||||
return null;
|
||||
|
||||
return element switch
|
||||
{
|
||||
Element<DiscCategory> dcElement => dcElement.Value,
|
||||
Element<InternalProgram> ipElement => ipElement.Value,
|
||||
Element<MediaType> mtElement => mtElement.Value,
|
||||
RedumpSystemComboBoxItem rsElement => rsElement.Value,
|
||||
Element<Region> reValue => reValue.Value,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace WPFCustomMessageBox
|
||||
/// Global parameter to enable (true) or disable (false) the removal of the title bar icon.
|
||||
/// If you are using a custom window style, the icon removal may cause issues like displaying two title bar (the default windows one and the custom one).
|
||||
/// </summary>
|
||||
public static bool RemoveTitleBarIcon = true;
|
||||
private static readonly bool RemoveTitleBarIcon = true;
|
||||
|
||||
/// <summary>
|
||||
/// Displays a message box that has a message and returns a result.
|
||||
@@ -173,7 +173,7 @@ namespace WPFCustomMessageBox
|
||||
/// </param>
|
||||
/// <param name="timeoutResult">If the message box closes automatically due to the <paramref name="timeout"/> being exceeded, this result is returned.</param>
|
||||
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
|
||||
public static MessageBoxResult Show(Window owner, string messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
public static MessageBoxResult Show(Window owner, string? messageBoxText, string caption, MessageBoxButton button, MessageBoxImage icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
@@ -515,11 +515,11 @@ namespace WPFCustomMessageBox
|
||||
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
|
||||
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
|
||||
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
|
||||
private static MessageBoxResult ShowOKMessage(Window owner, string messageBoxText, string caption, string okButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
private static MessageBoxResult ShowOKMessage(Window? owner, string? messageBoxText, string caption, string? okButtonText, string? cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
{
|
||||
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.OK : MessageBoxButton.OKCancel;
|
||||
|
||||
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
|
||||
var msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
|
||||
if (!string.IsNullOrEmpty(okButtonText))
|
||||
msg.OkButtonText = okButtonText;
|
||||
if (!string.IsNullOrEmpty(cancelButtonText))
|
||||
@@ -545,11 +545,11 @@ namespace WPFCustomMessageBox
|
||||
/// <param name="timeout">The message box will close automatically after given milliseconds</param>
|
||||
/// <param name="timeoutResult">If the message box closes automatically due to <paramref name="timeout"/> being exceeded, this result is returned.</param>
|
||||
/// <returns>A System.Windows.MessageBoxResult value that specifies which message box button is clicked by the user.</returns>
|
||||
private static MessageBoxResult ShowYesNoMessage(Window owner, string messageBoxText, string caption, string yesButtonText, string noButtonText, string cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
private static MessageBoxResult ShowYesNoMessage(Window? owner, string? messageBoxText, string caption, string? yesButtonText, string? noButtonText, string? cancelButtonText, MessageBoxImage? icon, int? timeout = null, MessageBoxResult timeoutResult = MessageBoxResult.None)
|
||||
{
|
||||
MessageBoxButton buttonLayout = string.IsNullOrEmpty(cancelButtonText) ? MessageBoxButton.YesNo : MessageBoxButton.YesNoCancel;
|
||||
|
||||
CustomMessageBoxWindow msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
|
||||
var msg = new CustomMessageBoxWindow(owner, messageBoxText, caption, buttonLayout, icon, RemoveTitleBarIcon);
|
||||
if (!string.IsNullOrEmpty(yesButtonText))
|
||||
msg.YesButtonText = yesButtonText;
|
||||
if (!string.IsNullOrEmpty(noButtonText))
|
||||
@@ -566,14 +566,14 @@ namespace WPFCustomMessageBox
|
||||
{
|
||||
if (timeout.HasValue && timeout.Value <= 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("timeout", string.Format("Timeout must be greater than 0."));
|
||||
throw new ArgumentOutOfRangeException(nameof(timeout), string.Format("Timeout must be greater than 0."));
|
||||
}
|
||||
|
||||
if (timeout.HasValue)
|
||||
{
|
||||
//System.Threading.Timer timer = null;
|
||||
//timer = new System.Threading.Timer(s => { msg.Close(); timer.Dispose(); }, null, timeout.Value, System.Threading.Timeout.Infinite);
|
||||
System.Timers.Timer timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
|
||||
var timer = new System.Timers.Timer(timeout.Value) { AutoReset = false };
|
||||
timer.Elapsed += delegate {
|
||||
Application.Current.Dispatcher.Invoke(new Action(() =>
|
||||
{
|
||||
|
||||
@@ -5,14 +5,9 @@
|
||||
WindowStartupLocation="CenterScreen"
|
||||
WindowStyle="None"
|
||||
ResizeMode="NoResize" SizeToContent="WidthAndHeight"
|
||||
TextOptions.TextFormattingMode="Display" TextOptions.TextRenderingMode="ClearType" UseLayoutRounding="True"
|
||||
Title="" MinHeight="155" MaxWidth="470" MinWidth="154"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using MPF.UI.Core;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace WPFCustomMessageBox
|
||||
{
|
||||
@@ -10,9 +14,9 @@ namespace WPFCustomMessageBox
|
||||
/// </summary>
|
||||
internal partial class CustomMessageBoxWindow : Window
|
||||
{
|
||||
private bool _removeTitleBarIcon = true;
|
||||
private readonly bool _removeTitleBarIcon = true;
|
||||
|
||||
public string Caption
|
||||
public string? Caption
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -24,78 +28,150 @@ namespace WPFCustomMessageBox
|
||||
}
|
||||
}
|
||||
|
||||
public string Message
|
||||
public string? Message
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET35
|
||||
return _TextBlock_Message!.Text;
|
||||
#else
|
||||
return TextBlock_Message.Text;
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if NET35
|
||||
_TextBlock_Message!.Text = value;
|
||||
#else
|
||||
TextBlock_Message.Text = value;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public string OkButtonText
|
||||
public string? OkButtonText
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET35
|
||||
return _Label_Ok!.Content.ToString();
|
||||
#else
|
||||
return Label_Ok.Content.ToString();
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if NET35
|
||||
_Label_Ok!.Content = value.TryAddKeyboardAccellerator();
|
||||
#else
|
||||
Label_Ok.Content = value.TryAddKeyboardAccellerator();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public string CancelButtonText
|
||||
public string? CancelButtonText
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET35
|
||||
return _Label_Cancel!.Content.ToString();
|
||||
#else
|
||||
return Label_Cancel.Content.ToString();
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if NET35
|
||||
_Label_Cancel!.Content = value.TryAddKeyboardAccellerator();
|
||||
#else
|
||||
Label_Cancel.Content = value.TryAddKeyboardAccellerator();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public string YesButtonText
|
||||
public string? YesButtonText
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET35
|
||||
return _Label_Yes!.Content.ToString();
|
||||
#else
|
||||
return Label_Yes.Content.ToString();
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if NET35
|
||||
_Label_Yes!.Content = value.TryAddKeyboardAccellerator();
|
||||
#else
|
||||
Label_Yes.Content = value.TryAddKeyboardAccellerator();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public string NoButtonText
|
||||
public string? NoButtonText
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET35
|
||||
return _Label_No!.Content.ToString();
|
||||
#else
|
||||
return Label_No.Content.ToString();
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if NET35
|
||||
_Label_No!.Content = value.TryAddKeyboardAccellerator();
|
||||
#else
|
||||
Label_No.Content = value.TryAddKeyboardAccellerator();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public MessageBoxResult Result { get; set; }
|
||||
|
||||
internal CustomMessageBoxWindow(Window owner, string message, string caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
|
||||
#if NET35
|
||||
|
||||
private Button? _Button_Cancel => ItemHelper.FindChild<Button>(this, "Button_Cancel");
|
||||
private Button? _Button_No => ItemHelper.FindChild<Button>(this, "Button_No");
|
||||
private Button? _Button_OK => ItemHelper.FindChild<Button>(this, "Button_OK");
|
||||
private Button? _Button_Yes => ItemHelper.FindChild<Button>(this, "Button_Yes");
|
||||
private System.Windows.Controls.Image? _Image_MessageBox => ItemHelper.FindChild<System.Windows.Controls.Image>(this, "Image_MessageBox");
|
||||
private Label? _Label_Cancel => ItemHelper.FindChild<Label>(this, "Label_Cancel");
|
||||
private Label? _Label_No => ItemHelper.FindChild<Label>(this, "Label_No");
|
||||
private Label? _Label_Ok => ItemHelper.FindChild<Label>(this, "Label_Ok");
|
||||
private Label? _Label_Yes => ItemHelper.FindChild<Label>(this, "Label_Yes");
|
||||
private TextBlock? _TextBlock_Message => ItemHelper.FindChild<TextBlock>(this, "TextBlock_Message");
|
||||
|
||||
#endif
|
||||
|
||||
internal CustomMessageBoxWindow(Window? owner, string? message, string? caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
System.Windows.Media.TextOptions.SetTextFormattingMode(this, System.Windows.Media.TextFormattingMode.Display);
|
||||
System.Windows.Media.TextOptions.SetTextRenderingMode(this, System.Windows.Media.TextRenderingMode.ClearType);
|
||||
UseLayoutRounding = true;
|
||||
#endif
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
var chrome = new System.Windows.Shell.WindowChrome
|
||||
{
|
||||
CaptionHeight = 0,
|
||||
ResizeBorderThickness = new Thickness(0),
|
||||
};
|
||||
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
|
||||
#endif
|
||||
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
_removeTitleBarIcon = removeTitleBarIcon;
|
||||
Focusable = true;
|
||||
ShowActivated = true;
|
||||
ShowInTaskbar = true;
|
||||
|
||||
if (owner != null)
|
||||
if (owner != null && owner.IsLoaded)
|
||||
{
|
||||
Owner = owner;
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
@@ -109,7 +185,11 @@ namespace WPFCustomMessageBox
|
||||
if (image.HasValue)
|
||||
DisplayImage(image.Value);
|
||||
else
|
||||
Image_MessageBox.Visibility = System.Windows.Visibility.Collapsed;
|
||||
#if NET35
|
||||
_Image_MessageBox!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
Image_MessageBox.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void OnSourceInitialized(EventArgs e)
|
||||
@@ -126,39 +206,75 @@ namespace WPFCustomMessageBox
|
||||
{
|
||||
case MessageBoxButton.OKCancel:
|
||||
// Hide all but OK, Cancel
|
||||
Button_OK.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_OK.Focus();
|
||||
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
|
||||
#if NET35
|
||||
_Button_OK!.Visibility = Visibility.Visible;
|
||||
_Button_OK.Focus();
|
||||
_Button_Cancel!.Visibility = Visibility.Visible;
|
||||
|
||||
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_No.Visibility = System.Windows.Visibility.Collapsed;
|
||||
_Button_Yes!.Visibility = Visibility.Collapsed;
|
||||
_Button_No!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
Button_OK.Visibility = Visibility.Visible;
|
||||
Button_OK.Focus();
|
||||
Button_Cancel.Visibility = Visibility.Visible;
|
||||
|
||||
Button_Yes.Visibility = Visibility.Collapsed;
|
||||
Button_No.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
break;
|
||||
case MessageBoxButton.YesNo:
|
||||
// Hide all but Yes, No
|
||||
Button_Yes.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_Yes.Focus();
|
||||
Button_No.Visibility = System.Windows.Visibility.Visible;
|
||||
#if NET35
|
||||
_Button_Yes!.Visibility = Visibility.Visible;
|
||||
_Button_Yes.Focus();
|
||||
_Button_No!.Visibility = Visibility.Visible;
|
||||
|
||||
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
|
||||
_Button_OK!.Visibility = Visibility.Collapsed;
|
||||
_Button_Cancel!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
Button_Yes.Visibility = Visibility.Visible;
|
||||
Button_Yes.Focus();
|
||||
Button_No.Visibility = Visibility.Visible;
|
||||
|
||||
Button_OK.Visibility = Visibility.Collapsed;
|
||||
Button_Cancel.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
break;
|
||||
case MessageBoxButton.YesNoCancel:
|
||||
// Hide only OK
|
||||
Button_Yes.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_Yes.Focus();
|
||||
Button_No.Visibility = System.Windows.Visibility.Visible;
|
||||
Button_Cancel.Visibility = System.Windows.Visibility.Visible;
|
||||
#if NET35
|
||||
_Button_Yes!.Visibility = Visibility.Visible;
|
||||
_Button_Yes.Focus();
|
||||
_Button_No!.Visibility = Visibility.Visible;
|
||||
_Button_Cancel!.Visibility = Visibility.Visible;
|
||||
|
||||
Button_OK.Visibility = System.Windows.Visibility.Collapsed;
|
||||
_Button_OK!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
Button_Yes.Visibility = Visibility.Visible;
|
||||
Button_Yes.Focus();
|
||||
Button_No.Visibility = Visibility.Visible;
|
||||
Button_Cancel.Visibility = Visibility.Visible;
|
||||
|
||||
Button_OK.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
// Hide all but OK
|
||||
Button_OK.Visibility = System.Windows.Visibility.Visible;
|
||||
#if NET35
|
||||
_Button_OK!.Visibility = Visibility.Visible;
|
||||
_Button_OK.Focus();
|
||||
|
||||
_Button_Yes!.Visibility = Visibility.Collapsed;
|
||||
_Button_No!.Visibility = Visibility.Collapsed;
|
||||
_Button_Cancel!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
Button_OK.Visibility = Visibility.Visible;
|
||||
Button_OK.Focus();
|
||||
|
||||
Button_Yes.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_No.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_Cancel.Visibility = System.Windows.Visibility.Collapsed;
|
||||
Button_Yes.Visibility = Visibility.Collapsed;
|
||||
Button_No.Visibility = Visibility.Collapsed;
|
||||
Button_Cancel.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -186,8 +302,13 @@ namespace WPFCustomMessageBox
|
||||
break;
|
||||
}
|
||||
|
||||
#if NET35
|
||||
_Image_MessageBox!.Source = icon.ToImageSource();
|
||||
_Image_MessageBox.Visibility = Visibility.Visible;
|
||||
#else
|
||||
Image_MessageBox.Source = icon.ToImageSource();
|
||||
Image_MessageBox.Visibility = System.Windows.Visibility.Visible;
|
||||
Image_MessageBox.Visibility = Visibility.Visible;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void Button_OK_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace WPFCustomMessageBox
|
||||
const int SWP_NOMOVE = 0x0002;
|
||||
const int SWP_NOZORDER = 0x0004;
|
||||
const int SWP_FRAMECHANGED = 0x0020;
|
||||
const uint WM_SETICON = 0x0080;
|
||||
//const uint WM_SETICON = 0x0080;
|
||||
|
||||
|
||||
internal static ImageSource ToImageSource(this Icon icon)
|
||||
@@ -60,7 +60,7 @@ namespace WPFCustomMessageBox
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
internal static string TryAddKeyboardAccellerator(this string input)
|
||||
internal static string? TryAddKeyboardAccellerator(this string? input)
|
||||
{
|
||||
if (input == null)
|
||||
return input;
|
||||
@@ -84,7 +84,7 @@ namespace WPFCustomMessageBox
|
||||
IntPtr hwnd = new WindowInteropHelper(window).Handle;
|
||||
// Change the extended window style to not show a window icon
|
||||
int extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
|
||||
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
|
||||
_ = SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
|
||||
// Update the window's non-client area to reflect the changes
|
||||
SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
|
||||
}
|
||||
|
||||
124
MPF.UI.Core/ItemHelper.cs
Normal file
124
MPF.UI.Core/ItemHelper.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows;
|
||||
|
||||
namespace MPF.UI.Core
|
||||
{
|
||||
internal static class ItemHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds a Child of a given item in the visual tree.
|
||||
/// </summary>
|
||||
/// <param name="parent">A direct parent of the queried item.</param>
|
||||
/// <typeparam name="T">The type of the queried item.</typeparam>
|
||||
/// <param name="childName">x:Name or Name of child. </param>
|
||||
/// <returns>The first parent item that matches the submitted type parameter.
|
||||
/// If not matching item can be found,
|
||||
/// a null parent is being returned.</returns>
|
||||
public static T? FindChild<T>(DependencyObject? parent, string childName) where T : DependencyObject
|
||||
{
|
||||
// Confirm parent and childName are valid.
|
||||
if (parent == null) return null;
|
||||
|
||||
T? foundChild = null;
|
||||
|
||||
if (parent is ItemsControl itemsControl && itemsControl.Items != null)
|
||||
{
|
||||
int childrenCount = itemsControl.Items.Count;
|
||||
for (int i = 0; i < childrenCount; i++)
|
||||
{
|
||||
var child = itemsControl.Items[i] as DependencyObject;
|
||||
|
||||
// If the child is not of the request child type child
|
||||
if (child is not T)
|
||||
{
|
||||
// recursively drill down the tree
|
||||
foundChild = FindChild<T>(child, childName);
|
||||
|
||||
// If the child is found, break so we do not overwrite the found child.
|
||||
if (foundChild != null)
|
||||
break;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(childName))
|
||||
{
|
||||
// If the child's name is set for search
|
||||
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
|
||||
{
|
||||
// if the child's name is of the request name
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// child element found.
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (parent is ContentControl contentControl && contentControl.Content != null)
|
||||
{
|
||||
var child = contentControl.Content as DependencyObject;
|
||||
|
||||
// If the child is not of the request child type child
|
||||
if (child is not T)
|
||||
{
|
||||
// recursively drill down the tree
|
||||
foundChild = FindChild<T>(child, childName);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(childName))
|
||||
{
|
||||
// If the child's name is set for search
|
||||
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
|
||||
{
|
||||
// if the child's name is of the request name
|
||||
foundChild = (T)child;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// child element found.
|
||||
foundChild = (T)child;
|
||||
}
|
||||
}
|
||||
else if (parent is Visual)
|
||||
{
|
||||
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
|
||||
for (int i = 0; i < childrenCount; i++)
|
||||
{
|
||||
var child = VisualTreeHelper.GetChild(parent, i);
|
||||
|
||||
// If the child is not of the request child type child
|
||||
if (child is not T)
|
||||
{
|
||||
// recursively drill down the tree
|
||||
foundChild = FindChild<T>(child, childName);
|
||||
|
||||
// If the child is found, break so we do not overwrite the found child.
|
||||
if (foundChild != null)
|
||||
break;
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(childName))
|
||||
{
|
||||
// If the child's name is set for search
|
||||
if (child is FrameworkElement frameworkElement && frameworkElement.Name == childName)
|
||||
{
|
||||
// if the child's name is of the request name
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// child element found.
|
||||
foundChild = (T)child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundChild;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net48;net6.0-windows;net7.0-windows</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0-windows;net6.0-windows;net7.0-windows;net8.0-windows</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<ImportFrameworkWinFXTargets Condition="$(TargetFramework.StartsWith(`net3`))">true</ImportFrameworkWinFXTargets>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<Version>2.6.6</Version>
|
||||
</PropertyGroup>
|
||||
<VersionPrefix>3.1.4</VersionPrefix>
|
||||
|
||||
<PropertyGroup>
|
||||
<NrtRevisionFormat>$(Version)-{chash:8}</NrtRevisionFormat>
|
||||
<NrtResolveSimpleAttributes>true</NrtResolveSimpleAttributes>
|
||||
<NrtShowRevision>false</NrtShowRevision>
|
||||
<UserSecretsId>27abb4ca-bf7a-431e-932f-49153303d5ff</UserSecretsId>
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Common code for all MPF UI implementations</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -24,23 +32,18 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
<ProjectReference Include="..\MPF.Library\MPF.Library.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)'=='net48'">
|
||||
<Reference Include="PresentationFramework.Aero" />
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net3`))">
|
||||
<Reference Include="PresentationBuildTasks" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationBuildTasks.dll" />
|
||||
<Reference Include="PresentationCore" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll" />
|
||||
<Reference Include="PresentationFramework" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll" />
|
||||
<Reference Include="WindowsBase" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.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\" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -6,39 +6,39 @@ namespace MPF.UI.Core
|
||||
/// <summary>
|
||||
/// Represents all required mapping values for the UI
|
||||
/// </summary>
|
||||
public class Theme
|
||||
public abstract class Theme
|
||||
{
|
||||
#region Application-Wide
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush used to paint the active window's border.
|
||||
/// </summary>
|
||||
public SolidColorBrush ActiveBorderBrush { get; set; }
|
||||
public SolidColorBrush? ActiveBorderBrush { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints the face of a three-dimensional display element.
|
||||
/// </summary>
|
||||
public SolidColorBrush ControlBrush { get; set; }
|
||||
public SolidColorBrush? ControlBrush { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints text in a three-dimensional display element.
|
||||
/// </summary>
|
||||
public SolidColorBrush ControlTextBrush { get; set; }
|
||||
public SolidColorBrush? ControlTextBrush { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints disabled text.
|
||||
/// </summary>
|
||||
public SolidColorBrush GrayTextBrush { get; set; }
|
||||
public SolidColorBrush? GrayTextBrush { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints the background of a window's client area.
|
||||
/// </summary>
|
||||
public SolidColorBrush WindowBrush { get; set; }
|
||||
public SolidColorBrush? WindowBrush { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// SolidColorBrush that paints the text in the client area of a window.
|
||||
/// </summary>
|
||||
public SolidColorBrush WindowTextBrush { get; set; }
|
||||
public SolidColorBrush? WindowTextBrush { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -47,22 +47,22 @@ namespace MPF.UI.Core
|
||||
/// <summary>
|
||||
/// Brush for the Button.Disabled.Background resource
|
||||
/// </summary>
|
||||
public Brush Button_Disabled_Background { get; set; }
|
||||
public Brush? Button_Disabled_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.MouseOver.Background resource
|
||||
/// </summary>
|
||||
public Brush Button_MouseOver_Background { get; set; }
|
||||
public Brush? Button_MouseOver_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.Pressed.Background resource
|
||||
/// </summary>
|
||||
public Brush Button_Pressed_Background { get; set; }
|
||||
public Brush? Button_Pressed_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the Button.Static.Background resource
|
||||
/// </summary>
|
||||
public Brush Button_Static_Background { get; set; }
|
||||
public Brush? Button_Static_Background { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -71,62 +71,62 @@ namespace MPF.UI.Core
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Disabled.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Disabled_Background { get; set; }
|
||||
public Brush? ComboBox_Disabled_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Disabled.Editable.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Disabled_Editable_Background { get; set; }
|
||||
public Brush? ComboBox_Disabled_Editable_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Disabled.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Disabled_Editable_Button_Background { get; set; }
|
||||
public Brush? ComboBox_Disabled_Editable_Button_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.MouseOver.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_MouseOver_Background { get; set; }
|
||||
public Brush? ComboBox_MouseOver_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.MouseOver.Editable.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_MouseOver_Editable_Background { get; set; }
|
||||
public Brush? ComboBox_MouseOver_Editable_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.MouseOver.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_MouseOver_Editable_Button_Background { get; set; }
|
||||
public Brush? ComboBox_MouseOver_Editable_Button_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Pressed.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Pressed_Background { get; set; }
|
||||
public Brush? ComboBox_Pressed_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Pressed.Editable.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Pressed_Editable_Background { get; set; }
|
||||
public Brush? ComboBox_Pressed_Editable_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Pressed.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Pressed_Editable_Button_Background { get; set; }
|
||||
public Brush? ComboBox_Pressed_Editable_Button_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Static.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Static_Background { get; set; }
|
||||
public Brush? ComboBox_Static_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Static.Editable.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Static_Editable_Background { get; set; }
|
||||
public Brush? ComboBox_Static_Editable_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the ComboBox.Static.Editable.Button.Background resource
|
||||
/// </summary>
|
||||
public Brush ComboBox_Static_Editable_Button_Background { get; set; }
|
||||
public Brush? ComboBox_Static_Editable_Button_Background { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -135,7 +135,7 @@ namespace MPF.UI.Core
|
||||
/// <summary>
|
||||
/// Brush for the CustomMessageBox.Static.Background resource
|
||||
/// </summary>
|
||||
public Brush CustomMessageBox_Static_Background { get; set; }
|
||||
public Brush? CustomMessageBox_Static_Background { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -144,12 +144,12 @@ namespace MPF.UI.Core
|
||||
/// <summary>
|
||||
/// Brush for the MenuItem.SubMenu.Background resource
|
||||
/// </summary>
|
||||
public Brush MenuItem_SubMenu_Background { get; set; }
|
||||
public Brush? MenuItem_SubMenu_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the MenuItem.SubMenu.Border resource
|
||||
/// </summary>
|
||||
public Brush MenuItem_SubMenu_Border { get; set; }
|
||||
public Brush? MenuItem_SubMenu_Border { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -158,7 +158,7 @@ namespace MPF.UI.Core
|
||||
/// <summary>
|
||||
/// Brush for the ProgressBar.Background resource
|
||||
/// </summary>
|
||||
public Brush ProgressBar_Background { get; set; }
|
||||
public Brush? ProgressBar_Background { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace MPF.UI.Core
|
||||
/// <summary>
|
||||
/// Brush for the ScrollViewer.ScrollBar.Background resource
|
||||
/// </summary>
|
||||
public Brush ScrollViewer_ScrollBar_Background { get; set; }
|
||||
public Brush? ScrollViewer_ScrollBar_Background { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -176,17 +176,17 @@ namespace MPF.UI.Core
|
||||
/// <summary>
|
||||
/// Brush for the TabItem.Selected.Background resource
|
||||
/// </summary>
|
||||
public Brush TabItem_Selected_Background { get; set; }
|
||||
public Brush? TabItem_Selected_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the TabItem.Static.Background resource
|
||||
/// </summary>
|
||||
public Brush TabItem_Static_Background { get; set; }
|
||||
public Brush? TabItem_Static_Background { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Brush for the TabItem.Static.Border resource
|
||||
/// </summary>
|
||||
public Brush TabItem_Static_Border { get; set; }
|
||||
public Brush? TabItem_Static_Border { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -195,7 +195,7 @@ namespace MPF.UI.Core
|
||||
/// <summary>
|
||||
/// Brush for the TextBox.Static.Background resource
|
||||
/// </summary>
|
||||
public Brush TextBox_Static_Background { get; set; }
|
||||
public Brush? TextBox_Static_Background { get; protected set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -1,19 +1,241 @@
|
||||
using System.Windows.Controls;
|
||||
using MPF.UI.Core.ViewModels;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Threading;
|
||||
using MPF.Core.Data;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.UserControls
|
||||
{
|
||||
public partial class LogOutput : UserControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Read-only access to the current log view model
|
||||
/// Document representing the text
|
||||
/// </summary>
|
||||
public LogViewModel LogViewModel => DataContext as LogViewModel;
|
||||
internal FlowDocument Document { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Queue of items that need to be logged
|
||||
/// </summary>
|
||||
internal ProcessingQueue<LogLine> LogQueue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Paragraph backing the log
|
||||
/// </summary>
|
||||
private readonly Paragraph _paragraph;
|
||||
|
||||
/// <summary>
|
||||
/// Cached value of the last line written
|
||||
/// </summary>
|
||||
private Run? lastLine = null;
|
||||
|
||||
#if NET35
|
||||
|
||||
private Button? _ClearButton => ItemHelper.FindChild<Button>(this, "ClearButton");
|
||||
private RichTextBox? _Output => ItemHelper.FindChild<RichTextBox>(this, "Output");
|
||||
private ScrollViewer? _OutputViewer => ItemHelper.FindChild<ScrollViewer>(this, "OutputViewer");
|
||||
private Button? _SaveButton => ItemHelper.FindChild<Button>(this, "SaveButton");
|
||||
|
||||
#endif
|
||||
|
||||
public LogOutput()
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
DataContext = new LogViewModel(this);
|
||||
#endif
|
||||
|
||||
// Update the internal state
|
||||
Document = new FlowDocument()
|
||||
{
|
||||
Background = new SolidColorBrush(Color.FromArgb(0xFF, 0x20, 0x20, 0x20))
|
||||
};
|
||||
_paragraph = new Paragraph();
|
||||
Document.Blocks.Add(_paragraph);
|
||||
|
||||
// Setup the processing queue
|
||||
LogQueue = new ProcessingQueue<LogLine>(ProcessLogLine);
|
||||
|
||||
// Add handlers
|
||||
#if NET35
|
||||
_OutputViewer!.SizeChanged += OutputViewerSizeChanged;
|
||||
_Output!.TextChanged += OnTextChanged;
|
||||
_ClearButton!.Click += OnClearButton;
|
||||
_SaveButton!.Click += OnSaveButton;
|
||||
#else
|
||||
OutputViewer.SizeChanged += OutputViewerSizeChanged;
|
||||
Output.TextChanged += OnTextChanged;
|
||||
ClearButton.Click += OnClearButton;
|
||||
SaveButton.Click += OnSaveButton;
|
||||
#endif
|
||||
|
||||
// Update the internal state
|
||||
#if NET35
|
||||
_Output.Document = Document;
|
||||
#else
|
||||
Output.Document = Document;
|
||||
#endif
|
||||
}
|
||||
|
||||
#region Logging
|
||||
|
||||
/// <summary>
|
||||
/// Enqueue text to the log with formatting
|
||||
/// </summary>
|
||||
/// <param name="logLevel">LogLevel for the log</param>
|
||||
/// <param name="text">Text to write to the log</param>
|
||||
public void EnqueueLog(LogLevel logLevel, string text)
|
||||
{
|
||||
// Null text gets ignored
|
||||
if (text == null)
|
||||
return;
|
||||
|
||||
// Enqueue the text
|
||||
LogQueue.Enqueue(new LogLine(text, logLevel));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log line wrapper
|
||||
/// </summary>
|
||||
internal readonly struct LogLine
|
||||
{
|
||||
public readonly string Text;
|
||||
public readonly LogLevel LogLevel;
|
||||
|
||||
public LogLine(string text, LogLevel logLevel)
|
||||
{
|
||||
this.Text = text;
|
||||
this.LogLevel = logLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the foreground Brush for the current LogLevel
|
||||
/// </summary>
|
||||
/// <returns>Brush representing the color</returns>
|
||||
public Brush GetForegroundColor()
|
||||
{
|
||||
return this.LogLevel switch
|
||||
{
|
||||
LogLevel.SECRET => Brushes.Blue,
|
||||
LogLevel.ERROR => Brushes.Red,
|
||||
LogLevel.VERBOSE => Brushes.Yellow,
|
||||
_ => Brushes.White,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a Run object from the current LogLine
|
||||
/// </summary>
|
||||
/// <returns>Run object based on internal values</returns>
|
||||
public Run GenerateRun()
|
||||
{
|
||||
return new Run { Text = this.Text, Foreground = GetForegroundColor() };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process the log lines in the queue
|
||||
/// </summary>
|
||||
/// <param name="nextLogLine">LogLine item to process</param>
|
||||
internal void ProcessLogLine(LogLine nextLogLine)
|
||||
{
|
||||
// Null text gets ignored
|
||||
string nextText = nextLogLine.Text;
|
||||
if (nextText == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (nextText.StartsWith("\r"))
|
||||
ReplaceLastLine(nextLogLine);
|
||||
else
|
||||
AppendToTextBox(nextLogLine);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// In the event that something fails horribly, we want to log
|
||||
AppendToTextBox(new LogLine(ex.ToString(), LogLevel.ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Append log line to the log text box
|
||||
/// </summary>
|
||||
/// <param name="logLine">LogLine value to append</param>
|
||||
private void AppendToTextBox(LogLine logLine)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
var run = logLine.GenerateRun();
|
||||
_paragraph.Inlines.Add(run);
|
||||
lastLine = run;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace the last line written to the log text box
|
||||
/// </summary>
|
||||
/// <param name="logLine">LogLine value to append</param>
|
||||
private void ReplaceLastLine(LogLine logLine)
|
||||
{
|
||||
Dispatcher.Invoke(() =>
|
||||
{
|
||||
lastLine ??= new Run();
|
||||
lastLine.Text = logLine.Text;
|
||||
lastLine.Foreground = logLine.GetForegroundColor();
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Clear all inlines of the paragraph
|
||||
/// </summary>
|
||||
private void ClearInlines() => _paragraph.Inlines.Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Save all inlines to console.log
|
||||
/// </summary>
|
||||
private void SaveInlines()
|
||||
{
|
||||
using var sw = new StreamWriter(File.OpenWrite("console.log"));
|
||||
foreach (var inline in _paragraph.Inlines)
|
||||
{
|
||||
if (inline is Run run)
|
||||
sw.Write(run.Text);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scroll the current view to the bottom
|
||||
/// </summary>
|
||||
#if NET35
|
||||
public void ScrollToBottom() => _OutputViewer!.ScrollToBottom();
|
||||
#else
|
||||
public void ScrollToBottom() => OutputViewer.ScrollToBottom();
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region EventHandlers
|
||||
|
||||
private void OnClearButton(object sender, EventArgs e)
|
||||
=> ClearInlines();
|
||||
|
||||
private void OnSaveButton(object sender, EventArgs e)
|
||||
=> SaveInlines();
|
||||
|
||||
private void OnTextChanged(object sender, TextChangedEventArgs e)
|
||||
=> ScrollToBottom();
|
||||
|
||||
private void OutputViewerSizeChanged(object sender, SizeChangedEventArgs e)
|
||||
=> ScrollToBottom();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace MPF.UI.Core.UserControls
|
||||
|
||||
public static readonly DependencyProperty VerticalScrollBarVisibilityProperty =
|
||||
DependencyProperty.Register("VerticalScrollBarVisibility", typeof(ScrollBarVisibility), typeof(UserInput));
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -118,7 +118,9 @@ namespace MPF.UI.Core.UserControls
|
||||
HorizontalScrollBarVisibility = ScrollBarVisibility.Hidden;
|
||||
VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
|
||||
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,850 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.UI.Core.ComboBoxItems;
|
||||
using MPF.UI.Core.Windows;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.UI.Core.ViewModels
|
||||
{
|
||||
public class DiscInformationViewModel
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Parent DiscInformationWindow object
|
||||
/// </summary>
|
||||
public DiscInformationWindow Parent { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Application-level Options object
|
||||
/// </summary>
|
||||
public Options Options { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// SubmissionInfo object to fill and save
|
||||
/// </summary>
|
||||
public SubmissionInfo SubmissionInfo { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lists
|
||||
|
||||
/// <summary>
|
||||
/// List of available disc categories
|
||||
/// </summary>
|
||||
public List<Element<DiscCategory>> Categories { get; private set; } = Element<DiscCategory>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of available regions
|
||||
/// </summary>
|
||||
public List<Element<Region>> Regions { get; private set; } = Element<Region>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of Redump-supported Regions
|
||||
/// </summary>
|
||||
private static readonly List<Region> RedumpRegions = new List<Region>
|
||||
{
|
||||
Region.Argentina,
|
||||
Region.Asia,
|
||||
Region.AsiaEurope,
|
||||
Region.AsiaUSA,
|
||||
Region.Australia,
|
||||
Region.AustraliaGermany,
|
||||
Region.AustraliaNewZealand,
|
||||
Region.Austria,
|
||||
Region.AustriaSwitzerland,
|
||||
Region.Belarus,
|
||||
Region.Belgium,
|
||||
Region.BelgiumNetherlands,
|
||||
Region.Brazil,
|
||||
Region.Bulgaria,
|
||||
Region.Canada,
|
||||
Region.China,
|
||||
Region.Croatia,
|
||||
Region.Czechia,
|
||||
Region.Denmark,
|
||||
Region.Estonia,
|
||||
Region.Europe,
|
||||
Region.EuropeAsia,
|
||||
Region.EuropeAustralia,
|
||||
Region.EuropeCanada,
|
||||
Region.EuropeGermany,
|
||||
Region.Export,
|
||||
Region.Finland,
|
||||
Region.France,
|
||||
Region.FranceSpain,
|
||||
Region.Germany,
|
||||
Region.GreaterChina,
|
||||
Region.Greece,
|
||||
Region.Hungary,
|
||||
Region.Iceland,
|
||||
Region.India,
|
||||
Region.Ireland,
|
||||
Region.Israel,
|
||||
Region.Italy,
|
||||
Region.Japan,
|
||||
Region.JapanAsia,
|
||||
Region.JapanEurope,
|
||||
Region.JapanKorea,
|
||||
Region.JapanUSA,
|
||||
Region.SouthKorea,
|
||||
Region.LatinAmerica,
|
||||
Region.Lithuania,
|
||||
Region.Netherlands,
|
||||
Region.NewZealand,
|
||||
Region.Norway,
|
||||
Region.Poland,
|
||||
Region.Portugal,
|
||||
Region.Romania,
|
||||
Region.RussianFederation,
|
||||
Region.Scandinavia,
|
||||
Region.Serbia,
|
||||
Region.Singapore,
|
||||
Region.Slovakia,
|
||||
Region.SouthAfrica,
|
||||
Region.Spain,
|
||||
Region.SpainPortugal,
|
||||
Region.Sweden,
|
||||
Region.Switzerland,
|
||||
Region.Taiwan,
|
||||
Region.Thailand,
|
||||
Region.Turkey,
|
||||
Region.UnitedArabEmirates,
|
||||
Region.UnitedKingdom,
|
||||
Region.UKAustralia,
|
||||
Region.Ukraine,
|
||||
Region.UnitedStatesOfAmerica,
|
||||
Region.USAAsia,
|
||||
Region.USAAustralia,
|
||||
Region.USABrazil,
|
||||
Region.USACanada,
|
||||
Region.USAEurope,
|
||||
Region.USAGermany,
|
||||
Region.USAJapan,
|
||||
Region.USAKorea,
|
||||
Region.World,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// List of available languages
|
||||
/// </summary>
|
||||
public List<Element<Language>> Languages { get; private set; } = Element<Language>.GenerateElements().ToList();
|
||||
|
||||
/// <summary>
|
||||
/// List of Redump-supported Languages
|
||||
/// </summary>
|
||||
private static readonly List<Language> RedumpLanguages = new List<Language>
|
||||
{
|
||||
Language.Afrikaans,
|
||||
Language.Albanian,
|
||||
Language.Arabic,
|
||||
Language.Armenian,
|
||||
Language.Basque,
|
||||
Language.Belarusian,
|
||||
Language.Bulgarian,
|
||||
Language.Catalan,
|
||||
Language.Chinese,
|
||||
Language.Croatian,
|
||||
Language.Czech,
|
||||
Language.Danish,
|
||||
Language.Dutch,
|
||||
Language.English,
|
||||
Language.Estonian,
|
||||
Language.Finnish,
|
||||
Language.French,
|
||||
Language.Gaelic,
|
||||
Language.German,
|
||||
Language.Greek,
|
||||
Language.Hebrew,
|
||||
Language.Hindi,
|
||||
Language.Hungarian,
|
||||
Language.Icelandic,
|
||||
Language.Indonesian,
|
||||
Language.Italian,
|
||||
Language.Japanese,
|
||||
Language.Korean,
|
||||
Language.Latin,
|
||||
Language.Latvian,
|
||||
Language.Lithuanian,
|
||||
Language.Macedonian,
|
||||
Language.Norwegian,
|
||||
Language.Polish,
|
||||
Language.Portuguese,
|
||||
Language.Panjabi,
|
||||
Language.Romanian,
|
||||
Language.Russian,
|
||||
Language.Serbian,
|
||||
Language.Slovak,
|
||||
Language.Slovenian,
|
||||
Language.Spanish,
|
||||
Language.Swedish,
|
||||
Language.Tamil,
|
||||
Language.Thai,
|
||||
Language.Turkish,
|
||||
Language.Ukrainian,
|
||||
Language.Vietnamese,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// List of available languages
|
||||
/// </summary>
|
||||
public List<Element<LanguageSelection>> LanguageSelections { get; private set; } = Element<LanguageSelection>.GenerateElements().ToList();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public DiscInformationViewModel(DiscInformationWindow parent, Options options, SubmissionInfo submissionInfo)
|
||||
{
|
||||
Parent = parent;
|
||||
Options = options;
|
||||
SubmissionInfo = submissionInfo.Clone() as SubmissionInfo ?? new SubmissionInfo();
|
||||
|
||||
// Limit lists, if necessary
|
||||
if (this.Options.EnableRedumpCompatibility)
|
||||
{
|
||||
SetRedumpRegions();
|
||||
SetRedumpLanguages();
|
||||
}
|
||||
|
||||
// Add handlers
|
||||
Parent.AcceptButton.Click += OnAcceptClick;
|
||||
Parent.CancelButton.Click += OnCancelClick;
|
||||
Parent.RingCodeGuideButton.Click += OnRingCodeGuideClick;
|
||||
|
||||
// Update UI with new values
|
||||
ManipulateFields();
|
||||
Load();
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Enable tab entry on supported fields
|
||||
/// </summary>
|
||||
private void EnableTabsInInputFields()
|
||||
{
|
||||
// Additional Information
|
||||
Parent.CommentsTextBox.Tab = true;
|
||||
|
||||
// Contents
|
||||
Parent.GeneralContent.Tab = true;
|
||||
Parent.GamesTextBox.Tab = true;
|
||||
Parent.NetYarozeGamesTextBox.Tab = true;
|
||||
Parent.PlayableDemosTextBox.Tab = true;
|
||||
Parent.RollingDemosTextBox.Tab = true;
|
||||
Parent.TechDemosTextBox.Tab = true;
|
||||
Parent.GameFootageTextBox.Tab = true;
|
||||
Parent.VideosTextBox.Tab = true;
|
||||
Parent.PatchesTextBox.Tab = true;
|
||||
Parent.SavegamesTextBox.Tab = true;
|
||||
Parent.ExtrasTextBox.Tab = true;
|
||||
|
||||
// L0
|
||||
Parent.L0MasteringRing.Tab = true;
|
||||
Parent.L0MasteringSID.Tab = true;
|
||||
Parent.L0Toolstamp.Tab = true;
|
||||
Parent.L0MouldSID.Tab = true;
|
||||
Parent.L0AdditionalMould.Tab = true;
|
||||
|
||||
// L1
|
||||
Parent.L1MasteringRing.Tab = true;
|
||||
Parent.L1MasteringSID.Tab = true;
|
||||
Parent.L1Toolstamp.Tab = true;
|
||||
Parent.L1MouldSID.Tab = true;
|
||||
Parent.L1AdditionalMould.Tab = true;
|
||||
|
||||
// L2
|
||||
Parent.L2MasteringRing.Tab = true;
|
||||
Parent.L2MasteringSID.Tab = true;
|
||||
Parent.L2Toolstamp.Tab = true;
|
||||
|
||||
// L3
|
||||
Parent.L3MasteringRing.Tab = true;
|
||||
Parent.L3MasteringSID.Tab = true;
|
||||
Parent.L3Toolstamp.Tab = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide any optional, read-only fields if they don't have a value
|
||||
/// </summary>
|
||||
private void HideReadOnlyFields()
|
||||
{
|
||||
if (SubmissionInfo?.FullyMatchedID == null)
|
||||
Parent.FullyMatchedID.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.FullyMatchedID.Text = SubmissionInfo.FullyMatchedID.ToString();
|
||||
if (SubmissionInfo?.PartiallyMatchedIDs == null)
|
||||
Parent.PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.PartiallyMatchedIDs.Text = string.Join(", ", SubmissionInfo.PartiallyMatchedIDs);
|
||||
if (SubmissionInfo?.CopyProtection?.AntiModchip == null)
|
||||
Parent.AntiModchip.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.AntiModchip.Text = SubmissionInfo.CopyProtection.AntiModchip.LongName();
|
||||
if (SubmissionInfo?.TracksAndWriteOffsets?.OtherWriteOffsets == null)
|
||||
Parent.DiscOffset.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.DiscOffset.Text = SubmissionInfo.TracksAndWriteOffsets.OtherWriteOffsets;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.DMIHash) != true)
|
||||
Parent.DMIHash.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.EDC?.EDC == null)
|
||||
Parent.EDC.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.EDC.Text = SubmissionInfo.EDC.EDC.LongName();
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CommonDiscInfo?.ErrorsCount))
|
||||
Parent.ErrorsCount.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CommonDiscInfo?.EXEDateBuildDate))
|
||||
Parent.EXEDateBuildDate.Visibility = Visibility.Collapsed;
|
||||
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?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.Multisession) != true)
|
||||
Parent.Multisession.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CopyProtection?.LibCrypt == null)
|
||||
Parent.LibCrypt.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
Parent.LibCrypt.Text = SubmissionInfo.CopyProtection.LibCrypt.LongName();
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.LibCryptData))
|
||||
Parent.LibCryptData.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.PFIHash) != true)
|
||||
Parent.PFIHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.PIC))
|
||||
Parent.PIC.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.PVD))
|
||||
Parent.PVD.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.RingNonZeroDataStart) != true)
|
||||
Parent.RingNonZeroDataStart.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.CopyProtection?.SecuROMData))
|
||||
Parent.SecuROMData.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSHash) != true)
|
||||
Parent.SSHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(SubmissionInfo?.Extras?.SecuritySectorRanges))
|
||||
Parent.SecuritySectorRanges.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.SSVersion) != true)
|
||||
Parent.SSVersion.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.UniversalHash) != true)
|
||||
Parent.UniversalHash.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.VolumeLabel) != true)
|
||||
Parent.VolumeLabel.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.XeMID) != true)
|
||||
Parent.XeMID.Visibility = Visibility.Collapsed;
|
||||
if (SubmissionInfo?.CommonDiscInfo?.CommentsSpecialFields.Keys.Contains(SiteCode.XMID) != true)
|
||||
Parent.XMID.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manipulate fields based on the current disc
|
||||
/// </summary>
|
||||
private void ManipulateFields()
|
||||
{
|
||||
// Enable tabs in all fields, if required
|
||||
if (this.Options.EnableTabsInInputFields)
|
||||
EnableTabsInInputFields();
|
||||
|
||||
// Hide read-only fields that don't have values set
|
||||
HideReadOnlyFields();
|
||||
|
||||
// Different media types mean different fields available
|
||||
UpdateFromDiscType();
|
||||
|
||||
// Different systems mean different fields available
|
||||
UpdateFromSystemType();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the current contents of the base SubmissionInfo to the UI
|
||||
/// </summary>
|
||||
private void Load()
|
||||
{
|
||||
Parent.CategoryComboBox.SelectedIndex = Categories.FindIndex(r => r == SubmissionInfo.CommonDiscInfo.Category);
|
||||
Parent.RegionComboBox.SelectedIndex = Regions.FindIndex(r => r == SubmissionInfo.CommonDiscInfo.Region);
|
||||
if (SubmissionInfo.CommonDiscInfo.Languages != null)
|
||||
Languages.ForEach(l => l.IsChecked = SubmissionInfo.CommonDiscInfo.Languages.Contains(l));
|
||||
if (SubmissionInfo.CommonDiscInfo.LanguageSelection != null)
|
||||
LanguageSelections.ForEach(ls => ls.IsChecked = SubmissionInfo.CommonDiscInfo.LanguageSelection.Contains(ls));
|
||||
|
||||
// TODO: Figure out if this can be automatically mapped instead
|
||||
|
||||
// Comment Fields
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields != null)
|
||||
{
|
||||
// Additional Information
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.AlternativeTitle))
|
||||
Parent.AlternativeTitleTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeTitle];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.AlternativeForeignTitle))
|
||||
Parent.AlternativeForeignTitleTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeForeignTitle];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Genre))
|
||||
Parent.GenreTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Genre];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PostgapType))
|
||||
Parent.PostgapTypeCheckBox.IsChecked = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PostgapType] != null;
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Series))
|
||||
Parent.SeriesTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Series];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VCD))
|
||||
Parent.VCDCheckBox.IsChecked = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VCD] != null;
|
||||
|
||||
// Physical Identifiers
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.BBFCRegistrationNumber))
|
||||
Parent.BBFCRegistrationNumberTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BBFCRegistrationNumber];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.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))
|
||||
Parent.DNASDiscIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DNASDiscID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ISBN))
|
||||
Parent.ISBNTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISBN];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ISSN))
|
||||
Parent.ISSNTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISSN];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PPN))
|
||||
Parent.PPNTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PPN];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VFCCode))
|
||||
Parent.VFCCodeTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VFCCode];
|
||||
|
||||
// Publisher Identifiers
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.AcclaimID))
|
||||
Parent.AcclaimIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AcclaimID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ActivisionID))
|
||||
Parent.ActivisionIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ActivisionID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.BandaiID))
|
||||
Parent.BandaiIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BandaiID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ElectronicArtsID))
|
||||
Parent.ElectronicArtsIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ElectronicArtsID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.FoxInteractiveID))
|
||||
Parent.FoxInteractiveIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.FoxInteractiveID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.GTInteractiveID))
|
||||
Parent.GTInteractiveIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.GTInteractiveID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.JASRACID))
|
||||
Parent.JASRACIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.JASRACID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.KingRecordsID))
|
||||
Parent.KingRecordsIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KingRecordsID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.KoeiID))
|
||||
Parent.KoeiIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KoeiID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.KonamiID))
|
||||
Parent.KonamiIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KonamiID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.LucasArtsID))
|
||||
Parent.LucasArtsIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.LucasArtsID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.MicrosoftID))
|
||||
Parent.MicrosoftIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.MicrosoftID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.NaganoID))
|
||||
Parent.NaganoIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NaganoID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.NamcoID))
|
||||
Parent.NamcoIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NamcoID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.NipponIchiSoftwareID))
|
||||
Parent.NipponIchiSoftwareIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NipponIchiSoftwareID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.OriginID))
|
||||
Parent.OriginIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.OriginID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PonyCanyonID))
|
||||
Parent.PonyCanyonIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PonyCanyonID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SegaID))
|
||||
Parent.SegaIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SegaID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SelenID))
|
||||
Parent.SelenIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SelenID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SierraID))
|
||||
Parent.SierraIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SierraID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.TaitoID))
|
||||
Parent.TaitoIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.TaitoID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.UbisoftID))
|
||||
Parent.UbisoftIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UbisoftID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.ValveID))
|
||||
Parent.ValveIDTextBox.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ValveID];
|
||||
|
||||
// Read-Only Information
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.DMIHash))
|
||||
Parent.DMIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Filename))
|
||||
Parent.Filename.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalName))
|
||||
Parent.InternalName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.InternalSerialName))
|
||||
Parent.InternalSerialName.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.Multisession))
|
||||
Parent.Multisession.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.PFIHash))
|
||||
Parent.PFIHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.RingNonZeroDataStart))
|
||||
Parent.RingNonZeroDataStart.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSHash))
|
||||
Parent.SSHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.SSVersion))
|
||||
Parent.SSVersion.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.UniversalHash))
|
||||
Parent.UniversalHash.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.VolumeLabel))
|
||||
Parent.VolumeLabel.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.XeMID))
|
||||
Parent.XeMID.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID];
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields.ContainsKey(SiteCode.XMID))
|
||||
Parent.XMID.Text = SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID];
|
||||
}
|
||||
|
||||
// Content Fields
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields != null)
|
||||
{
|
||||
// Games
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Games))
|
||||
Parent.GamesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Games];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.NetYarozeGames))
|
||||
Parent.NetYarozeGamesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.NetYarozeGames];
|
||||
|
||||
// Demos
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.PlayableDemos))
|
||||
Parent.PlayableDemosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.PlayableDemos];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.RollingDemos))
|
||||
Parent.RollingDemosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.RollingDemos];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.TechDemos))
|
||||
Parent.TechDemosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.TechDemos];
|
||||
|
||||
// Video
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.GameFootage))
|
||||
Parent.GameFootageTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.GameFootage];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Videos))
|
||||
Parent.VideosTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Videos];
|
||||
|
||||
// Miscellaneous
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Patches))
|
||||
Parent.PatchesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Patches];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Savegames))
|
||||
Parent.SavegamesTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Savegames];
|
||||
if (SubmissionInfo.CommonDiscInfo.ContentsSpecialFields.ContainsKey(SiteCode.Extras))
|
||||
Parent.ExtrasTextBox.Text = SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Extras];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current contents of the UI to the base SubmissionInfo
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
SubmissionInfo.CommonDiscInfo.Category = (Parent.CategoryComboBox.SelectedItem as Element<DiscCategory>)?.Value ?? DiscCategory.Games;
|
||||
SubmissionInfo.CommonDiscInfo.Region = (Parent.RegionComboBox.SelectedItem as Element<Region>)?.Value ?? Region.World;
|
||||
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
|
||||
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
|
||||
SubmissionInfo.CommonDiscInfo.Languages = new Language?[] { null };
|
||||
SubmissionInfo.CommonDiscInfo.LanguageSelection = LanguageSelections.Where(ls => ls.IsChecked).Select(ls => ls?.Value).ToArray();
|
||||
|
||||
// TODO: Figure out if this can be automatically mapped instead
|
||||
|
||||
// Initialize the dictionaries, if needed
|
||||
if (SubmissionInfo.CommonDiscInfo.CommentsSpecialFields == null)
|
||||
#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
|
||||
|
||||
// Additional Information
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeTitle] = Parent.AlternativeTitleTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AlternativeForeignTitle] = Parent.AlternativeForeignTitleTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Genre] = Parent.GenreTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PostgapType] = Parent.PostgapTypeCheckBox.IsChecked?.ToString();
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Series] = Parent.SeriesTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VCD] = Parent.VCDCheckBox.IsChecked?.ToString();
|
||||
|
||||
// Physical Identifiers
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BBFCRegistrationNumber] = Parent.BBFCRegistrationNumberTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.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;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ISSN] = Parent.ISSNTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PPN] = Parent.PPNTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VFCCode] = Parent.VFCCodeTextBox.Text;
|
||||
|
||||
// Publisher Identifiers
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.AcclaimID] = Parent.AcclaimIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ActivisionID] = Parent.ActivisionIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.BandaiID] = Parent.BandaiIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ElectronicArtsID] = Parent.ElectronicArtsIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.FoxInteractiveID] = Parent.FoxInteractiveIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.GTInteractiveID] = Parent.GTInteractiveIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.JASRACID] = Parent.JASRACIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KingRecordsID] = Parent.KingRecordsIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KoeiID] = Parent.KoeiIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.KonamiID] = Parent.KonamiIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.LucasArtsID] = Parent.LucasArtsIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.MicrosoftID] = Parent.MicrosoftIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NaganoID] = Parent.NaganoIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NamcoID] = Parent.NamcoIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.NipponIchiSoftwareID] = Parent.NipponIchiSoftwareIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.OriginID] = Parent.OriginIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PonyCanyonID] = Parent.PonyCanyonIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SegaID] = Parent.SegaIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SelenID] = Parent.SelenIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SierraID] = Parent.SierraIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.TaitoID] = Parent.TaitoIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UbisoftID] = Parent.UbisoftIDTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.ValveID] = Parent.ValveIDTextBox.Text;
|
||||
|
||||
// Read-Only Information
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.DMIHash] = Parent.DMIHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Filename] = Parent.Filename.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalName] = Parent.InternalName.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.InternalSerialName] = Parent.InternalSerialName.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.Multisession] = Parent.Multisession.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.PFIHash] = Parent.PFIHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.RingNonZeroDataStart] = Parent.RingNonZeroDataStart.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSHash] = Parent.SSHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.SSVersion] = Parent.SSVersion.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.UniversalHash] = Parent.UniversalHash.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.VolumeLabel] = Parent.VolumeLabel.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XeMID] = Parent.XeMID.Text;
|
||||
SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[SiteCode.XMID] = Parent.XMID.Text;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Content Fields
|
||||
|
||||
// Games
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Games] = Parent.GamesTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.NetYarozeGames] = Parent.NetYarozeGamesTextBox.Text;
|
||||
|
||||
// Demos
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.PlayableDemos] = Parent.PlayableDemosTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.RollingDemos] = Parent.RollingDemosTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.TechDemos] = Parent.TechDemosTextBox.Text;
|
||||
|
||||
// Video
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.GameFootage] = Parent.GameFootageTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Videos] = Parent.VideosTextBox.Text;
|
||||
|
||||
// Miscellaneous
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Patches] = Parent.PatchesTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Savegames] = Parent.SavegamesTextBox.Text;
|
||||
SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[SiteCode.Extras] = Parent.ExtrasTextBox.Text;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulate the list of Languages based on Redump support
|
||||
/// </summary>
|
||||
private void SetRedumpLanguages()
|
||||
{
|
||||
this.Languages = RedumpLanguages.Select(l => new Element<Language>(l)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulate the list of Regions based on Redump support
|
||||
/// </summary>
|
||||
private void SetRedumpRegions()
|
||||
{
|
||||
this.Regions = RedumpRegions.Select(r => new Element<Region>(r)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visible fields and sections based on the media type
|
||||
/// </summary>
|
||||
private void UpdateFromDiscType()
|
||||
{
|
||||
// Sony-printed discs have layers in the opposite order
|
||||
var system = SubmissionInfo?.CommonDiscInfo?.System;
|
||||
bool reverseOrder = system.HasReversedRingcodes();
|
||||
|
||||
switch (SubmissionInfo?.CommonDiscInfo?.Media)
|
||||
{
|
||||
case DiscType.CD:
|
||||
case DiscType.GDROM:
|
||||
Parent.L0Info.Header = "Data Side";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Label Side";
|
||||
Parent.L1MasteringRing.Visibility = Visibility.Collapsed;
|
||||
Parent.L1MasteringSID.Visibility = Visibility.Collapsed;
|
||||
Parent.L1Toolstamp.Visibility = Visibility.Collapsed;
|
||||
Parent.L1MouldSID.Label = "Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Additional Mould";
|
||||
break;
|
||||
|
||||
case DiscType.DVD5:
|
||||
case DiscType.DVD9:
|
||||
case DiscType.HDDVDSL:
|
||||
case DiscType.HDDVDDL:
|
||||
case DiscType.BD25:
|
||||
case DiscType.BD33:
|
||||
case DiscType.BD50:
|
||||
case DiscType.BD66:
|
||||
case DiscType.BD100:
|
||||
case DiscType.BD128:
|
||||
case DiscType.NintendoGameCubeGameDisc:
|
||||
case DiscType.NintendoWiiOpticalDiscSL:
|
||||
case DiscType.NintendoWiiOpticalDiscDL:
|
||||
case DiscType.NintendoWiiUOpticalDiscSL:
|
||||
// Quad-layer discs
|
||||
if (SubmissionInfo?.SizeAndChecksums?.Layerbreak3 != default(long))
|
||||
{
|
||||
Parent.L2Info.Visibility = Visibility.Visible;
|
||||
Parent.L3Info.Visibility = Visibility.Visible;
|
||||
|
||||
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Data Side Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Layer 1";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Label Side Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
|
||||
Parent.L2Info.Header = "Layer 2";
|
||||
Parent.L2MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L2MasteringSID.Label = "Mastering SID";
|
||||
Parent.L2Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
|
||||
Parent.L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
|
||||
Parent.L3MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L3MasteringSID.Label = "Mastering SID";
|
||||
Parent.L3Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
}
|
||||
|
||||
// Triple-layer discs
|
||||
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak2 != default(long))
|
||||
{
|
||||
Parent.L2Info.Visibility = Visibility.Visible;
|
||||
|
||||
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Data Side Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Layer 1";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Label Side Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
|
||||
Parent.L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
|
||||
Parent.L2MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L2MasteringSID.Label = "Mastering SID";
|
||||
Parent.L2Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
}
|
||||
|
||||
// Double-layer discs
|
||||
else if (SubmissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
|
||||
{
|
||||
Parent.L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Data Side Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Label Side Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
}
|
||||
|
||||
// Single-layer discs
|
||||
else
|
||||
{
|
||||
Parent.L0Info.Header = "Data Side";
|
||||
Parent.L0MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L0MasteringSID.Label = "Mastering SID";
|
||||
Parent.L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L0MouldSID.Label = "Mould SID";
|
||||
Parent.L0AdditionalMould.Label = "Additional Mould";
|
||||
|
||||
Parent.L1Info.Header = "Label Side";
|
||||
Parent.L1MasteringRing.Label = "Mastering Ring";
|
||||
Parent.L1MasteringSID.Label = "Mastering SID";
|
||||
Parent.L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
Parent.L1MouldSID.Label = "Mould SID";
|
||||
Parent.L1AdditionalMould.Label = "Additional Mould";
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// All other media we assume to have no rings
|
||||
default:
|
||||
Parent.L0Info.Visibility = Visibility.Collapsed;
|
||||
Parent.L1Info.Visibility = Visibility.Collapsed;
|
||||
Parent.L2Info.Visibility = Visibility.Collapsed;
|
||||
Parent.L3Info.Visibility = Visibility.Collapsed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visible fields and sections based on the system type
|
||||
/// </summary>
|
||||
private void UpdateFromSystemType()
|
||||
{
|
||||
var system = SubmissionInfo?.CommonDiscInfo?.System;
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
Parent.LanguageSelectionGrid.Visibility = Visibility.Visible;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AcceptButton Click event
|
||||
/// </summary>
|
||||
private void OnAcceptClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Save();
|
||||
Parent.DialogResult = true;
|
||||
Parent.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CancelButton Click event
|
||||
/// </summary>
|
||||
private void OnCancelClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Parent.DialogResult = false;
|
||||
Parent.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for RingCodeGuideButton Click event
|
||||
/// </summary>
|
||||
private void OnRingCodeGuideClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var ringCodeGuideWindow = new RingCodeGuideWindow()
|
||||
{
|
||||
Owner = Parent,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
ringCodeGuideWindow.Show();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,293 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,277 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Forms;
|
||||
using MPF.Core.Data;
|
||||
using MPF.UI.Core.ComboBoxItems;
|
||||
using MPF.UI.Core.Windows;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
using WPFCustomMessageBox;
|
||||
|
||||
namespace MPF.UI.Core.ViewModels
|
||||
{
|
||||
public class OptionsViewModel
|
||||
{
|
||||
#region Fields
|
||||
|
||||
/// <summary>
|
||||
/// Parent OptionsWindow object
|
||||
/// </summary>
|
||||
public OptionsWindow Parent { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Current set of options
|
||||
/// </summary>
|
||||
public Options Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag for if settings were saved or not
|
||||
/// </summary>
|
||||
public bool SavedSettings { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lists
|
||||
|
||||
/// <summary>
|
||||
/// List of available internal programs
|
||||
/// </summary>
|
||||
public List<Element<InternalProgram>> InternalPrograms => PopulateInternalPrograms();
|
||||
|
||||
/// <summary>
|
||||
/// Current list of supported system profiles
|
||||
/// </summary>
|
||||
public List<RedumpSystemComboBoxItem> Systems => RedumpSystemComboBoxItem.GenerateElements().ToList();
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public OptionsViewModel(OptionsWindow parent, Options baseOptions)
|
||||
{
|
||||
Parent = parent;
|
||||
Options = new Options(baseOptions);
|
||||
|
||||
// Add handlers
|
||||
Parent.AaruPathButton.Click += BrowseForPathClick;
|
||||
Parent.DiscImageCreatorPathButton.Click += BrowseForPathClick;
|
||||
Parent.DefaultOutputPathButton.Click += BrowseForPathClick;
|
||||
|
||||
Parent.AcceptButton.Click += OnAcceptClick;
|
||||
Parent.CancelButton.Click += OnCancelClick;
|
||||
Parent.RedumpLoginTestButton.Click += OnRedumpTestClick;
|
||||
|
||||
// Update UI with new values
|
||||
Load();
|
||||
}
|
||||
|
||||
#region Load and Save
|
||||
|
||||
/// <summary>
|
||||
/// Load any options-related elements
|
||||
/// </summary>
|
||||
private void Load()
|
||||
{
|
||||
Parent.InternalProgramComboBox.SelectedIndex = InternalPrograms.FindIndex(r => r == Options.InternalProgram);
|
||||
Parent.DefaultSystemComboBox.SelectedIndex = Systems.FindIndex(r => r == Options.DefaultSystem);
|
||||
Parent.RedumpPasswordBox.Password = Options.RedumpPassword;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save any options-related elements
|
||||
/// </summary>
|
||||
private void Save()
|
||||
{
|
||||
var selectedInternalProgram = Parent.InternalProgramComboBox.SelectedItem as Element<InternalProgram>;
|
||||
Options.InternalProgram = selectedInternalProgram?.Value ?? InternalProgram.DiscImageCreator;
|
||||
var selectedDefaultSystem = Parent.DefaultSystemComboBox.SelectedItem as RedumpSystemComboBoxItem;
|
||||
Options.DefaultSystem = selectedDefaultSystem?.Value ?? null;
|
||||
Options.RedumpPassword = Parent.RedumpPasswordBox.Password;
|
||||
|
||||
SavedSettings = true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Population
|
||||
|
||||
/// <summary>
|
||||
/// Get a complete list of supported internal programs
|
||||
/// </summary>
|
||||
private static List<Element<InternalProgram>> PopulateInternalPrograms()
|
||||
{
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.Redumper };
|
||||
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Commands
|
||||
|
||||
/// <summary>
|
||||
/// Browse and set a path based on the invoking button
|
||||
/// </summary>
|
||||
private void BrowseForPath(System.Windows.Controls.Button button)
|
||||
{
|
||||
// If the button is null, we can't do anything
|
||||
if (button == null)
|
||||
return;
|
||||
|
||||
// Strips button prefix to obtain the setting name
|
||||
string pathSettingName = button.Name.Substring(0, button.Name.IndexOf("Button"));
|
||||
|
||||
// TODO: hack for now, then we'll see
|
||||
bool shouldBrowseForPath = pathSettingName == "DefaultOutputPath";
|
||||
|
||||
string currentPath = TextBoxForPathSetting(pathSettingName)?.Text;
|
||||
string initialDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||
if (!shouldBrowseForPath && !string.IsNullOrEmpty(currentPath))
|
||||
initialDirectory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
|
||||
|
||||
CommonDialog dialog = shouldBrowseForPath
|
||||
? (CommonDialog)CreateFolderBrowserDialog()
|
||||
: CreateOpenFileDialog(initialDirectory);
|
||||
using (dialog)
|
||||
{
|
||||
DialogResult result = dialog.ShowDialog();
|
||||
if (result == DialogResult.OK)
|
||||
{
|
||||
string path = string.Empty;
|
||||
bool exists = false;
|
||||
|
||||
if (shouldBrowseForPath && dialog is FolderBrowserDialog folderBrowserDialog)
|
||||
{
|
||||
path = folderBrowserDialog.SelectedPath;
|
||||
exists = Directory.Exists(path);
|
||||
}
|
||||
else if (dialog is OpenFileDialog openFileDialog)
|
||||
{
|
||||
path = openFileDialog.FileName;
|
||||
exists = File.Exists(path);
|
||||
}
|
||||
|
||||
if (exists)
|
||||
{
|
||||
Options[pathSettingName] = path;
|
||||
TextBoxForPathSetting(pathSettingName).Text = path;
|
||||
}
|
||||
else
|
||||
{
|
||||
CustomMessageBox.Show(
|
||||
"Specified path doesn't exist!",
|
||||
"Error",
|
||||
MessageBoxButton.OK,
|
||||
MessageBoxImage.Error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optionally save the current options and close the parent window
|
||||
/// </summary>
|
||||
private void OptionalSaveAndClose(bool save)
|
||||
{
|
||||
// Save if we're supposed to
|
||||
if (save)
|
||||
Save();
|
||||
|
||||
Parent.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test Redump login credentials
|
||||
/// </summary>
|
||||
#if NET48
|
||||
private bool? TestRedumpLogin()
|
||||
#else
|
||||
private async Task<bool?> TestRedumpLogin()
|
||||
#endif
|
||||
{
|
||||
#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
|
||||
|
||||
#region UI Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Create an open folder dialog box
|
||||
/// </summary>
|
||||
private static FolderBrowserDialog CreateFolderBrowserDialog() => new FolderBrowserDialog();
|
||||
|
||||
/// <summary>
|
||||
/// Create an open file dialog box
|
||||
/// </summary>
|
||||
private static OpenFileDialog CreateOpenFileDialog(string initialDirectory)
|
||||
{
|
||||
return new OpenFileDialog()
|
||||
{
|
||||
InitialDirectory = initialDirectory,
|
||||
Filter = "Executables (*.exe)|*.exe",
|
||||
FilterIndex = 0,
|
||||
RestoreDirectory = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a TextBox by setting name
|
||||
/// </summary>
|
||||
/// <param name="name">Setting name to find</param>
|
||||
/// <returns>TextBox for that setting</returns>
|
||||
private System.Windows.Controls.TextBox TextBoxForPathSetting(string name) =>
|
||||
Parent.FindName(name + "TextBox") as System.Windows.Controls.TextBox;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for generic Click event
|
||||
/// </summary>
|
||||
private void BrowseForPathClick(object sender, EventArgs e) =>
|
||||
BrowseForPath(sender as System.Windows.Controls.Button);
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AcceptButton Click event
|
||||
/// </summary>
|
||||
private void OnAcceptClick(object sender, EventArgs e) =>
|
||||
OptionalSaveAndClose(true);
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CancelButtom Click event
|
||||
/// </summary>
|
||||
private void OnCancelClick(object sender, EventArgs e) =>
|
||||
OptionalSaveAndClose(false);
|
||||
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
127
MPF.UI.Core/Windows/CheckDumpWindow.xaml
Normal file
127
MPF.UI.Core/Windows/CheckDumpWindow.xaml
Normal file
@@ -0,0 +1,127 @@
|
||||
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.CheckDumpWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
|
||||
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
|
||||
mc:Ignorable="d"
|
||||
Title="Check Existing Dump" Width="600" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<Window.DataContext>
|
||||
<viewModels:CheckDumpViewModel/>
|
||||
</Window.DataContext>
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid Margin="0,2,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="25"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
|
||||
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
|
||||
<Label.Content>
|
||||
<Run FontWeight="Bold" Text="Check Existing Dump" />
|
||||
</Label.Content>
|
||||
<Label.ContextMenu>
|
||||
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Style="{DynamicResource CustomContextMenuStyle}">
|
||||
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"/>
|
||||
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"/>
|
||||
</ContextMenu>
|
||||
</Label.ContextMenu>
|
||||
</Label>
|
||||
<Grid Grid.Column="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
|
||||
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
|
||||
</Button>
|
||||
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
|
||||
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Settings">
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="2.5*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label x:Name="InputPathLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Input Path"/>
|
||||
<TextBox x:Name="InputPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding InputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding InputPathTextBoxEnabled}" />
|
||||
<Button x:Name="InputPathBrowseButton" Grid.Row="0" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
IsEnabled="{Binding InputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
|
||||
|
||||
<Label x:Name="SystemMediaTypeLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
|
||||
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="1" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding Systems}" SelectedItem="{Binding Path=CurrentSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding SystemTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ComboBoxItem}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHeader}" Value="True">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="1" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right"
|
||||
ItemsSource="{Binding MediaTypes}" SelectedItem="{Binding Path=CurrentMediaType, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding MediaTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="DumpingProgramLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Dumping Program"/>
|
||||
<ComboBox x:Name="DumpingProgramComboBox" Grid.Row="2" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding InternalPrograms}" SelectedItem="{Binding Path=CurrentProgram, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding DumpingProgramComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Check Dump / Cancel -->
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
<Button Name="CheckDumpButton" Height="25" Width="80" IsDefault="True" Content="Check Dump"
|
||||
IsEnabled="{Binding CheckDumpButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
|
||||
IsEnabled="{Binding CancelButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</coreWindows:WindowBase>
|
||||
291
MPF.UI.Core/Windows/CheckDumpWindow.xaml.cs
Normal file
291
MPF.UI.Core/Windows/CheckDumpWindow.xaml.cs
Normal file
@@ -0,0 +1,291 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using MPF.Core.UI.ViewModels;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using WPFCustomMessageBox;
|
||||
using WinForms = System.Windows.Forms;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for CheckDumpWindow.xaml
|
||||
/// </summary>
|
||||
public partial class CheckDumpWindow : WindowBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Read-only access to the current check dump view model
|
||||
/// </summary>
|
||||
public CheckDumpViewModel CheckDumpViewModel => DataContext as CheckDumpViewModel ?? new CheckDumpViewModel();
|
||||
|
||||
#if NET35
|
||||
|
||||
#region Settings
|
||||
|
||||
private ComboBox? _DumpingProgramComboBox => ItemHelper.FindChild<ComboBox>(this, "DumpingProgramComboBox");
|
||||
private Button? _InputPathBrowseButton => ItemHelper.FindChild<Button>(this, "InputPathBrowseButton");
|
||||
private TextBox? _InputPathTextBox => ItemHelper.FindChild<TextBox>(this, "InputPathTextBox");
|
||||
private ComboBox? _MediaTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "MediaTypeComboBox");
|
||||
private ComboBox? _SystemTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "SystemTypeComboBox");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controls
|
||||
|
||||
private System.Windows.Controls.Button? _CheckDumpButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CheckDumpButton");
|
||||
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CheckDumpWindow(MainWindow parent)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
var chrome = new System.Windows.Shell.WindowChrome
|
||||
{
|
||||
CaptionHeight = 0,
|
||||
ResizeBorderThickness = new Thickness(0),
|
||||
};
|
||||
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckDumpWindow OnContentRendered event
|
||||
/// </summary>
|
||||
protected override void OnContentRendered(EventArgs e)
|
||||
{
|
||||
base.OnContentRendered(e);
|
||||
|
||||
// Add the click handlers to the UI
|
||||
AddEventHandlers();
|
||||
}
|
||||
|
||||
#region UI Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Add all event handlers
|
||||
/// </summary>
|
||||
public void AddEventHandlers()
|
||||
{
|
||||
// Main buttons
|
||||
#if NET35
|
||||
_CheckDumpButton!.Click += OnCheckDumpClick;
|
||||
_CancelButton!.Click += OnCancelClick;
|
||||
#else
|
||||
CheckDumpButton.Click += OnCheckDumpClick;
|
||||
CancelButton.Click += OnCancelClick;
|
||||
#endif
|
||||
|
||||
// User Area Click
|
||||
#if NET35
|
||||
_InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
|
||||
#else
|
||||
InputPathBrowseButton.Click += InputPathBrowseButtonClick;
|
||||
#endif
|
||||
|
||||
// User Area SelectionChanged
|
||||
#if NET35
|
||||
_SystemTypeComboBox!.SelectionChanged += SystemTypeComboBoxSelectionChanged;
|
||||
_MediaTypeComboBox!.SelectionChanged += MediaTypeComboBoxSelectionChanged;
|
||||
_DumpingProgramComboBox!.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
|
||||
#else
|
||||
SystemTypeComboBox.SelectionChanged += SystemTypeComboBoxSelectionChanged;
|
||||
MediaTypeComboBox.SelectionChanged += MediaTypeComboBoxSelectionChanged;
|
||||
DumpingProgramComboBox.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
|
||||
#endif
|
||||
|
||||
// User Area TextChanged
|
||||
#if NET35
|
||||
_InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
|
||||
#else
|
||||
InputPathTextBox.TextChanged += InputPathTextBoxTextChanged;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Browse for an input file path
|
||||
/// </summary>
|
||||
public void BrowseFile()
|
||||
{
|
||||
// Get the current path, if possible
|
||||
string? currentPath = CheckDumpViewModel.InputPath;
|
||||
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CheckDumpViewModel.Options.DefaultOutputPath))
|
||||
currentPath = CheckDumpViewModel.Options.DefaultOutputPath!;
|
||||
if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
|
||||
|
||||
// Get the full directory
|
||||
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
|
||||
|
||||
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
|
||||
{
|
||||
InitialDirectory = directory,
|
||||
Filter = "Disc Images|*.iso;*.cue;*.aaruf|All Files|*.*",
|
||||
};
|
||||
WinForms.DialogResult result = fileDialog.ShowDialog();
|
||||
|
||||
if (result == WinForms.DialogResult.OK)
|
||||
{
|
||||
CheckDumpViewModel.InputPath = fileDialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display a user message using a CustomMessageBox
|
||||
/// </summary>
|
||||
/// <param name="title">Title to display to the user</param>
|
||||
/// <param name="message">Message to display to the user</param>
|
||||
/// <param name="optionCount">Number of options to display</param>
|
||||
/// <param name="flag">true for inquiry, false otherwise</param>
|
||||
/// <returns>true for positive, false for negative, null for neutral</returns>
|
||||
public bool? DisplayUserMessage(string title, string message, int optionCount, bool flag)
|
||||
{
|
||||
// Set the correct button style
|
||||
var button = optionCount switch
|
||||
{
|
||||
1 => MessageBoxButton.OK,
|
||||
2 => MessageBoxButton.YesNo,
|
||||
3 => MessageBoxButton.YesNoCancel,
|
||||
|
||||
// This should not happen, but default to "OK"
|
||||
_ => MessageBoxButton.OK,
|
||||
};
|
||||
|
||||
// Set the correct icon
|
||||
MessageBoxImage image = flag ? MessageBoxImage.Question : MessageBoxImage.Exclamation;
|
||||
|
||||
// Display and get the result
|
||||
MessageBoxResult result = CustomMessageBox.Show(this, message, title, button, image);
|
||||
return result switch
|
||||
{
|
||||
MessageBoxResult.OK or MessageBoxResult.Yes => true,
|
||||
MessageBoxResult.No => false,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the disc information window
|
||||
/// </summary>
|
||||
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
|
||||
/// <returns>Dialog open result</returns>
|
||||
public (bool?, SubmissionInfo?) ShowDiscInformationWindow(SubmissionInfo? submissionInfo)
|
||||
{
|
||||
var discInformationWindow = new DiscInformationWindow(CheckDumpViewModel.Options, submissionInfo)
|
||||
{
|
||||
Focusable = true,
|
||||
Owner = this,
|
||||
ShowActivated = true,
|
||||
ShowInTaskbar = true,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
discInformationWindow.Closed += delegate { this.Activate(); };
|
||||
bool? result = discInformationWindow.ShowDialog();
|
||||
|
||||
// Copy back the submission info changes, if necessary
|
||||
if (result == true)
|
||||
submissionInfo = (discInformationWindow.DiscInformationViewModel.SubmissionInfo.Clone() as SubmissionInfo)!;
|
||||
|
||||
return (result, submissionInfo!);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckDumpButton Click event
|
||||
/// </summary>
|
||||
#if NET40
|
||||
private void OnCheckDumpClick(object sender, EventArgs e)
|
||||
{
|
||||
var checkTask = CheckDumpViewModel.CheckDump(ShowDiscInformationWindow);
|
||||
checkTask.Wait();
|
||||
string? errorMessage = checkTask.Result;
|
||||
#else
|
||||
private async void OnCheckDumpClick(object sender, EventArgs e)
|
||||
{
|
||||
string? errorMessage = await CheckDumpViewModel.CheckDump(ShowDiscInformationWindow);
|
||||
#endif
|
||||
if (string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
bool? checkAgain = DisplayUserMessage("Check Complete", "The dump has been processed successfully! Would you like to check another dump?", 2, false);
|
||||
if (checkAgain == false)
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayUserMessage("Check Failed", errorMessage!, 1, false);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CancelButtom Click event
|
||||
/// </summary>
|
||||
private void OnCancelClick(object sender, EventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DumpingProgramComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void DumpingProgramComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (CheckDumpViewModel.CanExecuteSelectionChanged)
|
||||
CheckDumpViewModel.ChangeDumpingProgram();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for InputPathBrowseButton Click event
|
||||
/// </summary>
|
||||
public void InputPathBrowseButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BrowseFile();
|
||||
if (CheckDumpViewModel.CanExecuteSelectionChanged)
|
||||
CheckDumpViewModel.ChangeInputPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for InputPathTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void InputPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (CheckDumpViewModel.CanExecuteSelectionChanged)
|
||||
CheckDumpViewModel.ChangeInputPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for MediaTypeComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void MediaTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (CheckDumpViewModel.CanExecuteSelectionChanged)
|
||||
CheckDumpViewModel.ChangeMediaType();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for SystemTypeComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void SystemTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (CheckDumpViewModel.CanExecuteSelectionChanged)
|
||||
CheckDumpViewModel.ChangeSystem();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
209
MPF.UI.Core/Windows/CreateIRDWindow.xaml
Normal file
209
MPF.UI.Core/Windows/CreateIRDWindow.xaml
Normal file
@@ -0,0 +1,209 @@
|
||||
<coreWindows:WindowBase x:Class="MPF.UI.Core.Windows.CreateIRDWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
|
||||
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
|
||||
mc:Ignorable="d"
|
||||
Title="Create PS3 IRD" Width="600" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<Window.DataContext>
|
||||
<viewModels:CreateIRDViewModel/>
|
||||
</Window.DataContext>
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid Margin="0,2,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="25"/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
|
||||
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
|
||||
<Label.Content>
|
||||
<Run FontWeight="Bold" Text="Create PS3 IRD" />
|
||||
</Label.Content>
|
||||
<Label.ContextMenu>
|
||||
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Style="{DynamicResource CustomContextMenuStyle}">
|
||||
<MenuItem Header="Minimize" Click="MinimizeButtonClick" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"/>
|
||||
<MenuItem Header="Close" Click="CloseButtonClick" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"/>
|
||||
</ContextMenu>
|
||||
</Label.ContextMenu>
|
||||
</Label>
|
||||
<Grid Grid.Column="5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition/>
|
||||
<ColumnDefinition/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Button x:Name="MinimizeButton" Grid.Column="0" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="MinimizeButtonClick">
|
||||
<Path Data="M 0,0 L 10,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
|
||||
</Button>
|
||||
<Button x:Name="CloseButton" Grid.Column="1" BorderThickness="0" Background="Transparent" Style="{DynamicResource CustomButtonStyle}" Click="CloseButtonClick">
|
||||
<Path Data="M 0,0 L 12,12 M 0,12 L 12,0" Stroke="{Binding Path=Foreground,RelativeSource={RelativeSource AncestorType={x:Type Button}}}" StrokeThickness="1"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Input">
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="2.5*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label x:Name="InputPathLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="PS3 ISO Path"/>
|
||||
<TextBox x:Name="InputPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding InputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding InputPathTextBoxEnabled}" />
|
||||
<Button x:Name="InputPathBrowseButton" Grid.Row="0" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
IsEnabled="{Binding InputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
|
||||
|
||||
<Label x:Name="LogPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="*.getkey.log File Path"/>
|
||||
<TextBox x:Name="LogPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding LogPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding LogPathTextBoxEnabled}" />
|
||||
<Button x:Name="LogPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
IsEnabled="{Binding LogPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<Expander Name="KeyExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
|
||||
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define PS3 Disc Encryption Key">
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="2.5*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label x:Name="KeyLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Hexadecimal Key"/>
|
||||
<TextBox x:Name="KeyTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding HexKey, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding HexKeyTextBoxEnabled}" />
|
||||
|
||||
<Label x:Name="KeyPathLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
|
||||
ToolTip="Typically a *.key file" Content="Key File Path"/>
|
||||
<TextBox x:Name="KeyPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding KeyPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding KeyPathTextBoxEnabled}" />
|
||||
<Button x:Name="KeyPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
IsEnabled="{Binding KeyPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
|
||||
|
||||
<Label x:Name="KeyStatusLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Key Status"/>
|
||||
<Label x:Name="KeyStatusText" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" Content="{Binding KeyStatus}"/>
|
||||
</Grid>
|
||||
</Expander>
|
||||
|
||||
<Expander Name="DiscIDExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
|
||||
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define PS3 Disc ID">
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="2.5*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label x:Name="DiscIDLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Disc ID"/>
|
||||
<TextBox x:Name="DiscIDTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding DiscIDString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding DiscIDTextBoxEnabled}" />
|
||||
|
||||
<Label x:Name="DiscIDStatusLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="DiscID Status"/>
|
||||
<Label x:Name="DiscIDStatusText" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" Content="{Binding DiscIDStatus}"/>
|
||||
</Grid>
|
||||
</Expander>
|
||||
|
||||
<Expander Name="PICExpander" BorderThickness="1" BorderBrush="#D5DFE5" Margin="5,5,5,5" HorizontalAlignment="Stretch"
|
||||
IsEnabled="{Binding LogPathNotProvided}" IsExpanded="False" Header="Manually define Permanent Information & Control (PIC)">
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="2.5*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label x:Name="LayerbreakLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="Layerbreak (sectors)"/>
|
||||
<TextBox x:Name="LayerbreakTextBox" Grid.Row="0" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding LayerbreakString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding LayerbreakTextBoxEnabled}" />
|
||||
|
||||
<Label x:Name="PICLabel" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Content="PIC"/>
|
||||
<TextBox x:Name="PICTextBox" Grid.Row="1" Grid.Column="1" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Top"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
MinHeight="55" MaxHeight="146" AcceptsReturn="True"
|
||||
Text="{Binding PICString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding PICTextBoxEnabled}" />
|
||||
|
||||
<Label x:Name="PICPathLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center"
|
||||
ToolTip="Typically a *.physical or *_PIC.bin file" Content="PIC File Path"/>
|
||||
<TextBox x:Name="PICPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding PICPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding PICPathTextBoxEnabled}" />
|
||||
<Button x:Name="PICPathBrowseButton" Grid.Row="2" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
IsEnabled="{Binding PICPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
|
||||
|
||||
<Label x:Name="PICStatusLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="PIC Status"/>
|
||||
<Label x:Name="PICStatusText" Grid.Row="3" Grid.Column="1" VerticalAlignment="Center" Content="{Binding PICStatus}"/>
|
||||
</Grid>
|
||||
</Expander>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
|
||||
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
|
||||
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Text="{Binding CreateIRDStatus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- Create IRD / Cancel -->
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
<Button Name="CreateIRDButton" Height="25" Width="80" IsDefault="True" Content="Create IRD"
|
||||
IsEnabled="{Binding CreateIRDButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
|
||||
IsEnabled="{Binding CancelButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</coreWindows:WindowBase>
|
||||
489
MPF.UI.Core/Windows/CreateIRDWindow.xaml.cs
Normal file
489
MPF.UI.Core/Windows/CreateIRDWindow.xaml.cs
Normal file
@@ -0,0 +1,489 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using MPF.Core.UI.ViewModels;
|
||||
using WPFCustomMessageBox;
|
||||
using WinForms = System.Windows.Forms;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for CreateIRDWindow.xaml
|
||||
/// </summary>
|
||||
public partial class CreateIRDWindow : WindowBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Read-only access to the current check dump view model
|
||||
/// </summary>
|
||||
public CreateIRDViewModel CreateIRDViewModel => DataContext as CreateIRDViewModel ?? new CreateIRDViewModel();
|
||||
|
||||
#if NET35
|
||||
|
||||
#region Settings
|
||||
|
||||
private Button? _InputPathBrowseButton => ItemHelper.FindChild<Button>(this, "InputPathBrowseButton");
|
||||
private TextBox? _InputPathTextBox => ItemHelper.FindChild<TextBox>(this, "InputPathTextBox");
|
||||
|
||||
private Button? _LogPathBrowseButton => ItemHelper.FindChild<Button>(this, "LogPathBrowseButton");
|
||||
private TextBox? _LogPathTextBox => ItemHelper.FindChild<TextBox>(this, "LogPathTextBox");
|
||||
private Button? _KeyPathBrowseButton => ItemHelper.FindChild<Button>(this, "KeyPathBrowseButton");
|
||||
private TextBox? _KeyPathTextBox => ItemHelper.FindChild<TextBox>(this, "KeyPathTextBox");
|
||||
private TextBox? _KeyTextBox => ItemHelper.FindChild<TextBox>(this, "KeyTextBox");
|
||||
private TextBox? _DiscIDTextBox => ItemHelper.FindChild<TextBox>(this, "DiscIDTextBox");
|
||||
private Button? _PICPathBrowseButton => ItemHelper.FindChild<Button>(this, "PICPathBrowseButton");
|
||||
private TextBox? _PICPathTextBox => ItemHelper.FindChild<TextBox>(this, "PICPathTextBox");
|
||||
private TextBox? _PICTextBox => ItemHelper.FindChild<TextBox>(this, "PICTextBox");
|
||||
private TextBox? _LayerbreakTextBox => ItemHelper.FindChild<TextBox>(this, "LayerbreakTextBox");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controls
|
||||
|
||||
private System.Windows.Controls.Button? _CreateIRDButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CreateIRDButton");
|
||||
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Expanders
|
||||
|
||||
private Expander? _KeyExpander => ItemHelper.FindChild<Expander>(this, "KeyExpander");
|
||||
private Expander? _DiscIDExpander => ItemHelper.FindChild<Expander>(this, "DiscIDExpander");
|
||||
private Expander? _PICExpander => ItemHelper.FindChild<Expander>(this, "PICExpander");
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public CreateIRDWindow(MainWindow parent)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
var chrome = new System.Windows.Shell.WindowChrome
|
||||
{
|
||||
CaptionHeight = 0,
|
||||
ResizeBorderThickness = new Thickness(0),
|
||||
};
|
||||
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckDumpWindow OnContentRendered event
|
||||
/// </summary>
|
||||
protected override void OnContentRendered(EventArgs e)
|
||||
{
|
||||
base.OnContentRendered(e);
|
||||
|
||||
// Add the click handlers to the UI
|
||||
AddEventHandlers();
|
||||
}
|
||||
|
||||
#region UI Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Add all event handlers
|
||||
/// </summary>
|
||||
public void AddEventHandlers()
|
||||
{
|
||||
// Main buttons
|
||||
#if NET35
|
||||
_CreateIRDButton!.Click += OnCreateIRDClick;
|
||||
_CancelButton!.Click += OnCancelClick;
|
||||
#else
|
||||
CreateIRDButton.Click += OnCreateIRDClick;
|
||||
CancelButton.Click += OnCancelClick;
|
||||
#endif
|
||||
|
||||
// User Area Click
|
||||
#if NET35
|
||||
_InputPathBrowseButton!.Click += InputPathBrowseButtonClick;
|
||||
_LogPathBrowseButton!.Click += LogPathBrowseButtonClick;
|
||||
_KeyPathBrowseButton!.Click += KeyPathBrowseButtonClick;
|
||||
_PICPathBrowseButton!.Click += PICPathBrowseButtonClick;
|
||||
#else
|
||||
InputPathBrowseButton.Click += InputPathBrowseButtonClick;
|
||||
LogPathBrowseButton.Click += LogPathBrowseButtonClick;
|
||||
KeyPathBrowseButton.Click += KeyPathBrowseButtonClick;
|
||||
PICPathBrowseButton.Click += PICPathBrowseButtonClick;
|
||||
#endif
|
||||
|
||||
// User Area TextChanged
|
||||
#if NET35
|
||||
_InputPathTextBox!.TextChanged += InputPathTextBoxTextChanged;
|
||||
_LogPathTextBox!.TextChanged += LogPathTextBoxTextChanged;
|
||||
_KeyPathTextBox!.TextChanged += KeyPathTextBoxTextChanged;
|
||||
_KeyTextBox!.TextChanged += KeyTextBoxTextChanged;
|
||||
_DiscIDTextBox!.TextChanged += DiscIDTextBoxTextChanged;
|
||||
_PICPathTextBox!.TextChanged += PICPathTextBoxTextChanged;
|
||||
_PICTextBox!.TextChanged += PICTextBoxTextChanged;
|
||||
_LayerbreakTextBox!.TextChanged += LayerbreakTextBoxTextChanged;
|
||||
#else
|
||||
InputPathTextBox.TextChanged += InputPathTextBoxTextChanged;
|
||||
LogPathTextBox.TextChanged += LogPathTextBoxTextChanged;
|
||||
KeyPathTextBox.TextChanged += KeyPathTextBoxTextChanged;
|
||||
KeyTextBox.TextChanged += KeyTextBoxTextChanged;
|
||||
DiscIDTextBox.TextChanged += DiscIDTextBoxTextChanged;
|
||||
PICPathTextBox.TextChanged += PICPathTextBoxTextChanged;
|
||||
PICTextBox.TextChanged += PICTextBoxTextChanged;
|
||||
LayerbreakTextBox.TextChanged += LayerbreakTextBoxTextChanged;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Browse for an input ISO file path
|
||||
/// </summary>
|
||||
public void BrowseISOFile()
|
||||
{
|
||||
// Get the current path, if possible
|
||||
string? currentPath = CreateIRDViewModel.InputPath;
|
||||
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
|
||||
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
|
||||
if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
|
||||
|
||||
// Get the full directory
|
||||
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
|
||||
|
||||
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
|
||||
{
|
||||
InitialDirectory = directory,
|
||||
Filter = "ISO|*.iso|All Files|*.*",
|
||||
};
|
||||
WinForms.DialogResult result = fileDialog.ShowDialog();
|
||||
|
||||
if (result == WinForms.DialogResult.OK)
|
||||
{
|
||||
CreateIRDViewModel.InputPath = fileDialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Browse for an .getkey.log file path
|
||||
/// </summary>
|
||||
public void BrowseLogFile()
|
||||
{
|
||||
// Get the current path, if possible
|
||||
string? currentPath = CreateIRDViewModel.LogPath;
|
||||
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
|
||||
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
|
||||
if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
|
||||
|
||||
// Get the full directory
|
||||
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
|
||||
|
||||
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
|
||||
{
|
||||
InitialDirectory = directory,
|
||||
Filter = "GetKey Log|*.getkey.log|All Files|*.*",
|
||||
};
|
||||
WinForms.DialogResult result = fileDialog.ShowDialog();
|
||||
|
||||
if (result == WinForms.DialogResult.OK)
|
||||
{
|
||||
CreateIRDViewModel.LogPath = fileDialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Browse for an key file path
|
||||
/// </summary>
|
||||
public void BrowseKeyFile()
|
||||
{
|
||||
// Get the current path, if possible
|
||||
string? currentPath = CreateIRDViewModel.LogPath;
|
||||
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
|
||||
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
|
||||
if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
|
||||
|
||||
// Get the full directory
|
||||
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
|
||||
|
||||
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
|
||||
{
|
||||
InitialDirectory = directory,
|
||||
Filter = "Key|*.key|All Files|*.*",
|
||||
};
|
||||
WinForms.DialogResult result = fileDialog.ShowDialog();
|
||||
|
||||
if (result == WinForms.DialogResult.OK)
|
||||
{
|
||||
CreateIRDViewModel.KeyPath = fileDialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Browse for an IRD output path
|
||||
/// </summary>
|
||||
/// <returns>Output path if provided, else null</returns>
|
||||
public string? BrowseOutputFile()
|
||||
{
|
||||
// Get the current path, if possible
|
||||
string? currentPath = CreateIRDViewModel.InputPath;
|
||||
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
|
||||
currentPath = Path.Combine(CreateIRDViewModel.Options.DefaultOutputPath, "game.ird");
|
||||
else if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = "game.ird";
|
||||
if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, "game.ird");
|
||||
|
||||
// Get the full path
|
||||
currentPath = Path.GetFullPath(currentPath);
|
||||
|
||||
// Get the directory
|
||||
var directory = Path.GetDirectoryName(currentPath);
|
||||
|
||||
// Get the filename
|
||||
string filename = Path.ChangeExtension(Path.GetFileName(currentPath), ".ird");
|
||||
|
||||
WinForms.FileDialog fileDialog = new WinForms.SaveFileDialog
|
||||
{
|
||||
FileName = filename,
|
||||
InitialDirectory = directory,
|
||||
Filter = "IRD File|*.ird|All Files|*.*"
|
||||
};
|
||||
WinForms.DialogResult result = fileDialog.ShowDialog();
|
||||
if (result == WinForms.DialogResult.OK)
|
||||
return fileDialog.FileName;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Browse for an PIC file path
|
||||
/// </summary>
|
||||
public void BrowsePICFile()
|
||||
{
|
||||
// Get the current path, if possible
|
||||
string? currentPath = CreateIRDViewModel.LogPath;
|
||||
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(CreateIRDViewModel.Options.DefaultOutputPath))
|
||||
currentPath = CreateIRDViewModel.Options.DefaultOutputPath!;
|
||||
if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = AppDomain.CurrentDomain.BaseDirectory!;
|
||||
|
||||
// Get the full directory
|
||||
var directory = Path.GetDirectoryName(Path.GetFullPath(currentPath));
|
||||
|
||||
WinForms.FileDialog fileDialog = new WinForms.OpenFileDialog
|
||||
{
|
||||
InitialDirectory = directory,
|
||||
Filter = "PIC|*.physical;*_PIC.bin;*.PIC|All Files|*.*",
|
||||
};
|
||||
WinForms.DialogResult result = fileDialog.ShowDialog();
|
||||
|
||||
if (result == WinForms.DialogResult.OK)
|
||||
{
|
||||
CreateIRDViewModel.PICPath = fileDialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display a user message using a CustomMessageBox
|
||||
/// </summary>
|
||||
/// <param name="title">Title to display to the user</param>
|
||||
/// <param name="message">Message to display to the user</param>
|
||||
/// <param name="optionCount">Number of options to display</param>
|
||||
/// <param name="flag">true for inquiry, false otherwise</param>
|
||||
/// <returns>true for positive, false for negative, null for neutral</returns>
|
||||
public bool? DisplayUserMessage(string title, string message, int optionCount, bool flag)
|
||||
{
|
||||
// Set the correct button style
|
||||
var button = optionCount switch
|
||||
{
|
||||
1 => MessageBoxButton.OK,
|
||||
2 => MessageBoxButton.YesNo,
|
||||
3 => MessageBoxButton.YesNoCancel,
|
||||
|
||||
// This should not happen, but default to "OK"
|
||||
_ => MessageBoxButton.OK,
|
||||
};
|
||||
|
||||
// Set the correct icon
|
||||
MessageBoxImage image = flag ? MessageBoxImage.Question : MessageBoxImage.Exclamation;
|
||||
|
||||
// Display and get the result
|
||||
MessageBoxResult result = CustomMessageBox.Show(this, message, title, button, image);
|
||||
return result switch
|
||||
{
|
||||
MessageBoxResult.OK or MessageBoxResult.Yes => true,
|
||||
MessageBoxResult.No => false,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CreateIRDButton Click event
|
||||
/// </summary>
|
||||
private void OnCreateIRDClick(object sender, EventArgs e)
|
||||
{
|
||||
#if NET35
|
||||
if (_KeyExpander != null) _KeyExpander.IsExpanded = false;
|
||||
if (_DiscIDExpander != null) _DiscIDExpander.IsExpanded = false;
|
||||
if (_PICExpander != null) _PICExpander.IsExpanded = false;
|
||||
#else
|
||||
KeyExpander.IsExpanded = false;
|
||||
DiscIDExpander.IsExpanded = false;
|
||||
PICExpander.IsExpanded = false;
|
||||
#endif
|
||||
string tempStatus = CreateIRDViewModel.CreateIRDStatus;
|
||||
bool[] enabledFields = CreateIRDViewModel.DisableUIFields();
|
||||
CreateIRDViewModel.CreateIRDStatus = "Creating IRD... Please Wait";
|
||||
string? outputPath = BrowseOutputFile();
|
||||
string? errorMessage = "Please provide an output path";
|
||||
if (outputPath != null)
|
||||
{
|
||||
errorMessage = CreateIRDViewModel.CreateIRD(outputPath);
|
||||
}
|
||||
if (string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
bool? checkAgain = DisplayUserMessage("IRD Create", "An IRD has been created successfully! Would you like to create another IRD?", 2, false);
|
||||
if (checkAgain == false)
|
||||
Close();
|
||||
else
|
||||
CreateIRDViewModel.ResetFields();
|
||||
}
|
||||
else
|
||||
{
|
||||
DisplayUserMessage("Failed to create IRD", errorMessage!, 1, false);
|
||||
CreateIRDViewModel.ReenableUIFields(enabledFields);
|
||||
CreateIRDViewModel.CreateIRDStatus = tempStatus;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CancelButtom Click event
|
||||
/// </summary>
|
||||
private void OnCancelClick(object sender, EventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DiscIDTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void DiscIDTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangeDiscID();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for InputPathBrowseButton Click event
|
||||
/// </summary>
|
||||
public void InputPathBrowseButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BrowseISOFile();
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangeInputPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for InputPathTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void InputPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangeInputPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for LogPathBrowseButton Click event
|
||||
/// </summary>
|
||||
public void LogPathBrowseButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BrowseLogFile();
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangeLogPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for LogPathTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void LogPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangeLogPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for KeyPathBrowseButton Click event
|
||||
/// </summary>
|
||||
public void KeyPathBrowseButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BrowseKeyFile();
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangeKeyPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for KeyPathTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void KeyPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangeKeyPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for KeyTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void KeyTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangeKey();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for PICPathBrowseButton Click event
|
||||
/// </summary>
|
||||
public void PICPathBrowseButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BrowsePICFile();
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangePICPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for PICPathTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void PICPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangePICPath();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for PICTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void PICTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangePIC();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for LayerbreakTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void LayerbreakTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (CreateIRDViewModel.CanExecuteSelectionChanged)
|
||||
CreateIRDViewModel.ChangeLayerbreak();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,18 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
|
||||
xmlns:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:redump="clr-namespace:SabreTools.RedumpLib.Data;assembly=SabreTools.RedumpLib"
|
||||
xmlns:windows="clr-namespace:MPF.UI.Core.Windows"
|
||||
mc:Ignorable="d"
|
||||
Title="Disc Information" Width="515" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical" Width="500" MaxHeight="650">
|
||||
<Grid Margin="0,10,0,0">
|
||||
@@ -29,7 +31,7 @@
|
||||
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
|
||||
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
|
||||
<Label.Content>
|
||||
<TextBlock TextAlignment="Center"><Bold>Disc Information</Bold></TextBlock>
|
||||
<Run FontWeight="Bold" Text="Disc Information" />
|
||||
</Label.Content>
|
||||
<Label.ContextMenu>
|
||||
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
@@ -74,10 +76,12 @@
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput Label="Title"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.Title, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="AlternativeTitleTextBox" Label="Alternative Title"/>
|
||||
<controls:UserInput x:Name="AlternativeTitleTextBox" Label="Alternative Title"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AlternativeTitle], Mode=TwoWay}"/>
|
||||
<controls:UserInput Label="Foreign Title (Non-Latin)"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.ForeignTitleNonLatin, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="AlternativeForeignTitleTextBox" Label="Alternative Foreign Title"/>
|
||||
<controls:UserInput x:Name="AlternativeForeignTitleTextBox" Label="Alternative Foreign Title"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AlternativeForeignTitle], Mode=TwoWay}"/>
|
||||
<controls:UserInput Label="Disc Number / Letter"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.DiscNumberLetter, Mode=TwoWay}"/>
|
||||
<controls:UserInput Label="Disc Title"
|
||||
@@ -90,8 +94,9 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label x:Name="CategoryLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Category" />
|
||||
<ComboBox x:Name="CategoryComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" ItemsSource="{Binding Categories}"
|
||||
Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
<ComboBox x:Name="CategoryComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Categories}" SelectedItem="{Binding SubmissionInfo.CommonDiscInfo.Category, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
@@ -101,8 +106,9 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label x:Name="RegionLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Region" />
|
||||
<ComboBox x:Name="RegionComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" ItemsSource="{Binding Regions}"
|
||||
Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
<ComboBox x:Name="RegionComboBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Regions}" SelectedItem="{Binding SubmissionInfo.CommonDiscInfo.Region, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
</Grid>
|
||||
|
||||
<Grid>
|
||||
@@ -112,7 +118,8 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label x:Name="LanguagesLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Languages" />
|
||||
<ComboBox x:Name="LanguagesComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch" ItemsSource="{Binding Languages}"
|
||||
<ComboBox x:Name="LanguagesComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding Languages}"
|
||||
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -129,7 +136,8 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label x:Name="LanguageSelectionLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Language Selection Via" />
|
||||
<ComboBox x:Name="LanguageSelectionComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch" ItemsSource="{Binding LanguageSelections}"
|
||||
<ComboBox x:Name="LanguageSelectionComboBox" Grid.Row="0" Grid.Column="1" Height="24" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding LanguageSelections}"
|
||||
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
@@ -164,7 +172,12 @@
|
||||
<controls:UserInput x:Name="CommentsTextBox" Label="Comments"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.Comments, Mode=TwoWay}" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top" />
|
||||
<controls:UserInput x:Name="GenreTextBox" Label="Genre"/>
|
||||
<controls:UserInput x:Name="DiscKeyTextBox" Label="Disc Key" Visibility="Collapsed"
|
||||
Text="{Binding Path=SubmissionInfo.Extras.DiscKey, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="DiscIDTextBox" Label="Disc ID" Visibility="Collapsed"
|
||||
Text="{Binding Path=SubmissionInfo.Extras.DiscID, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="GenreTextBox" Label="Genre"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Genre], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ProtectionTextBox" Label="Protection" ToolTip="CAUTION: Only edit if you know what you are doing!"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.Protection, Mode=TwoWay}" TextHeight="75" TextWrapping="Wrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
@@ -175,9 +188,11 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label/>
|
||||
<CheckBox Grid.Column="1" x:Name="PostgapTypeCheckBox" Content="Postgap type (Form 2)" Margin="0,4"/>
|
||||
<CheckBox Grid.Column="1" x:Name="PostgapTypeCheckBox" Content="Postgap type (Form 2)" Margin="0,4"
|
||||
IsChecked="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PostgapType], Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
<controls:UserInput x:Name="SeriesTextBox" Label="Series"/>
|
||||
<controls:UserInput x:Name="SeriesTextBox" Label="Series"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Series], Mode=TwoWay}"/>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="0.75*" />
|
||||
@@ -185,7 +200,8 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label/>
|
||||
<CheckBox Grid.Column="1" x:Name="VCDCheckBox" Content="V-CD" Margin="0,4"/>
|
||||
<CheckBox Grid.Column="1" x:Name="VCDCheckBox" Content="V-CD" Margin="0,4"
|
||||
IsChecked="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VCD], Mode=TwoWay}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
@@ -193,43 +209,74 @@
|
||||
<GroupBox Header="Physical Identifiers" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput x:Name="BBFCRegistrationNumberTextBox" Label="BBFC Reg. No."/>
|
||||
<controls:UserInput x:Name="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"/>
|
||||
<controls:UserInput x:Name="ISSNTextBox" Label="ISSN"/>
|
||||
<controls:UserInput x:Name="PPNTextBox" Label="PPN"/>
|
||||
<controls:UserInput x:Name="VFCCodeTextBox" Label="VFC code"/>
|
||||
<controls:UserInput x:Name="BBFCRegistrationNumberTextBox" Label="BBFC Reg. No."
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BBFCRegistrationNumber], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="DiscHologramIDTextBox" Label="Disc Hologram ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DiscHologramID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="DNASDiscIDTextBox" Label="DNAS Disc ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DNASDiscID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ISBNTextBox" Label="ISBN"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ISBN], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ISSNTextBox" Label="ISSN"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ISSN], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="PPNTextBox" Label="PPN"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PPN], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="VFCCodeTextBox" Label="VFC code"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VFCCode], Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Header="Publisher Identifiers" Margin="5" Padding="5" BorderThickness="1" BorderBrush="LightGray"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput x:Name="AcclaimIDTextBox" Label="Acclaim ID"/>
|
||||
<controls:UserInput x:Name="ActivisionIDTextBox" Label="Activision ID"/>
|
||||
<controls:UserInput x:Name="BandaiIDTextBox" Label="Bandai ID"/>
|
||||
<controls:UserInput x:Name="ElectronicArtsIDTextBox" Label="Electronic Arts ID"/>
|
||||
<controls:UserInput x:Name="FoxInteractiveIDTextBox" Label="Fox Interactive ID"/>
|
||||
<controls:UserInput x:Name="GTInteractiveIDTextBox" Label="GT Interactive ID"/>
|
||||
<controls:UserInput x:Name="JASRACIDTextBox" Label="JASRAC ID"/>
|
||||
<controls:UserInput x:Name="KingRecordsIDTextBox" Label="King Records ID"/>
|
||||
<controls:UserInput x:Name="KoeiIDTextBox" Label="Koei ID"/>
|
||||
<controls:UserInput x:Name="KonamiIDTextBox" Label="Konami ID"/>
|
||||
<controls:UserInput x:Name="LucasArtsIDTextBox" Label="LucasArts ID"/>
|
||||
<controls:UserInput x:Name="MicrosoftIDTextBox" Label="Microsoft ID"/>
|
||||
<controls:UserInput x:Name="NaganoIDTextBox" Label="Nagano ID"/>
|
||||
<controls:UserInput x:Name="NamcoIDTextBox" Label="Namco ID"/>
|
||||
<controls:UserInput x:Name="NipponIchiSoftwareIDTextBox" Label="Nippon Ichi Software ID"/>
|
||||
<controls:UserInput x:Name="OriginIDTextBox" Label="Origin ID"/>
|
||||
<controls:UserInput x:Name="PonyCanyonIDTextBox" Label="Pony Canyon ID"/>
|
||||
<controls:UserInput x:Name="SegaIDTextBox" Label="Sega ID"/>
|
||||
<controls:UserInput x:Name="SelenIDTextBox" Label="Selen ID"/>
|
||||
<controls:UserInput x:Name="SierraIDTextBox" Label="Sierra ID"/>
|
||||
<controls:UserInput x:Name="TaitoIDTextBox" Label="Taito ID"/>
|
||||
<controls:UserInput x:Name="UbisoftIDTextBox" Label="Ubisoft ID"/>
|
||||
<controls:UserInput x:Name="ValveIDTextBox" Label="Valve ID"/>
|
||||
<controls:UserInput x:Name="AcclaimIDTextBox" Label="Acclaim ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)AcclaimID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ActivisionIDTextBox" Label="Activision ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ActivisionID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="BandaiIDTextBox" Label="Bandai ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BandaiID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="CDProjektIDTextBox" Label="CD Projekt ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)CDProjektID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ElectronicArtsIDTextBox" Label="Electronic Arts ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ElectronicArtsID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="FoxInteractiveIDTextBox" Label="Fox Interactive ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)FoxInteractiveID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="GTInteractiveIDTextBox" Label="GT Interactive ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)GTInteractiveID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="JASRACIDTextBox" Label="JASRAC ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)JASRACID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="KingRecordsIDTextBox" Label="King Records ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KingRecordsID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="KoeiIDTextBox" Label="Koei ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KoeiID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="KonamiIDTextBox" Label="Konami ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)KonamiID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="LucasArtsIDTextBox" Label="LucasArts ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)LucasArtsID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="MicrosoftIDTextBox" Label="Microsoft ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)MicrosoftID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="NaganoIDTextBox" Label="Nagano ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NaganoID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="NamcoIDTextBox" Label="Namco ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NamcoID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="NipponIchiSoftwareIDTextBox" Label="Nippon Ichi Software ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)NipponIchiSoftwareID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="OriginIDTextBox" Label="Origin ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)OriginID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="PonyCanyonIDTextBox" Label="Pony Canyon ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PonyCanyonID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SegaIDTextBox" Label="Sega ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SegaID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SelenIDTextBox" Label="Selen ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SelenID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SierraIDTextBox" Label="Sierra ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SierraID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="TaitoIDTextBox" Label="Taito ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)TaitoID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="UbisoftIDTextBox" Label="Ubisoft ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)UbisoftID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ValveIDTextBox" Label="Valve ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ValveID], Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
@@ -245,31 +292,41 @@
|
||||
|
||||
<!-- Games -->
|
||||
<controls:UserInput x:Name="GamesTextBox" Label="Games" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Games], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="NetYarozeGamesTextBox" Label="Net Yaroze Games" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)NetYarozeGames], Mode=TwoWay}"/>
|
||||
|
||||
<!-- Demos -->
|
||||
<controls:UserInput x:Name="PlayableDemosTextBox" Label="Playable Demos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)PlayableDemos], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="RollingDemosTextBox" Label="Rolling Demos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)RollingDemos], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="TechDemosTextBox" Label="Tech Demos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)TechDemos], Mode=TwoWay}"/>
|
||||
|
||||
<!-- Video -->
|
||||
<controls:UserInput x:Name="GameFootageTextBox" Label="Game Footage" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)GameFootage], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="VideosTextBox" Label="Videos" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Videos], Mode=TwoWay}"/>
|
||||
|
||||
<!-- Miscellaneous -->
|
||||
<controls:UserInput x:Name="PatchesTextBox" Label="Patches" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Patches], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SavegamesTextBox" Label="Savegames" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Savegames], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ExtrasTextBox" Label="Extras" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"/>
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.ContentsSpecialFields[(redump:SiteCode)Extras], Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</TabItem>
|
||||
@@ -339,50 +396,85 @@
|
||||
<TabItem Header="Read-Only Info" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<ScrollViewer CanContentScroll="False" VerticalScrollBarVisibility="Auto" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MaxHeight="500">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<controls:UserInput x:Name="FullyMatchedID" Label="Fully Matched ID" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="FullyMatchedID" Label="Fully Matched ID" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.FullyMatchedID, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="PartiallyMatchedIDs" Label="Partially Matched ID(s)" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="DiscOffset" Label="Disc Offset" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="DMIHash" Label="DMI Hash" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="EDC" Label="EDC" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="HashData" Label="Hash Data" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.TracksAndWriteOffsets.ClrMameProData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="HashDataSize" Label="Size" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.SizeAndChecksums.Size, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="HashDataCRC" Label="CRC-32" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.SizeAndChecksums.CRC32, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="HashDataMD5" Label="MD5" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.SizeAndChecksums.MD5, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="HashDataSHA1" Label="SHA-1" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.SizeAndChecksums.SHA1, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="HashDataLayerbreak1" Label="Layerbreak 1" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.SizeAndChecksums.Layerbreak, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="HashDataLayerbreak2" Label="Layerbreak 2" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.SizeAndChecksums.Layerbreak2, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="HashDataLayerbreak3" Label="Layerbreak 3" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.SizeAndChecksums.Layerbreak3, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="AntiModchip" Label="Anti-Modchip" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.AntiModchip, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="DiscOffset" Label="Disc Offset" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.TracksAndWriteOffsets.OtherWriteOffsets, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="DMIHash" Label="DMI Hash" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)DMIHash], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="EDC" Label="EDC" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.EDC.EDC, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ErrorsCount" Label="Error(s) Count" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.ErrorsCount, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="EXEDateBuildDate" Label="EXE/Build Date" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.EXEDateBuildDate, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="Filename" Label="Filename" IsReadOnly="True"
|
||||
TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Filename], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="Header" Label="Header" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.Header, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="InternalName" Label="Internal Names" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="InternalName" Label="Internal Names" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)InternalName], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="InternalSerialName" Label="Internal Serial" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)InternalSerialName], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="Multisession" Label="Multisession" IsReadOnly="True" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"/>
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Multisession], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="LibCrypt" Label="LibCrypt" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.LibCrypt, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="LibCryptData" Label="LibCrypt Data" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.LibCryptData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="PFIHash" Label="PFI Hash" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="PFIHash" Label="PFI Hash" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)PFIHash], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="PIC" Label="PIC" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.PIC, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="PVD" Label="PVD" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.PVD, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="RingNonZeroDataStart" Label="Ring Non-Zero Data Start" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="RingNonZeroDataStart" Label="Ring Non-Zero Data Start" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)RingNonZeroDataStart], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SecuROMData" Label="SecuROM Data" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.CopyProtection.SecuROMData, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="SSHash" Label="Security Sector Hash" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="SSHash" Label="Security Sector Hash" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SSHash], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="SecuritySectorRanges" Label="Security Sector Ranges" IsReadOnly="True"
|
||||
Text="{Binding SubmissionInfo.Extras.SecuritySectorRanges, Mode=TwoWay}" TextHeight="75" TextWrapping="NoWrap"
|
||||
VerticalContentAlignmentValue="Top" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"/>
|
||||
<controls:UserInput x:Name="SSVersion" Label="Security Sector Version" IsReadOnly="True"/>
|
||||
<controls:UserInput x:Name="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"/>
|
||||
<controls:UserInput x:Name="SSVersion" Label="Security Sector Version" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)SSVersion], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="UniversalHash" Label="Universal Hash (SHA-1)" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)UniversalHash], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="VolumeLabel" Label="Volume Label" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)VolumeLabel], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="XeMID" Label="XeMID" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)XeMID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="XMID" Label="XMID" IsReadOnly="True"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)XMID], Mode=TwoWay}"/>
|
||||
|
||||
<!-- TODO: Add track/checksum data once there's a reasonable way to do so -->
|
||||
</StackPanel>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
using MPF.Core.Data;
|
||||
using MPF.UI.Core.ViewModels;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ViewModels;
|
||||
using MPF.Core.Utilities;
|
||||
using MPF.UI.Core.UserControls;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
@@ -9,18 +16,784 @@ namespace MPF.UI.Core.Windows
|
||||
/// </summary>
|
||||
public partial class DiscInformationWindow : WindowBase
|
||||
{
|
||||
#if NET35
|
||||
|
||||
#region Common Info
|
||||
|
||||
private Grid? _LanguageSelectionGrid => ItemHelper.FindChild<Grid>(this, "LanguageSelectionGrid");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Additional Info
|
||||
|
||||
private UserInput? _CommentsTextBox => ItemHelper.FindChild<UserInput>(this, "CommentsTextBox");
|
||||
private UserInput? _DiscIDTextBox => ItemHelper.FindChild<UserInput>(this, "DiscIDTextBox");
|
||||
private UserInput? _DiscKeyTextBox => ItemHelper.FindChild<UserInput>(this, "DiscKeyTextBox");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Contents
|
||||
|
||||
private UserInput? _ExtrasTextBox => ItemHelper.FindChild<UserInput>(this, "ExtrasTextBox");
|
||||
private UserInput? _GameFootageTextBox => ItemHelper.FindChild<UserInput>(this, "GameFootageTextBox");
|
||||
private UserInput? _GamesTextBox => ItemHelper.FindChild<UserInput>(this, "GamesTextBox");
|
||||
private UserInput? _GeneralContent => ItemHelper.FindChild<UserInput>(this, "GeneralContent");
|
||||
private UserInput? _NetYarozeGamesTextBox => ItemHelper.FindChild<UserInput>(this, "NetYarozeGamesTextBox");
|
||||
private UserInput? _PatchesTextBox => ItemHelper.FindChild<UserInput>(this, "PatchesTextBox");
|
||||
private UserInput? _PlayableDemosTextBox => ItemHelper.FindChild<UserInput>(this, "PlayableDemosTextBox");
|
||||
private UserInput? _RollingDemosTextBox => ItemHelper.FindChild<UserInput>(this, "RollingDemosTextBox");
|
||||
private UserInput? _SavegamesTextBox => ItemHelper.FindChild<UserInput>(this, "SavegamesTextBox");
|
||||
private UserInput? _TechDemosTextBox => ItemHelper.FindChild<UserInput>(this, "TechDemosTextBox");
|
||||
private UserInput? _VideosTextBox => ItemHelper.FindChild<UserInput>(this, "VideosTextBox");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ringcodes
|
||||
|
||||
private GroupBox? _L0Info => ItemHelper.FindChild<GroupBox>(this, "L0Info");
|
||||
private UserInput? _L0MasteringRing => ItemHelper.FindChild<UserInput>(this, "L0MasteringRing");
|
||||
private UserInput? _L0MasteringSID => ItemHelper.FindChild<UserInput>(this, "L0MasteringSID");
|
||||
private UserInput? _L0Toolstamp => ItemHelper.FindChild<UserInput>(this, "L0Toolstamp");
|
||||
private UserInput? _L0MouldSID => ItemHelper.FindChild<UserInput>(this, "L0MouldSID");
|
||||
private UserInput? _L0AdditionalMould => ItemHelper.FindChild<UserInput>(this, "L0AdditionalMould");
|
||||
private GroupBox? _L1Info => ItemHelper.FindChild<GroupBox>(this, "L1Info");
|
||||
private UserInput? _L1MasteringRing => ItemHelper.FindChild<UserInput>(this, "L1MasteringRing");
|
||||
private UserInput? _L1MasteringSID => ItemHelper.FindChild<UserInput>(this, "L1MasteringSID");
|
||||
private UserInput? _L1Toolstamp => ItemHelper.FindChild<UserInput>(this, "L1Toolstamp");
|
||||
private UserInput? _L1MouldSID => ItemHelper.FindChild<UserInput>(this, "L1MouldSID");
|
||||
private UserInput? _L1AdditionalMould => ItemHelper.FindChild<UserInput>(this, "L1AdditionalMould");
|
||||
private GroupBox? _L2Info => ItemHelper.FindChild<GroupBox>(this, "L2Info");
|
||||
private UserInput? _L2MasteringRing => ItemHelper.FindChild<UserInput>(this, "L2MasteringRing");
|
||||
private UserInput? _L2MasteringSID => ItemHelper.FindChild<UserInput>(this, "L2MasteringSID");
|
||||
private UserInput? _L2Toolstamp => ItemHelper.FindChild<UserInput>(this, "L2Toolstamp");
|
||||
private GroupBox? _L3Info => ItemHelper.FindChild<GroupBox>(this, "L3Info");
|
||||
private UserInput? _L3MasteringRing => ItemHelper.FindChild<UserInput>(this, "L3MasteringRing");
|
||||
private UserInput? _L3MasteringSID => ItemHelper.FindChild<UserInput>(this, "L3MasteringSID");
|
||||
private UserInput? _L3Toolstamp => ItemHelper.FindChild<UserInput>(this, "L3Toolstamp");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Read-Only Info
|
||||
|
||||
private UserInput? _FullyMatchedID => ItemHelper.FindChild<UserInput>(this, "FullyMatchedID");
|
||||
private UserInput? _PartiallyMatchedIDs => ItemHelper.FindChild<UserInput>(this, "PartiallyMatchedIDs");
|
||||
private UserInput? _HashData => ItemHelper.FindChild<UserInput>(this, "HashData");
|
||||
private UserInput? _HashDataSize => ItemHelper.FindChild<UserInput>(this, "HashDataSize");
|
||||
private UserInput? _HashDataCRC => ItemHelper.FindChild<UserInput>(this, "HashDataCRC");
|
||||
private UserInput? _HashDataMD5 => ItemHelper.FindChild<UserInput>(this, "HashDataMD5");
|
||||
private UserInput? _HashDataSHA1 => ItemHelper.FindChild<UserInput>(this, "HashDataSHA1");
|
||||
private UserInput? _HashDataLayerbreak1 => ItemHelper.FindChild<UserInput>(this, "HashDataLayerbreak1");
|
||||
private UserInput? _HashDataLayerbreak2 => ItemHelper.FindChild<UserInput>(this, "HashDataLayerbreak2");
|
||||
private UserInput? _HashDataLayerbreak3 => ItemHelper.FindChild<UserInput>(this, "HashDataLayerbreak3");
|
||||
private UserInput? _AntiModchip => ItemHelper.FindChild<UserInput>(this, "AntiModchip");
|
||||
private UserInput? _DiscOffset => ItemHelper.FindChild<UserInput>(this, "DiscOffset");
|
||||
private UserInput? _DMIHash => ItemHelper.FindChild<UserInput>(this, "DMIHash");
|
||||
private UserInput? _EDC => ItemHelper.FindChild<UserInput>(this, "EDC");
|
||||
private UserInput? _ErrorsCount => ItemHelper.FindChild<UserInput>(this, "ErrorsCount");
|
||||
private UserInput? _EXEDateBuildDate => ItemHelper.FindChild<UserInput>(this, "EXEDateBuildDate");
|
||||
private UserInput? _Filename => ItemHelper.FindChild<UserInput>(this, "Filename");
|
||||
private UserInput? _Header => ItemHelper.FindChild<UserInput>(this, "Header");
|
||||
private UserInput? _InternalName => ItemHelper.FindChild<UserInput>(this, "InternalName");
|
||||
private UserInput? _InternalSerialName => ItemHelper.FindChild<UserInput>(this, "InternalSerialName");
|
||||
private UserInput? _Multisession => ItemHelper.FindChild<UserInput>(this, "Multisession");
|
||||
private UserInput? _LibCrypt => ItemHelper.FindChild<UserInput>(this, "LibCrypt");
|
||||
private UserInput? _LibCryptData => ItemHelper.FindChild<UserInput>(this, "LibCryptData");
|
||||
private UserInput? _PFIHash => ItemHelper.FindChild<UserInput>(this, "PFIHash");
|
||||
private UserInput? _PIC => ItemHelper.FindChild<UserInput>(this, "PIC");
|
||||
private UserInput? _PVD => ItemHelper.FindChild<UserInput>(this, "PVD");
|
||||
private UserInput? _RingNonZeroDataStart => ItemHelper.FindChild<UserInput>(this, "RingNonZeroDataStart");
|
||||
private UserInput? _SecuROMData => ItemHelper.FindChild<UserInput>(this, "SecuROMData");
|
||||
private UserInput? _SSHash => ItemHelper.FindChild<UserInput>(this, "SSHash");
|
||||
private UserInput? _SecuritySectorRanges => ItemHelper.FindChild<UserInput>(this, "SecuritySectorRanges");
|
||||
private UserInput? _SSVersion => ItemHelper.FindChild<UserInput>(this, "SSVersion");
|
||||
private UserInput? _UniversalHash => ItemHelper.FindChild<UserInput>(this, "UniversalHash");
|
||||
private UserInput? _VolumeLabel => ItemHelper.FindChild<UserInput>(this, "VolumeLabel");
|
||||
private UserInput? _XeMID => ItemHelper.FindChild<UserInput>(this, "XeMID");
|
||||
private UserInput? _XMID => ItemHelper.FindChild<UserInput>(this, "XMID");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Accept / Cancel
|
||||
|
||||
private Button? _AcceptButton => ItemHelper.FindChild<Button>(this, "AcceptButton");
|
||||
private Button? _CancelButton => ItemHelper.FindChild<Button>(this, "CancelButton");
|
||||
private Button? _RingCodeGuideButton => ItemHelper.FindChild<Button>(this, "RingCodeGuideButton");
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Read-only access to the current disc information view model
|
||||
/// </summary>
|
||||
public DiscInformationViewModel DiscInformationViewModel => DataContext as DiscInformationViewModel;
|
||||
public DiscInformationViewModel DiscInformationViewModel => DataContext as DiscInformationViewModel ?? new DiscInformationViewModel(new Options(), new SubmissionInfo());
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public DiscInformationWindow(Options options, SubmissionInfo submissionInfo)
|
||||
public DiscInformationWindow(Options options, SubmissionInfo? submissionInfo)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
DataContext = new DiscInformationViewModel(this, options, submissionInfo);
|
||||
#endif
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
var chrome = new System.Windows.Shell.WindowChrome
|
||||
{
|
||||
CaptionHeight = 0,
|
||||
ResizeBorderThickness = new Thickness(0),
|
||||
};
|
||||
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
|
||||
#endif
|
||||
|
||||
DataContext = new DiscInformationViewModel(options, submissionInfo);
|
||||
DiscInformationViewModel.Load();
|
||||
|
||||
// Limit lists, if necessary
|
||||
if (options.EnableRedumpCompatibility)
|
||||
{
|
||||
DiscInformationViewModel.SetRedumpRegions();
|
||||
DiscInformationViewModel.SetRedumpLanguages();
|
||||
}
|
||||
|
||||
// Add handlers
|
||||
#if NET35
|
||||
_AcceptButton!.Click += OnAcceptClick;
|
||||
_CancelButton!.Click += OnCancelClick;
|
||||
_RingCodeGuideButton!.Click += OnRingCodeGuideClick;
|
||||
#else
|
||||
AcceptButton.Click += OnAcceptClick;
|
||||
CancelButton.Click += OnCancelClick;
|
||||
RingCodeGuideButton.Click += OnRingCodeGuideClick;
|
||||
#endif
|
||||
|
||||
// Update UI with new values
|
||||
ManipulateFields(options, submissionInfo);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Manipulate fields based on the current disc
|
||||
/// </summary>
|
||||
private void ManipulateFields(Options options, SubmissionInfo? submissionInfo)
|
||||
{
|
||||
// Enable tabs in all fields, if required
|
||||
if (options.EnableTabsInInputFields)
|
||||
EnableTabsInInputFields();
|
||||
|
||||
// Hide read-only fields that don't have values set
|
||||
HideReadOnlyFields(submissionInfo);
|
||||
|
||||
// Different media types mean different fields available
|
||||
UpdateFromDiscType(submissionInfo);
|
||||
|
||||
// Different systems mean different fields available
|
||||
UpdateFromSystemType(submissionInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable tab entry on supported fields
|
||||
/// </summary>
|
||||
/// TODO: See if these can be done by binding
|
||||
private void EnableTabsInInputFields()
|
||||
{
|
||||
// Additional Information
|
||||
#if NET35
|
||||
_CommentsTextBox!.Tab = true;
|
||||
#else
|
||||
CommentsTextBox.Tab = true;
|
||||
#endif
|
||||
|
||||
// Contents
|
||||
#if NET35
|
||||
_GeneralContent!.Tab = true;
|
||||
_GamesTextBox!.Tab = true;
|
||||
_NetYarozeGamesTextBox!.Tab = true;
|
||||
_PlayableDemosTextBox!.Tab = true;
|
||||
_RollingDemosTextBox!.Tab = true;
|
||||
_TechDemosTextBox!.Tab = true;
|
||||
_GameFootageTextBox!.Tab = true;
|
||||
_VideosTextBox!.Tab = true;
|
||||
_PatchesTextBox!.Tab = true;
|
||||
_SavegamesTextBox!.Tab = true;
|
||||
_ExtrasTextBox!.Tab = true;
|
||||
#else
|
||||
GeneralContent.Tab = true;
|
||||
GamesTextBox.Tab = true;
|
||||
NetYarozeGamesTextBox.Tab = true;
|
||||
PlayableDemosTextBox.Tab = true;
|
||||
RollingDemosTextBox.Tab = true;
|
||||
TechDemosTextBox.Tab = true;
|
||||
GameFootageTextBox.Tab = true;
|
||||
VideosTextBox.Tab = true;
|
||||
PatchesTextBox.Tab = true;
|
||||
SavegamesTextBox.Tab = true;
|
||||
ExtrasTextBox.Tab = true;
|
||||
#endif
|
||||
|
||||
// L0
|
||||
#if NET35
|
||||
_L0MasteringRing!.Tab = true;
|
||||
_L0MasteringSID!.Tab = true;
|
||||
_L0Toolstamp!.Tab = true;
|
||||
_L0MouldSID!.Tab = true;
|
||||
_L0AdditionalMould!.Tab = true;
|
||||
#else
|
||||
L0MasteringRing.Tab = true;
|
||||
L0MasteringSID.Tab = true;
|
||||
L0Toolstamp.Tab = true;
|
||||
L0MouldSID.Tab = true;
|
||||
L0AdditionalMould.Tab = true;
|
||||
#endif
|
||||
|
||||
// L1
|
||||
#if NET35
|
||||
_L1MasteringRing!.Tab = true;
|
||||
_L1MasteringSID!.Tab = true;
|
||||
_L1Toolstamp!.Tab = true;
|
||||
_L1MouldSID!.Tab = true;
|
||||
_L1AdditionalMould!.Tab = true;
|
||||
#else
|
||||
L1MasteringRing.Tab = true;
|
||||
L1MasteringSID.Tab = true;
|
||||
L1Toolstamp.Tab = true;
|
||||
L1MouldSID.Tab = true;
|
||||
L1AdditionalMould.Tab = true;
|
||||
#endif
|
||||
|
||||
// L2
|
||||
#if NET35
|
||||
_L2MasteringRing!.Tab = true;
|
||||
_L2MasteringSID!.Tab = true;
|
||||
_L2Toolstamp!.Tab = true;
|
||||
#else
|
||||
L2MasteringRing.Tab = true;
|
||||
L2MasteringSID.Tab = true;
|
||||
L2Toolstamp.Tab = true;
|
||||
#endif
|
||||
|
||||
// L3
|
||||
#if NET35
|
||||
_L3MasteringRing!.Tab = true;
|
||||
_L3MasteringSID!.Tab = true;
|
||||
_L3Toolstamp!.Tab = true;
|
||||
#else
|
||||
L3MasteringRing.Tab = true;
|
||||
L3MasteringSID.Tab = true;
|
||||
L3Toolstamp.Tab = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide any optional, read-only fields if they don't have a value
|
||||
/// </summary>
|
||||
/// TODO: Figure out how to bind the PartiallyMatchedIDs array to a text box
|
||||
/// TODO: Convert visibility to a binding
|
||||
private void HideReadOnlyFields(SubmissionInfo? submissionInfo)
|
||||
{
|
||||
// If there's no submission information
|
||||
if (submissionInfo == null)
|
||||
return;
|
||||
|
||||
#if NET35
|
||||
if (submissionInfo.FullyMatchedID == null)
|
||||
_FullyMatchedID!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.PartiallyMatchedIDs == null)
|
||||
_PartiallyMatchedIDs!.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
_PartiallyMatchedIDs!.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs.Select(i => i.ToString()).ToArray());
|
||||
if (string.IsNullOrEmpty(submissionInfo.TracksAndWriteOffsets?.ClrMameProData))
|
||||
_HashData!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.SizeAndChecksums?.Size == null || submissionInfo.SizeAndChecksums.Size == 0)
|
||||
_HashDataSize!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.SizeAndChecksums?.CRC32))
|
||||
_HashDataCRC!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.SizeAndChecksums?.MD5))
|
||||
_HashDataMD5!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.SizeAndChecksums?.SHA1))
|
||||
_HashDataSHA1!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.SizeAndChecksums?.Layerbreak == null || submissionInfo.SizeAndChecksums.Layerbreak == 0)
|
||||
_HashDataLayerbreak1!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.SizeAndChecksums?.Layerbreak2 == null || submissionInfo.SizeAndChecksums.Layerbreak2 == 0)
|
||||
_HashDataLayerbreak2!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.SizeAndChecksums?.Layerbreak3 == null || submissionInfo.SizeAndChecksums.Layerbreak3 == 0)
|
||||
_HashDataLayerbreak3!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CopyProtection?.AntiModchip == null)
|
||||
_AntiModchip!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.TracksAndWriteOffsets?.OtherWriteOffsets == null)
|
||||
_DiscOffset!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.Keys?.Contains(SiteCode.DMIHash) != true)
|
||||
_DMIHash!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.EDC?.EDC == null)
|
||||
_EDC!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.ErrorsCount))
|
||||
_ErrorsCount!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
|
||||
_EXEDateBuildDate!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
|
||||
_Filename!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.Header))
|
||||
_Header!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
|
||||
_InternalName!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalSerialName) != true)
|
||||
_InternalSerialName!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Multisession) != true)
|
||||
_Multisession!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CopyProtection?.LibCrypt == null)
|
||||
_LibCrypt!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.LibCryptData))
|
||||
_LibCryptData!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
|
||||
_PFIHash!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.PIC))
|
||||
_PIC!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.PVD))
|
||||
_PVD!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
|
||||
_RingNonZeroDataStart!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.SecuROMData))
|
||||
_SecuROMData!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
|
||||
_SSHash!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.SecuritySectorRanges))
|
||||
_SecuritySectorRanges!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
|
||||
_SSVersion!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) != true)
|
||||
_UniversalHash!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.VolumeLabel) != true)
|
||||
_VolumeLabel!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XeMID) != true)
|
||||
_XeMID!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
|
||||
_XMID!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
if (submissionInfo.FullyMatchedID == null)
|
||||
FullyMatchedID.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.PartiallyMatchedIDs == null)
|
||||
PartiallyMatchedIDs.Visibility = Visibility.Collapsed;
|
||||
else
|
||||
PartiallyMatchedIDs.Text = string.Join(", ", submissionInfo.PartiallyMatchedIDs.Select(i => i.ToString()).ToArray());
|
||||
if (string.IsNullOrEmpty(submissionInfo.TracksAndWriteOffsets?.ClrMameProData))
|
||||
HashData!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.SizeAndChecksums?.Size == null)
|
||||
HashDataSize!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.SizeAndChecksums?.CRC32))
|
||||
HashDataCRC!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.SizeAndChecksums?.MD5))
|
||||
HashDataMD5!.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.SizeAndChecksums?.SHA1))
|
||||
HashDataSHA1!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.SizeAndChecksums?.Layerbreak == null || submissionInfo.SizeAndChecksums.Layerbreak == 0)
|
||||
HashDataLayerbreak1!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.SizeAndChecksums?.Layerbreak2 == null || submissionInfo.SizeAndChecksums.Layerbreak2 == 0)
|
||||
HashDataLayerbreak2!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.SizeAndChecksums?.Layerbreak3 == null || submissionInfo.SizeAndChecksums.Layerbreak3 == 0)
|
||||
HashDataLayerbreak3!.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CopyProtection?.AntiModchip == null)
|
||||
AntiModchip.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.TracksAndWriteOffsets?.OtherWriteOffsets == null)
|
||||
DiscOffset.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.Keys?.Contains(SiteCode.DMIHash) != true)
|
||||
DMIHash.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.EDC?.EDC == null)
|
||||
EDC.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.ErrorsCount))
|
||||
ErrorsCount.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
|
||||
EXEDateBuildDate.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
|
||||
Filename.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.Header))
|
||||
Header.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
|
||||
InternalName.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalSerialName) != true)
|
||||
InternalSerialName.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Multisession) != true)
|
||||
Multisession.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CopyProtection?.LibCrypt == null)
|
||||
LibCrypt.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.LibCryptData))
|
||||
LibCryptData.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
|
||||
PFIHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.PIC))
|
||||
PIC.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.PVD))
|
||||
PVD.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
|
||||
RingNonZeroDataStart.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.SecuROMData))
|
||||
SecuROMData.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
|
||||
SSHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.SecuritySectorRanges))
|
||||
SecuritySectorRanges.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
|
||||
SSVersion.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.UniversalHash) != true)
|
||||
UniversalHash.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.VolumeLabel) != true)
|
||||
VolumeLabel.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XeMID) != true)
|
||||
XeMID.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
|
||||
XMID.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visible fields and sections based on the media type
|
||||
/// </summary>
|
||||
/// TODO: See if these can be done by binding
|
||||
private void UpdateFromDiscType(SubmissionInfo? submissionInfo)
|
||||
{
|
||||
// Sony-printed discs have layers in the opposite order
|
||||
var system = submissionInfo?.CommonDiscInfo?.System;
|
||||
bool reverseOrder = system.HasReversedRingcodes();
|
||||
|
||||
switch (submissionInfo?.CommonDiscInfo?.Media)
|
||||
{
|
||||
case DiscType.CD:
|
||||
case DiscType.GDROM:
|
||||
#if NET35
|
||||
_L0Info!.Header = "Data Side";
|
||||
_L0MasteringRing!.Label = "Mastering Ring";
|
||||
_L0MasteringSID!.Label = "Mastering SID";
|
||||
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
_L0MouldSID!.Label = "Mould SID";
|
||||
_L0AdditionalMould!.Label = "Additional Mould";
|
||||
#else
|
||||
L0Info.Header = "Data Side";
|
||||
L0MasteringRing.Label = "Mastering Ring";
|
||||
L0MasteringSID.Label = "Mastering SID";
|
||||
L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L0MouldSID.Label = "Mould SID";
|
||||
L0AdditionalMould.Label = "Additional Mould";
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
_L1Info!.Header = "Label Side";
|
||||
_L1MasteringRing!.Visibility = Visibility.Collapsed;
|
||||
_L1MasteringSID!.Visibility = Visibility.Collapsed;
|
||||
_L1Toolstamp!.Visibility = Visibility.Collapsed;
|
||||
_L1MouldSID!.Label = "Mould SID";
|
||||
_L1AdditionalMould!.Label = "Additional Mould";
|
||||
#else
|
||||
L1Info.Header = "Label Side";
|
||||
L1MasteringRing.Visibility = Visibility.Collapsed;
|
||||
L1MasteringSID.Visibility = Visibility.Collapsed;
|
||||
L1Toolstamp.Visibility = Visibility.Collapsed;
|
||||
L1MouldSID.Label = "Mould SID";
|
||||
L1AdditionalMould.Label = "Additional Mould";
|
||||
#endif
|
||||
break;
|
||||
|
||||
case DiscType.DVD5:
|
||||
case DiscType.DVD9:
|
||||
case DiscType.HDDVDSL:
|
||||
case DiscType.HDDVDDL:
|
||||
case DiscType.BD25:
|
||||
case DiscType.BD33:
|
||||
case DiscType.BD50:
|
||||
case DiscType.BD66:
|
||||
case DiscType.BD100:
|
||||
case DiscType.BD128:
|
||||
case DiscType.NintendoGameCubeGameDisc:
|
||||
case DiscType.NintendoWiiOpticalDiscSL:
|
||||
case DiscType.NintendoWiiOpticalDiscDL:
|
||||
case DiscType.NintendoWiiUOpticalDiscSL:
|
||||
case DiscType.UMDSL:
|
||||
case DiscType.UMDDL:
|
||||
// Quad-layer discs
|
||||
if (submissionInfo?.SizeAndChecksums?.Layerbreak3 != default(long))
|
||||
{
|
||||
#if NET35
|
||||
_L2Info!.Visibility = Visibility.Visible;
|
||||
_L3Info!.Visibility = Visibility.Visible;
|
||||
#else
|
||||
L2Info.Visibility = Visibility.Visible;
|
||||
L3Info.Visibility = Visibility.Visible;
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
_L0MasteringRing!.Label = "Mastering Ring";
|
||||
_L0MasteringSID!.Label = "Mastering SID";
|
||||
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
_L0MouldSID!.Label = "Data Side Mould SID";
|
||||
_L0AdditionalMould!.Label = "Data Side Additional Mould";
|
||||
#else
|
||||
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
L0MasteringRing.Label = "Mastering Ring";
|
||||
L0MasteringSID.Label = "Mastering SID";
|
||||
L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L0MouldSID.Label = "Data Side Mould SID";
|
||||
L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
_L1Info!.Header = "Layer 1";
|
||||
_L1MasteringRing!.Label = "Mastering Ring";
|
||||
_L1MasteringSID!.Label = "Mastering SID";
|
||||
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
_L1MouldSID!.Label = "Label Side Mould SID";
|
||||
_L1AdditionalMould!.Label = "Label Side Additional Mould";
|
||||
#else
|
||||
L1Info.Header = "Layer 1";
|
||||
L1MasteringRing.Label = "Mastering Ring";
|
||||
L1MasteringSID.Label = "Mastering SID";
|
||||
L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L1MouldSID.Label = "Label Side Mould SID";
|
||||
L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
_L2Info!.Header = "Layer 2";
|
||||
_L2MasteringRing!.Label = "Mastering Ring";
|
||||
_L2MasteringSID!.Label = "Mastering SID";
|
||||
_L2Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
#else
|
||||
L2Info.Header = "Layer 2";
|
||||
L2MasteringRing.Label = "Mastering Ring";
|
||||
L2MasteringSID.Label = "Mastering SID";
|
||||
L2Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
_L3Info!.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
|
||||
_L3MasteringRing!.Label = "Mastering Ring";
|
||||
_L3MasteringSID!.Label = "Mastering SID";
|
||||
_L3Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
#else
|
||||
L3Info.Header = reverseOrder ? "Layer 3 (Inner)" : "Layer 3 (Outer)";
|
||||
L3MasteringRing.Label = "Mastering Ring";
|
||||
L3MasteringSID.Label = "Mastering SID";
|
||||
L3Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Triple-layer discs
|
||||
else if (submissionInfo?.SizeAndChecksums?.Layerbreak2 != default(long))
|
||||
{
|
||||
#if NET35
|
||||
_L2Info!.Visibility = Visibility.Visible;
|
||||
#else
|
||||
L2Info.Visibility = Visibility.Visible;
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
_L0MasteringRing!.Label = "Mastering Ring";
|
||||
_L0MasteringSID!.Label = "Mastering SID";
|
||||
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
_L0MouldSID!.Label = "Data Side Mould SID";
|
||||
_L0AdditionalMould!.Label = "Data Side Additional Mould";
|
||||
#else
|
||||
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
L0MasteringRing.Label = "Mastering Ring";
|
||||
L0MasteringSID.Label = "Mastering SID";
|
||||
L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L0MouldSID.Label = "Data Side Mould SID";
|
||||
L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
_L1Info!.Header = "Layer 1";
|
||||
_L1MasteringRing!.Label = "Mastering Ring";
|
||||
_L1MasteringSID!.Label = "Mastering SID";
|
||||
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
_L1MouldSID!.Label = "Label Side Mould SID";
|
||||
_L1AdditionalMould!.Label = "Label Side Additional Mould";
|
||||
#else
|
||||
L1Info.Header = "Layer 1";
|
||||
L1MasteringRing.Label = "Mastering Ring";
|
||||
L1MasteringSID.Label = "Mastering SID";
|
||||
L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L1MouldSID.Label = "Label Side Mould SID";
|
||||
L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
_L2Info!.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
|
||||
_L2MasteringRing!.Label = "Mastering Ring";
|
||||
_L2MasteringSID!.Label = "Mastering SID";
|
||||
_L2Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
#else
|
||||
L2Info.Header = reverseOrder ? "Layer 2 (Inner)" : "Layer 2 (Outer)";
|
||||
L2MasteringRing.Label = "Mastering Ring";
|
||||
L2MasteringSID.Label = "Mastering SID";
|
||||
L2Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Double-layer discs
|
||||
else if (submissionInfo?.SizeAndChecksums?.Layerbreak != default(long))
|
||||
{
|
||||
#if NET35
|
||||
_L0Info!.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
_L0MasteringRing!.Label = "Mastering Ring";
|
||||
_L0MasteringSID!.Label = "Mastering SID";
|
||||
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
_L0MouldSID!.Label = "Data Side Mould SID";
|
||||
_L0AdditionalMould!.Label = "Data Side Additional Mould";
|
||||
#else
|
||||
L0Info.Header = reverseOrder ? "Layer 0 (Outer)" : "Layer 0 (Inner)";
|
||||
L0MasteringRing.Label = "Mastering Ring";
|
||||
L0MasteringSID.Label = "Mastering SID";
|
||||
L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L0MouldSID.Label = "Data Side Mould SID";
|
||||
L0AdditionalMould.Label = "Data Side Additional Mould";
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
_L1Info!.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
|
||||
_L1MasteringRing!.Label = "Mastering Ring";
|
||||
_L1MasteringSID!.Label = "Mastering SID";
|
||||
_L1Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
_L1MouldSID!.Label = "Label Side Mould SID";
|
||||
_L1AdditionalMould!.Label = "Label Side Additional Mould";
|
||||
#else
|
||||
L1Info.Header = reverseOrder ? "Layer 1 (Inner)" : "Layer 1 (Outer)";
|
||||
L1MasteringRing.Label = "Mastering Ring";
|
||||
L1MasteringSID.Label = "Mastering SID";
|
||||
L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L1MouldSID.Label = "Label Side Mould SID";
|
||||
L1AdditionalMould.Label = "Label Side Additional Mould";
|
||||
#endif
|
||||
}
|
||||
|
||||
// Single-layer discs
|
||||
else
|
||||
{
|
||||
#if NET35
|
||||
_L0Info!.Header = "Data Side";
|
||||
_L0MasteringRing!.Label = "Mastering Ring";
|
||||
_L0MasteringSID!.Label = "Mastering SID";
|
||||
_L0Toolstamp!.Label = "Toolstamp/Mastering Code";
|
||||
_L0MouldSID!.Label = "Mould SID";
|
||||
_L0AdditionalMould!.Label = "Additional Mould";
|
||||
#else
|
||||
L0Info.Header = "Data Side";
|
||||
L0MasteringRing.Label = "Mastering Ring";
|
||||
L0MasteringSID.Label = "Mastering SID";
|
||||
L0Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
L0MouldSID.Label = "Mould SID";
|
||||
L0AdditionalMould.Label = "Additional Mould";
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
_L1Info!.Header = "Label Side";
|
||||
_L1MasteringRing!.Visibility = Visibility.Collapsed;
|
||||
_L1MasteringSID!.Visibility = Visibility.Collapsed;
|
||||
_L1Toolstamp!.Visibility = Visibility.Collapsed;
|
||||
_L1MouldSID!.Label = "Mould SID";
|
||||
_L1AdditionalMould!.Label = "Additional Mould";
|
||||
#else
|
||||
L1Info.Header = "Label Side";
|
||||
L1MasteringRing.Visibility = Visibility.Collapsed;
|
||||
L1MasteringSID.Visibility = Visibility.Collapsed;
|
||||
L1Toolstamp.Visibility = Visibility.Collapsed;
|
||||
L1MouldSID.Label = "Mould SID";
|
||||
L1AdditionalMould.Label = "Additional Mould";
|
||||
#endif
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
// All other media we assume to have no rings
|
||||
default:
|
||||
#if NET35
|
||||
_L0Info!.Visibility = Visibility.Collapsed;
|
||||
_L1Info!.Visibility = Visibility.Collapsed;
|
||||
_L2Info!.Visibility = Visibility.Collapsed;
|
||||
_L3Info!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
L0Info.Visibility = Visibility.Collapsed;
|
||||
L1Info.Visibility = Visibility.Collapsed;
|
||||
L2Info.Visibility = Visibility.Collapsed;
|
||||
L3Info.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update visible fields and sections based on the system type
|
||||
/// </summary>
|
||||
/// TODO: See if these can be done by binding
|
||||
private void UpdateFromSystemType(SubmissionInfo? submissionInfo)
|
||||
{
|
||||
var system = submissionInfo?.CommonDiscInfo?.System;
|
||||
switch (system)
|
||||
{
|
||||
case RedumpSystem.NintendoWiiU:
|
||||
#if NET35
|
||||
_DiscKeyTextBox!.Visibility = Visibility.Visible;
|
||||
#else
|
||||
DiscKeyTextBox.Visibility = Visibility.Visible;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
#if NET35
|
||||
_LanguageSelectionGrid!.Visibility = Visibility.Visible;
|
||||
#else
|
||||
LanguageSelectionGrid.Visibility = Visibility.Visible;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
#if NET35
|
||||
_DiscKeyTextBox!.Visibility = Visibility.Visible;
|
||||
_DiscIDTextBox!.Visibility = Visibility.Visible;
|
||||
#else
|
||||
DiscKeyTextBox.Visibility = Visibility.Visible;
|
||||
DiscIDTextBox.Visibility = Visibility.Visible;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AcceptButton Click event
|
||||
/// </summary>
|
||||
private void OnAcceptClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DiscInformationViewModel.Save();
|
||||
DialogResult = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CancelButton Click event
|
||||
/// </summary>
|
||||
private void OnCancelClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for RingCodeGuideButton Click event
|
||||
/// </summary>
|
||||
private void OnRingCodeGuideClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var ringCodeGuideWindow = new RingCodeGuideWindow()
|
||||
{
|
||||
Owner = this,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
ringCodeGuideWindow.Show();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:controls="clr-namespace:MPF.UI.Core.UserControls"
|
||||
xmlns:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
|
||||
xmlns:viewModels="clr-namespace:MPF.UI.Core.ViewModels"
|
||||
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
|
||||
mc:Ignorable="d"
|
||||
Title="Media Preservation Frontend" Width="600" WindowStyle="None"
|
||||
WindowStartupLocation="CenterScreen" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Window.DataContext>
|
||||
<viewModels:MainViewModel/>
|
||||
</Window.DataContext>
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
@@ -49,10 +49,21 @@
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}">
|
||||
<MenuItem x:Name="CheckDumpMenuItem" Header="_Check Dump" HorizontalAlignment="Left" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"
|
||||
IsEnabled="{Binding CheckDumpMenuItemEnabled}"/>
|
||||
<MenuItem x:Name="CreateIRDMenuItem" Header="_Create PS3 IRD" HorizontalAlignment="Left" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"
|
||||
IsEnabled="{Binding CreateIRDMenuItemEnabled}"/>
|
||||
<MenuItem x:Name="OptionsMenuItem" Header="_Options" HorizontalAlignment="Left" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Template="{DynamicResource CustomMenuItemTemplate}" />
|
||||
Template="{DynamicResource CustomMenuItemTemplate}"
|
||||
IsEnabled="{Binding OptionsMenuItemEnabled}"/>
|
||||
<MenuItem x:Name="DebugViewMenuItem" Header="_Debug Info Window" HorizontalAlignment="Left" Width="185"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
@@ -75,7 +86,7 @@
|
||||
</StackPanel>
|
||||
<Label Panel.ZIndex="0" Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
|
||||
<Label.Content>
|
||||
<TextBlock TextAlignment="Center"><Bold>Media Preservation Frontend</Bold></TextBlock>
|
||||
<TextBlock TextAlignment="Center"><Run FontWeight="Bold" Text="Media Preservation Frontend" /></TextBlock>
|
||||
</Label.Content>
|
||||
<Label.ContextMenu>
|
||||
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
@@ -123,7 +134,9 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label x:Name="SystemMediaTypeLabel" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" Content="System/Media Type" />
|
||||
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left" ItemsSource="{Binding Systems}" SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox x:Name="SystemTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding Systems}" SelectedItem="{Binding Path=CurrentSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding SystemTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ComboBoxItem}">
|
||||
<Style.Triggers>
|
||||
@@ -134,15 +147,21 @@
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
<ComboBox x:Name="MediaTypeComboBox" Grid.Row="0" Grid.Column="1" Height="22" Width="140" HorizontalAlignment="Right"
|
||||
ItemsSource="{Binding MediaTypes}" SelectedItem="{Binding Path=CurrentMediaType, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding MediaTypeComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="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" />
|
||||
<TextBox x:Name="OutputPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" Width="345" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding OutputPath, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding OutputPathTextBoxEnabled}" />
|
||||
<Button x:Name="OutputPathBrowseButton" Grid.Row="1" Grid.Column="1" Height="22" Width="50" HorizontalAlignment="Right" Content="Browse"
|
||||
Style="{DynamicResource CustomButtonStyle}"/>
|
||||
IsEnabled="{Binding OutputPathBrowseButtonEnabled}" Style="{DynamicResource CustomButtonStyle}"/>
|
||||
|
||||
<Label x:Name="DriveLetterLabel" Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" Content="Drive Letter"/>
|
||||
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="2" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox x:Name="DriveLetterComboBox" Grid.Row="2" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding Drives}" SelectedItem="{Binding Path=CurrentDrive, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding DriveLetterComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding Letter}" />
|
||||
@@ -151,37 +170,48 @@
|
||||
</ComboBox>
|
||||
|
||||
<Label x:Name="DriveSpeedLabel" Grid.Row="3" Grid.Column="0" VerticalAlignment="Center" Content="Drive Speed"/>
|
||||
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
<ComboBox x:Name="DriveSpeedComboBox" Grid.Row="3" Grid.Column="1" Height="22" Width="60" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding DriveSpeeds}" SelectedItem="{Binding Path=DriveSpeed, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding DriveSpeedComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="DumpingProgramLabel" Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" Content="Dumping Program"/>
|
||||
<ComboBox x:Name="DumpingProgramComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<ComboBox x:Name="DumpingProgramComboBox" Grid.Row="4" Grid.Column="1" Height="22" Width="250" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding InternalPrograms}" SelectedItem="{Binding Path=CurrentProgram, Converter={StaticResource ElementConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding DumpingProgramComboBoxEnabled}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label x:Name="ParametersLabel" Grid.Row="5" Grid.Column="0" VerticalAlignment="Center" Content="Parameters"/>
|
||||
<TextBox x:Name="ParametersTextBox" Grid.Row="5" Grid.Column="1" Height="22" Width="370" HorizontalAlignment="Left" IsEnabled="False" VerticalContentAlignment="Center" />
|
||||
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right" IsChecked="False" />
|
||||
<TextBox x:Name="ParametersTextBox" Grid.Row="5" Grid.Column="1" Height="22" Width="370" HorizontalAlignment="Left" VerticalContentAlignment="Center"
|
||||
Text="{Binding Parameters, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding ParametersCheckBoxEnabled}"/>
|
||||
<CheckBox x:Name="EnableParametersCheckBox" Grid.Row="5" Grid.Column="1" Height="22" HorizontalAlignment="Right"
|
||||
IsChecked="{Binding ParametersCheckBoxEnabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding EnableParametersCheckBoxEnabled}"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Controls">
|
||||
<UniformGrid Columns="4" Margin="5,5,5,5">
|
||||
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True" Content="Start Dumping"
|
||||
IsEnabled="False" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="StartStopButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" IsDefault="True"
|
||||
Content="{Binding StartStopButtonText, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
IsEnabled="{Binding StartStopButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="MediaScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for discs"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
IsEnabled="{Binding MediaScanButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="UpdateVolumeLabel" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Update Label"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
IsEnabled="{Binding UpdateVolumeLabelEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button x:Name="CopyProtectScanButton" Height="22" Width="125" VerticalAlignment="Center" HorizontalAlignment="Center" Content="Scan for protection"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
IsEnabled="{Binding CopyProtectScanButtonEnabled}" Style="{DynamicResource CustomButtonStyle}" />
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" Header="Status">
|
||||
<UniformGrid Margin="5,5,5,5" Grid.ColumnSpan="2">
|
||||
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center" Text="Waiting for media..." />
|
||||
<TextBlock x:Name="StatusLabel" VerticalAlignment="Center" HorizontalAlignment="Center"
|
||||
Text="{Binding Status, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<Expander Margin="5,5 5 5" MaxHeight="300" HorizontalAlignment="Stretch" Header="Log Output" x:Name="LogPanel">
|
||||
<Expander Margin="5,5 5 5" MaxHeight="300" HorizontalAlignment="Stretch" Header="Log Output" x:Name="LogPanel"
|
||||
IsExpanded="{Binding LogPanelExpanded}">
|
||||
<controls:LogOutput x:Name="LogOutput"/>
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
using System;
|
||||
using MPF.UI.Core.ViewModels;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using MPF.Core.UI.ViewModels;
|
||||
using MPF.UI.Core.UserControls;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using WPFCustomMessageBox;
|
||||
using WinForms = System.Windows.Forms;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
@@ -8,12 +18,70 @@ namespace MPF.UI.Core.Windows
|
||||
/// <summary>
|
||||
/// Read-only access to the current main view model
|
||||
/// </summary>
|
||||
public MainViewModel MainViewModel => DataContext as MainViewModel;
|
||||
public MainViewModel MainViewModel => DataContext as MainViewModel ?? new MainViewModel();
|
||||
|
||||
#if NET35
|
||||
|
||||
#region Top Menu Bar
|
||||
|
||||
private MenuItem? _AboutMenuItem => ItemHelper.FindChild<MenuItem>(this, "AboutMenuItem");
|
||||
private MenuItem? _AppExitMenuItem => ItemHelper.FindChild<MenuItem>(this, "AppExitMenuItem");
|
||||
private MenuItem? _CheckForUpdatesMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckForUpdatesMenuItem");
|
||||
private MenuItem? _DebugViewMenuItem => ItemHelper.FindChild<MenuItem>(this, "DebugViewMenuItem");
|
||||
private MenuItem? _CheckDumpMenuItem => ItemHelper.FindChild<MenuItem>(this, "CheckDumpMenuItem");
|
||||
private MenuItem? _CreateIRDMenuItem => ItemHelper.FindChild<MenuItem>(this, "CreateIRDMenuItem");
|
||||
private MenuItem? _OptionsMenuItem => ItemHelper.FindChild<MenuItem>(this, "OptionsMenuItem");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Settings
|
||||
|
||||
private ComboBox? _DriveLetterComboBox => ItemHelper.FindChild<ComboBox>(this, "DriveLetterComboBox");
|
||||
private ComboBox? _DriveSpeedComboBox => ItemHelper.FindChild<ComboBox>(this, "DriveSpeedComboBox");
|
||||
private ComboBox? _DumpingProgramComboBox => ItemHelper.FindChild<ComboBox>(this, "DumpingProgramComboBox");
|
||||
private CheckBox? _EnableParametersCheckBox => ItemHelper.FindChild<CheckBox>(this, "EnableParametersCheckBox");
|
||||
private ComboBox? _MediaTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "MediaTypeComboBox");
|
||||
private Button? _OutputPathBrowseButton => ItemHelper.FindChild<Button>(this, "OutputPathBrowseButton");
|
||||
private TextBox? _OutputPathTextBox => ItemHelper.FindChild<TextBox>(this, "OutputPathTextBox");
|
||||
private ComboBox? _SystemTypeComboBox => ItemHelper.FindChild<ComboBox>(this, "SystemTypeComboBox");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Controls
|
||||
|
||||
private Button? _CopyProtectScanButton => ItemHelper.FindChild<Button>(this, "CopyProtectScanButton");
|
||||
private Button? _MediaScanButton => ItemHelper.FindChild<Button>(this, "MediaScanButton");
|
||||
private Button? _StartStopButton => ItemHelper.FindChild<Button>(this, "StartStopButton");
|
||||
private Button? _UpdateVolumeLabel => ItemHelper.FindChild<Button>(this, "UpdateVolumeLabel");
|
||||
|
||||
#endregion
|
||||
|
||||
#region Status
|
||||
|
||||
private LogOutput? _LogOutput => ItemHelper.FindChild<LogOutput>(this, "LogOutput");
|
||||
|
||||
#endregion
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public MainWindow() => InitializeComponent();
|
||||
public MainWindow()
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
var chrome = new System.Windows.Shell.WindowChrome
|
||||
{
|
||||
CaptionHeight = 0,
|
||||
ResizeBorderThickness = new Thickness(0),
|
||||
};
|
||||
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for MainWindow OnContentRendered event
|
||||
@@ -21,7 +89,517 @@ namespace MPF.UI.Core.Windows
|
||||
protected override void OnContentRendered(EventArgs e)
|
||||
{
|
||||
base.OnContentRendered(e);
|
||||
MainViewModel.Init(this);
|
||||
|
||||
// Disable buttons until we load fully
|
||||
MainViewModel.StartStopButtonEnabled = false;
|
||||
MainViewModel.MediaScanButtonEnabled = false;
|
||||
MainViewModel.UpdateVolumeLabelEnabled = false;
|
||||
MainViewModel.CopyProtectScanButtonEnabled = false;
|
||||
|
||||
// Add the click handlers to the UI
|
||||
AddEventHandlers();
|
||||
|
||||
// Display the debug option in the menu, if necessary
|
||||
if (MainViewModel.Options.ShowDebugViewMenuItem)
|
||||
#if NET35
|
||||
_DebugViewMenuItem!.Visibility = Visibility.Visible;
|
||||
#else
|
||||
DebugViewMenuItem.Visibility = Visibility.Visible;
|
||||
#endif
|
||||
|
||||
#if NET35
|
||||
MainViewModel.Init(_LogOutput!.EnqueueLog, DisplayUserMessage, ShowDiscInformationWindow);
|
||||
#else
|
||||
MainViewModel.Init(LogOutput.EnqueueLog, DisplayUserMessage, ShowDiscInformationWindow);
|
||||
#endif
|
||||
|
||||
// Set the UI color scheme according to the options
|
||||
ApplyTheme();
|
||||
|
||||
// Check for updates, if necessary
|
||||
if (MainViewModel.Options.CheckForUpdatesOnStartup)
|
||||
CheckForUpdates(showIfSame: false);
|
||||
|
||||
// Handle first-run, if necessary
|
||||
if (MainViewModel.Options.FirstRun)
|
||||
{
|
||||
// Show the options window
|
||||
ShowOptionsWindow("Welcome to MPF, Explore the Options");
|
||||
}
|
||||
}
|
||||
|
||||
#region UI Functionality
|
||||
|
||||
/// <summary>
|
||||
/// Add all event handlers
|
||||
/// </summary>
|
||||
public void AddEventHandlers()
|
||||
{
|
||||
// Menu Bar Click
|
||||
#if NET35
|
||||
_AboutMenuItem!.Click += AboutClick;
|
||||
_AppExitMenuItem!.Click += AppExitClick;
|
||||
_CheckForUpdatesMenuItem!.Click += CheckForUpdatesClick;
|
||||
_DebugViewMenuItem!.Click += DebugViewClick;
|
||||
_CheckDumpMenuItem!.Click += CheckDumpMenuItemClick;
|
||||
_CreateIRDMenuItem!.Click += CreateIRDMenuItemClick;
|
||||
_OptionsMenuItem!.Click += OptionsMenuItemClick;
|
||||
#else
|
||||
AboutMenuItem.Click += AboutClick;
|
||||
AppExitMenuItem.Click += AppExitClick;
|
||||
CheckForUpdatesMenuItem.Click += CheckForUpdatesClick;
|
||||
DebugViewMenuItem.Click += DebugViewClick;
|
||||
CheckDumpMenuItem.Click += CheckDumpMenuItemClick;
|
||||
CreateIRDMenuItem.Click += CreateIRDMenuItemClick;
|
||||
OptionsMenuItem.Click += OptionsMenuItemClick;
|
||||
#endif
|
||||
|
||||
// User Area Click
|
||||
#if NET35
|
||||
_CopyProtectScanButton!.Click += CopyProtectScanButtonClick;
|
||||
_EnableParametersCheckBox!.Click += EnableParametersCheckBoxClick;
|
||||
_MediaScanButton!.Click += MediaScanButtonClick;
|
||||
_UpdateVolumeLabel!.Click += UpdateVolumeLabelClick;
|
||||
_OutputPathBrowseButton!.Click += OutputPathBrowseButtonClick;
|
||||
_StartStopButton!.Click += StartStopButtonClick;
|
||||
#else
|
||||
CopyProtectScanButton.Click += CopyProtectScanButtonClick;
|
||||
EnableParametersCheckBox.Click += EnableParametersCheckBoxClick;
|
||||
MediaScanButton.Click += MediaScanButtonClick;
|
||||
UpdateVolumeLabel.Click += UpdateVolumeLabelClick;
|
||||
OutputPathBrowseButton.Click += OutputPathBrowseButtonClick;
|
||||
StartStopButton.Click += StartStopButtonClick;
|
||||
#endif
|
||||
|
||||
// User Area SelectionChanged
|
||||
#if NET35
|
||||
_SystemTypeComboBox!.SelectionChanged += SystemTypeComboBoxSelectionChanged;
|
||||
_MediaTypeComboBox!.SelectionChanged += MediaTypeComboBoxSelectionChanged;
|
||||
_DriveLetterComboBox!.SelectionChanged += DriveLetterComboBoxSelectionChanged;
|
||||
_DriveSpeedComboBox!.SelectionChanged += DriveSpeedComboBoxSelectionChanged;
|
||||
_DumpingProgramComboBox!.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
|
||||
#else
|
||||
SystemTypeComboBox.SelectionChanged += SystemTypeComboBoxSelectionChanged;
|
||||
MediaTypeComboBox.SelectionChanged += MediaTypeComboBoxSelectionChanged;
|
||||
DriveLetterComboBox.SelectionChanged += DriveLetterComboBoxSelectionChanged;
|
||||
DriveSpeedComboBox.SelectionChanged += DriveSpeedComboBoxSelectionChanged;
|
||||
DumpingProgramComboBox.SelectionChanged += DumpingProgramComboBoxSelectionChanged;
|
||||
#endif
|
||||
|
||||
// User Area TextChanged
|
||||
#if NET35
|
||||
_OutputPathTextBox!.TextChanged += OutputPathTextBoxTextChanged;
|
||||
#else
|
||||
OutputPathTextBox.TextChanged += OutputPathTextBoxTextChanged;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Browse for an output file path
|
||||
/// </summary>
|
||||
public void BrowseFile()
|
||||
{
|
||||
// Get the current path, if possible
|
||||
string currentPath = MainViewModel.OutputPath;
|
||||
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(MainViewModel.Options.DefaultOutputPath))
|
||||
currentPath = Path.Combine(MainViewModel.Options.DefaultOutputPath, "track.bin");
|
||||
else if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = "track.bin";
|
||||
if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, "track.bin");
|
||||
|
||||
// Get the full path
|
||||
currentPath = Path.GetFullPath(currentPath);
|
||||
|
||||
// Get the directory
|
||||
var directory = Path.GetDirectoryName(currentPath);
|
||||
|
||||
// Get the filename
|
||||
string filename = Path.GetFileName(currentPath);
|
||||
|
||||
WinForms.FileDialog fileDialog = new WinForms.SaveFileDialog
|
||||
{
|
||||
FileName = filename,
|
||||
InitialDirectory = directory,
|
||||
};
|
||||
WinForms.DialogResult result = fileDialog.ShowDialog();
|
||||
|
||||
if (result == WinForms.DialogResult.OK)
|
||||
{
|
||||
MainViewModel.OutputPath = fileDialog.FileName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for available updates
|
||||
/// </summary>
|
||||
/// <param name="showIfSame">True to show the box even if it's the same, false to only show if it's different</param>
|
||||
public void CheckForUpdates(bool showIfSame)
|
||||
{
|
||||
(bool different, string message, var url) = MainViewModel.CheckForUpdates();
|
||||
|
||||
// If we have a new version, put it in the clipboard
|
||||
if (different && !string.IsNullOrEmpty(url))
|
||||
Clipboard.SetText(url);
|
||||
|
||||
if (showIfSame || different)
|
||||
CustomMessageBox.Show(this, message, "Version Update Check", MessageBoxButton.OK, different ? MessageBoxImage.Exclamation : MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display a user message using a CustomMessageBox
|
||||
/// </summary>
|
||||
/// <param name="title">Title to display to the user</param>
|
||||
/// <param name="message">Message to display to the user</param>
|
||||
/// <param name="optionCount">Number of options to display</param>
|
||||
/// <param name="flag">true for inquiry, false otherwise</param>
|
||||
/// <returns>true for positive, false for negative, null for neutral</returns>
|
||||
public bool? DisplayUserMessage(string title, string message, int optionCount, bool flag)
|
||||
{
|
||||
// Set the correct button style
|
||||
var button = optionCount switch
|
||||
{
|
||||
1 => MessageBoxButton.OK,
|
||||
2 => MessageBoxButton.YesNo,
|
||||
3 => MessageBoxButton.YesNoCancel,
|
||||
|
||||
// This should not happen, but default to "OK"
|
||||
_ => MessageBoxButton.OK,
|
||||
};
|
||||
|
||||
// Set the correct icon
|
||||
MessageBoxImage image = flag ? MessageBoxImage.Question : MessageBoxImage.Exclamation;
|
||||
|
||||
// Display and get the result
|
||||
MessageBoxResult result = CustomMessageBox.Show(this, message, title, button, image);
|
||||
return result switch
|
||||
{
|
||||
MessageBoxResult.OK or MessageBoxResult.Yes => true,
|
||||
MessageBoxResult.No => false,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build a dummy SubmissionInfo and display it for testing
|
||||
/// </summary>
|
||||
public void ShowDebugDiscInfoWindow()
|
||||
{
|
||||
var submissionInfo = MainViewModel.CreateDebugSubmissionInfo();
|
||||
var result = ShowDiscInformationWindow(submissionInfo);
|
||||
Formatter.ProcessSpecialFields(result.Item2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the disc information window
|
||||
/// </summary>
|
||||
/// <param name="submissionInfo">SubmissionInfo object to display and possibly change</param>
|
||||
/// <returns>Dialog open result</returns>
|
||||
public (bool?, SubmissionInfo?) ShowDiscInformationWindow(SubmissionInfo? submissionInfo)
|
||||
{
|
||||
if (MainViewModel.Options.ShowDiscEjectReminder)
|
||||
CustomMessageBox.Show(this, "It is now safe to eject the disc", "Eject", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
|
||||
var discInformationWindow = new DiscInformationWindow(MainViewModel.Options, submissionInfo)
|
||||
{
|
||||
Focusable = true,
|
||||
Owner = this,
|
||||
ShowActivated = true,
|
||||
ShowInTaskbar = true,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
discInformationWindow.Closed += delegate { this.Activate(); };
|
||||
bool? result = discInformationWindow.ShowDialog();
|
||||
|
||||
// Copy back the submission info changes, if necessary
|
||||
if (result == true)
|
||||
submissionInfo = (discInformationWindow.DiscInformationViewModel.SubmissionInfo.Clone() as SubmissionInfo)!;
|
||||
|
||||
return (result, submissionInfo!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the Check Dump window
|
||||
/// </summary>
|
||||
public void ShowCheckDumpWindow()
|
||||
{
|
||||
// Hide MainWindow while Check GUI is open
|
||||
this.Hide();
|
||||
|
||||
var checkDumpWindow = new CheckDumpWindow(this)
|
||||
{
|
||||
Focusable = true,
|
||||
Owner = this,
|
||||
ShowActivated = true,
|
||||
ShowInTaskbar = true,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
checkDumpWindow.Closed += delegate {
|
||||
// Unhide Main window after Check window has been closed
|
||||
this.Show();
|
||||
this.Activate();
|
||||
};
|
||||
checkDumpWindow.Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the Create IRD window
|
||||
/// </summary>
|
||||
public void ShowCreateIRDWindow()
|
||||
{
|
||||
// Hide MainWindow while Create IRD UI is open
|
||||
this.Hide();
|
||||
|
||||
var createIRDWindow = new CreateIRDWindow(this)
|
||||
{
|
||||
Focusable = true,
|
||||
Owner = this,
|
||||
ShowActivated = true,
|
||||
ShowInTaskbar = true,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
createIRDWindow.Closed += delegate {
|
||||
// Unhide Main window after Create IRD window has been closed
|
||||
this.Show();
|
||||
this.Activate();
|
||||
};
|
||||
createIRDWindow.Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Show the Options window
|
||||
/// </summary>
|
||||
public void ShowOptionsWindow(string? title = null)
|
||||
{
|
||||
var optionsWindow = new OptionsWindow(MainViewModel.Options)
|
||||
{
|
||||
Focusable = true,
|
||||
Owner = this,
|
||||
ShowActivated = true,
|
||||
ShowInTaskbar = true,
|
||||
Title = title ?? "Options",
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
};
|
||||
|
||||
optionsWindow.Closed += delegate { this.Activate(); };
|
||||
optionsWindow.Closed += OnOptionsUpdated;
|
||||
optionsWindow.Show();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the UI color scheme according to the options
|
||||
/// </summary>
|
||||
private void ApplyTheme()
|
||||
{
|
||||
Theme theme;
|
||||
if (MainViewModel.Options.EnableDarkMode)
|
||||
theme = new DarkModeTheme();
|
||||
else
|
||||
theme = new LightModeTheme();
|
||||
|
||||
theme.Apply();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
/// <summary>
|
||||
/// Handler for OptionsWindow OnUpdated event
|
||||
/// </summary>
|
||||
public void OnOptionsUpdated(object? sender, EventArgs e)
|
||||
{
|
||||
// Get the options window
|
||||
var optionsWindow = (sender as OptionsWindow);
|
||||
if (optionsWindow?.OptionsViewModel == null)
|
||||
return;
|
||||
|
||||
bool savedSettings = optionsWindow.OptionsViewModel.SavedSettings;
|
||||
var options = optionsWindow.OptionsViewModel.Options;
|
||||
MainViewModel.UpdateOptions(savedSettings, options);
|
||||
|
||||
// Set the UI color scheme according to the options
|
||||
ApplyTheme();
|
||||
}
|
||||
|
||||
#region Menu Bar
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AboutMenuItem Click event
|
||||
/// </summary>
|
||||
public void AboutClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
string aboutText = MainViewModel.CreateAboutText();
|
||||
CustomMessageBox.Show(this, aboutText, "About", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for AppExitMenuItem Click event
|
||||
/// </summary>
|
||||
public void AppExitClick(object sender, RoutedEventArgs e) =>
|
||||
Application.Current.Shutdown();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckDumpMenuItem Click event
|
||||
/// </summary>
|
||||
public void CheckDumpMenuItemClick(object sender, RoutedEventArgs e) =>
|
||||
ShowCheckDumpWindow();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CreateIRDMenuItem Click event
|
||||
/// </summary>
|
||||
public void CreateIRDMenuItemClick(object sender, RoutedEventArgs e) =>
|
||||
ShowCreateIRDWindow();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CheckForUpdatesMenuItem Click event
|
||||
/// </summary>
|
||||
public void CheckForUpdatesClick(object sender, RoutedEventArgs e)
|
||||
=> CheckForUpdates(showIfSame: true);
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DebugViewMenuItem Click event
|
||||
/// </summary>
|
||||
public void DebugViewClick(object sender, RoutedEventArgs e) =>
|
||||
ShowDebugDiscInfoWindow();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for OptionsMenuItem Click event
|
||||
/// </summary>
|
||||
public void OptionsMenuItemClick(object sender, RoutedEventArgs e) =>
|
||||
ShowOptionsWindow();
|
||||
|
||||
#endregion
|
||||
|
||||
#region User Area
|
||||
|
||||
/// <summary>
|
||||
/// Handler for CopyProtectScanButton Click event
|
||||
/// </summary>
|
||||
#if NET40
|
||||
public void CopyProtectScanButtonClick(object sender, RoutedEventArgs e)
|
||||
#else
|
||||
public async void CopyProtectScanButtonClick(object sender, RoutedEventArgs e)
|
||||
#endif
|
||||
{
|
||||
#if NET40
|
||||
var (output, error) = MainViewModel.ScanAndShowProtection();
|
||||
#else
|
||||
var (output, error) = await MainViewModel.ScanAndShowProtection();
|
||||
#endif
|
||||
|
||||
if (!MainViewModel.LogPanelExpanded)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(output) && string.IsNullOrEmpty(error))
|
||||
CustomMessageBox.Show(this, output, "Detected Protection(s)", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
else
|
||||
CustomMessageBox.Show(this, "An exception occurred, see the log for details", "Error!", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DriveLetterComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void DriveLetterComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.InitializeUIValues(removeEventHandlers: true, rescanDrives: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DriveSpeedComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void DriveSpeedComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.EnsureDiscInformation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for DumpingProgramComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void DumpingProgramComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.ChangeDumpingProgram();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for EnableParametersCheckBox Click event
|
||||
/// </summary>
|
||||
public void EnableParametersCheckBoxClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.ToggleParameters();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for MediaScanButton Click event
|
||||
/// </summary>
|
||||
public void MediaScanButtonClick(object sender, RoutedEventArgs e) =>
|
||||
MainViewModel.InitializeUIValues(removeEventHandlers: true, rescanDrives: true);
|
||||
|
||||
/// <summary>
|
||||
/// Handler for MediaTypeComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void MediaTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.ChangeMediaType(e.RemovedItems, e.AddedItems);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for OutputPathBrowseButton Click event
|
||||
/// </summary>
|
||||
public void OutputPathBrowseButtonClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
BrowseFile();
|
||||
MainViewModel.EnsureDiscInformation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for OutputPathTextBox TextChanged event
|
||||
/// </summary>
|
||||
public void OutputPathTextBoxTextChanged(object sender, TextChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.EnsureDiscInformation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for StartStopButton Click event
|
||||
/// </summary>
|
||||
public void StartStopButtonClick(object sender, RoutedEventArgs e) =>
|
||||
MainViewModel.ToggleStartStop();
|
||||
|
||||
/// <summary>
|
||||
/// Handler for SystemTypeComboBox SelectionChanged event
|
||||
/// </summary>
|
||||
public void SystemTypeComboBoxSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
MainViewModel.ChangeSystem();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handler for UpdateVolumeLabel Click event
|
||||
/// </summary>
|
||||
public void UpdateVolumeLabelClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (MainViewModel.CanExecuteSelectionChanged)
|
||||
{
|
||||
if (MainViewModel.Options.FastUpdateLabel)
|
||||
MainViewModel.FastUpdateLabel(removeEventHandlers: true);
|
||||
else
|
||||
MainViewModel.InitializeUIValues(removeEventHandlers: true, rescanDrives: false);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion // Event Handlers
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,14 +5,18 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:core="clr-namespace:MPF.UI.Core"
|
||||
xmlns:coreWindows="clr-namespace:MPF.UI.Core.Windows"
|
||||
xmlns:viewModels="clr-namespace:MPF.Core.UI.ViewModels;assembly=MPF.Core"
|
||||
mc:Ignorable="d"
|
||||
Title="Options" Width="515.132" WindowStyle="None"
|
||||
Width="515.132" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
<Window.DataContext>
|
||||
<viewModels:OptionsViewModel/>
|
||||
</Window.DataContext>
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
@@ -27,10 +31,8 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Image Grid.Column="0" Source="/Images/Icon.ico" Height="20" Width="20" Margin="1" />
|
||||
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown">
|
||||
<Label.Content>
|
||||
<TextBlock TextAlignment="Center"><Bold>Options</Bold></TextBlock>
|
||||
</Label.Content>
|
||||
<Label Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" HorizontalContentAlignment="Center" MouseDown="TitleMouseDown"
|
||||
Content="{Binding Path=Title, Mode=OneWay}" FontWeight="Bold">
|
||||
<Label.ContextMenu>
|
||||
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
@@ -60,28 +62,84 @@
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
|
||||
<TabControl Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
|
||||
Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"
|
||||
Style="{DynamicResource CustomTabControlStyle}">
|
||||
<TabItem Header="User Interface" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
|
||||
IsChecked="{Binding Options.EnableDarkMode}"
|
||||
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
|
||||
/>
|
||||
<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 Header="General" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="User Interface">
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Dark Mode"
|
||||
IsChecked="{Binding Options.EnableDarkMode}"
|
||||
ToolTip="(Experimental) Enable dark mode across the entire application" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Check for Updates on Startup"
|
||||
IsChecked="{Binding Options.CheckForUpdatesOnStartup}"
|
||||
ToolTip="Check for updates when the application starts" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Fast Update Label"
|
||||
IsChecked="{Binding Options.FastUpdateLabel}"
|
||||
ToolTip="Bypasses disc checks to quickly update the output path. Use with caution!" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Detection">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox x:Name="SkipMediaTypeDetectionCheckBox" VerticalAlignment="Center" Content="Skip Type Detect"
|
||||
IsChecked="{Binding Options.SkipMediaTypeDetection}"
|
||||
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
|
||||
IsChecked="{Binding Options.SkipSystemDetection}"
|
||||
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
|
||||
IsChecked="{Binding Options.IgnoreFixedDrives}"
|
||||
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
|
||||
/>
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
|
||||
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
|
||||
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding Systems}" SelectedItem="{Binding Options.DefaultSystem, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ComboBoxItem}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHeader}" Value="True">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Logging">
|
||||
<UniformGrid Columns="2" Rows="1">
|
||||
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
|
||||
IsChecked="{Binding Options.VerboseLogging}"
|
||||
ToolTip="Display all logging statements" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Auto-Open Log"
|
||||
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
|
||||
ToolTip="Open the log panel when the program launches" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
|
||||
<TabItem Header="Paths" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<Grid Margin="5,5,5,5">
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -99,26 +157,32 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Aaru Path" />
|
||||
<TextBox x:Name="AaruPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.AaruPath}" VerticalContentAlignment="Center" />
|
||||
<TextBox x:Name="AaruPathTextBox" Grid.Row="0" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
Text="{Binding Options.AaruPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="AaruPathButton" Grid.Row="0" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="DiscImageCreator Path" />
|
||||
<TextBox x:Name="DiscImageCreatorPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DiscImageCreatorPath}" VerticalContentAlignment="Center" />
|
||||
<TextBox x:Name="DiscImageCreatorPathTextBox" Grid.Row="1" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
Text="{Binding Options.DiscImageCreatorPath}" VerticalContentAlignment="Center" />
|
||||
<Button x:Name="DiscImageCreatorPathButton" Grid.Row="1" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
|
||||
<Label Grid.Row="2" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Redumper Path" />
|
||||
<TextBox x:Name="RedumperPathTextBox" Grid.Row="2" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.RedumperPath}" VerticalContentAlignment="Center" />
|
||||
<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="Default Dumping Program" />
|
||||
<ComboBox x:Name="InternalProgramComboBox" Grid.Row="3" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
ItemsSource="{Binding InternalPrograms}" Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
ItemsSource="{Binding InternalPrograms}" SelectedItem="{Binding Options.InternalProgram, Converter={StaticResource ElementConverter}, Mode=TwoWay}"
|
||||
Style="{DynamicResource CustomComboBoxStyle}" />
|
||||
|
||||
<Label Grid.Row="4" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Right" Content="Default Output Path" />
|
||||
<TextBox x:Name="DefaultOutputPathTextBox" Grid.Row="4" Grid.Column="1" Height="22" HorizontalAlignment="Stretch" Text="{Binding Options.DefaultOutputPath}" VerticalContentAlignment="Center" />
|
||||
<TextBox x:Name="DefaultOutputPathTextBox" Grid.Row="4" Grid.Column="1" Height="22" HorizontalAlignment="Stretch"
|
||||
Text="{Binding Options.DefaultOutputPath}" VerticalContentAlignment="Center"
|
||||
ToolTip="Variables allowed:
 %SYSTEM%	(System name, long)
 %SYS%		(System name, short)
 %MEDIA%	(Media type)
 %PROGRAM%	(Program name, long)
 %PROG%	(Program name, short)
 %LABEL%	(Volume label)
 %DATE%	(Current date)
 %DATETIME%	(Current date and time)"/>
|
||||
<Button x:Name="DefaultOutputPathButton" Grid.Row="4" Grid.Column="2" Height="22" Width="22" Content="..."
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
</Grid>
|
||||
@@ -127,45 +191,12 @@
|
||||
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
|
||||
<UniformGrid Columns="2" Rows="9">
|
||||
<CheckBox x:Name="SkipMediaTypeDetectionCheckBox" VerticalAlignment="Center" Content="Skip Type Detect"
|
||||
IsChecked="{Binding Options.SkipMediaTypeDetection}"
|
||||
ToolTip="Disable trying to guess media type inserted (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Skip System Detect"
|
||||
IsChecked="{Binding Options.SkipSystemDetection}"
|
||||
ToolTip="Disable trying to guess system (may improve performance at startup)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label VerticalAlignment="Center" Content="Default System:" HorizontalAlignment="Right" />
|
||||
<ComboBox x:Name="DefaultSystemComboBox" Height="22" Width="200" HorizontalAlignment="Left" ItemsSource="{Binding Systems}" SelectedIndex="0" Style="{DynamicResource CustomComboBoxStyle}">
|
||||
<ComboBox.ItemContainerStyle>
|
||||
<Style TargetType="{x:Type ComboBoxItem}">
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsHeader}" Value="True">
|
||||
<Setter Property="IsEnabled" Value="False"/>
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ComboBox.ItemContainerStyle>
|
||||
</ComboBox>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="No Fixed Drives"
|
||||
IsChecked="{Binding Options.IgnoreFixedDrives}"
|
||||
ToolTip="Ignore hard drives and other fixed drives" Margin="0,4"
|
||||
/>
|
||||
|
||||
<UniformGrid Columns="2" Rows="8">
|
||||
<CheckBox VerticalAlignment="Center" Content="Show Separate Window"
|
||||
IsChecked="{Binding Options.ToolsInSeparateWindow}"
|
||||
ToolTip="Show program output in separate command window instead of in the log. Enable this if you have a weaker system as there is an increased processing load otherwise." Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Protection Scan"
|
||||
IsChecked="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Output Protection File"
|
||||
IsChecked="{Binding Options.OutputSeparateProtectionFile}" IsEnabled="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Output protection information to a separate file" Margin="0,4,0,0"
|
||||
@@ -206,6 +237,11 @@
|
||||
ToolTip="Enable adding placeholder text in the output for required and optional fields" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Add Filename Suffix"
|
||||
IsChecked="{Binding Options.AddFilenameSuffix}"
|
||||
ToolTip="Enable appending the output filename to the generated files" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Output Submission JSON"
|
||||
IsChecked="{Binding Options.OutputSubmissionJSON}"
|
||||
ToolTip="Enable outputting a compressed JSON version of the submission info" Margin="0,4"
|
||||
@@ -220,9 +256,48 @@
|
||||
IsChecked="{Binding Options.CompressLogFiles}"
|
||||
ToolTip="Compress output log files to reduce space" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Delete Unnecessary Files"
|
||||
IsChecked="{Binding Options.DeleteUnnecessaryFiles}"
|
||||
ToolTip="Delete unnecesary output files to reduce space" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Create PS3 IRD After Dumping"
|
||||
IsChecked="{Binding Options.CreateIRDAfterDumping}"
|
||||
ToolTip="Automatically creates an IRD file after dumping a PS3 disc" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Protection">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Protection Scan"
|
||||
IsChecked="{Binding Options.ScanForProtection}"
|
||||
ToolTip="Enable automatic checking for copy protection on dumped media" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Scan Archive Contents"
|
||||
IsChecked="{Binding Options.ScanArchivesForProtection}"
|
||||
ToolTip="Enable scanning archive contents during protection scanning (may drastically increase scanning time but is more accurate)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Executable Packers"
|
||||
IsChecked="{Binding Options.ScanPackersForProtection}"
|
||||
ToolTip="Include executable packers in outputted protections" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
|
||||
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
|
||||
ToolTip="Include debug information during protection scans" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Hide Drive Letters"
|
||||
IsChecked="{Binding Options.HideDriveLetters}"
|
||||
ToolTip="Remove drive letters from protection scan output" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Default Speeds" Grid.ColumnSpan="2">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -237,175 +312,154 @@
|
||||
<RowDefinition />
|
||||
<RowDefinition />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
|
||||
<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 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"
|
||||
<TextBox x:Name="DumpSpeedCDTextBox" Grid.Row="0" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedCDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<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 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"
|
||||
<TextBox x:Name="DumpSpeedDVDTextBox" Grid.Row="1" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedDVDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
|
||||
<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"
|
||||
<TextBox x:Name="DumpSpeedHDDVDTextBox" Grid.Row="2" Grid.Column="2" Width="22" Height="22" TextAlignment="Center" IsReadOnly="True" 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="3" 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" VerticalAlignment="Center"
|
||||
Text="{Binding ElementName=DumpSpeedBDSlider, Path=Value, UpdateSourceTrigger=PropertyChanged}" Background="LightGray" Foreground="Gray"/>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Protection Scanning" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<TabItem Header="Programs" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Scan Archive Contents"
|
||||
IsChecked="{Binding Options.ScanArchivesForProtection}"
|
||||
ToolTip="Enable scanning archive contents during protection scanning (may drastically increase scanning time but is more accurate)" Margin="0,4"
|
||||
/>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Aaru">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.AaruEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Executable Packers"
|
||||
IsChecked="{Binding Options.ScanPackersForProtection}"
|
||||
ToolTip="Include executable packers in outputted protections" Margin="0,4"
|
||||
/>
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.AaruEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Include Debug Information"
|
||||
IsChecked="{Binding Options.IncludeDebugProtectionInformation}"
|
||||
ToolTip="Include debug information during protection scans" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
<CheckBox VerticalAlignment="Center" Content="Force Dumping"
|
||||
IsChecked="{Binding Options.AaruForceDumping}"
|
||||
ToolTip="Enable forcing dump even if there are issues" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Strip Personal Data"
|
||||
IsChecked="{Binding Options.AaruStripPersonalData}"
|
||||
ToolTip="Enable stripping of personally identifiable information from metadata" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.AaruRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="DiscImageCreator">
|
||||
<UniformGrid Columns="2" Rows="6">
|
||||
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
|
||||
IsChecked="{Binding Options.DICQuietMode}"
|
||||
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Paranoid Mode"
|
||||
IsChecked="{Binding Options.DICParanoidMode}"
|
||||
ToolTip="Enable pedantic and super-safe flags" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Use CMI Flag"
|
||||
IsChecked="{Binding Options.DICUseCMIFlag}"
|
||||
ToolTip="Enable the CMI flag for supported disc types (DVD/HD-DVD only)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Reset After Dump"
|
||||
IsChecked="{Binding Options.DICResetDriveAfterDump}"
|
||||
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Multi-Sector Read"
|
||||
IsChecked="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Enable the /mr flag for BD drive dumping" Margin="0,4"
|
||||
/>
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
|
||||
<Label Content="Multi-Sector Read Value:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICMultiSectorReadValue}" IsEnabled="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Set the default value for the /mr flag"
|
||||
/>
|
||||
|
||||
<Label Content="CD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on C2 error [CD only]"
|
||||
/>
|
||||
|
||||
<Label Content="DVD/HD-DVD/BD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICDVDRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error [DVD/HD-DVD/BD only]"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redumper">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.RedumperEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.RedumperEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable BE Drive Reading"
|
||||
IsChecked="{Binding Options.RedumperUseBEReading}"
|
||||
ToolTip="Enable setting drive read method to BE_CDDA by default" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Set Generic Drive Type"
|
||||
IsChecked="{Binding Options.RedumperUseGenericDriveType}"
|
||||
ToolTip="Enable setting drive type to Generic by default" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.RedumperRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Aaru" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="3">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.AaruEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.AaruEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Force Dumping"
|
||||
IsChecked="{Binding Options.AaruForceDumping}"
|
||||
ToolTip="Enable forcing dump even if there are issues" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Strip Personal Data"
|
||||
IsChecked="{Binding Options.AaruStripPersonalData}"
|
||||
ToolTip="Enable stripping of personally identifiable information from metadata" Margin="0,4,0,0"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.AaruRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted for sector and subchannel errors"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="DiscImageCreator" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="6">
|
||||
<CheckBox VerticalAlignment="Center" Content="Quiet Mode"
|
||||
IsChecked="{Binding Options.DICQuietMode}"
|
||||
ToolTip="Disable sounds (beeps) during and after operations" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Paranoid Mode"
|
||||
IsChecked="{Binding Options.DICParanoidMode}"
|
||||
ToolTip="Enable pedantic and super-safe flags" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Use CMI Flag"
|
||||
IsChecked="{Binding Options.DICUseCMIFlag}"
|
||||
ToolTip="Enable the CMI flag for supported disc types (DVD/HD-DVD only)" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Reset After Dump"
|
||||
IsChecked="{Binding Options.DICResetDriveAfterDump}"
|
||||
ToolTip="Reset disc drives after dumping; useful for some older machines" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Multi-Sector Read"
|
||||
IsChecked="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Enable the /mr flag for BD drive dumping" Margin="0,4"
|
||||
/>
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
|
||||
<Label Content="Multi-Sector Read Value:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICMultiSectorReadValue}" IsEnabled="{Binding Options.DICMultiSectorRead}"
|
||||
ToolTip="Set the default value for the /mr flag"
|
||||
/>
|
||||
|
||||
<Label Content="CD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on C2 error [CD only]"
|
||||
/>
|
||||
|
||||
<Label Content="DVD/HD-DVD/BD Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.DICDVDRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error [DVD/HD-DVD/BD only]"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Redumper" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2" Rows="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Debug Output"
|
||||
IsChecked="{Binding Options.RedumperEnableDebug}"
|
||||
ToolTip="Enable debug output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Enable Verbose Output"
|
||||
IsChecked="{Binding Options.RedumperEnableVerbose}"
|
||||
ToolTip="Enable verbose output in logs" Margin="0,4"
|
||||
/>
|
||||
|
||||
<Label Content="Reread Tries:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
|
||||
<TextBox VerticalAlignment="Center" VerticalContentAlignment="Center"
|
||||
Text="{Binding Options.RedumperRereadCount}"
|
||||
ToolTip="Specifies how many rereads are attempted on read error"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Logging" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<UniformGrid Columns="2">
|
||||
<CheckBox VerticalAlignment="Center" Content="Verbose Logging"
|
||||
IsChecked="{Binding Options.VerboseLogging}"
|
||||
ToolTip="Display all logging statements" Margin="0,4"
|
||||
/>
|
||||
|
||||
<CheckBox VerticalAlignment="Center" Content="Auto-Open Log"
|
||||
IsChecked="{Binding Options.OpenLogWindowAtStartup}"
|
||||
ToolTip="Open the log panel when the program launches" Margin="0,4"
|
||||
/>
|
||||
</UniformGrid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Login Info" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump Credentials">
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Redump">
|
||||
<UniformGrid Columns="5">
|
||||
<Label VerticalAlignment="Center" HorizontalAlignment="Right" Content="Username" />
|
||||
<TextBox x:Name="RedumpUsernameTextBox" Height="22" HorizontalAlignment="Stretch"
|
||||
@@ -421,7 +475,10 @@
|
||||
|
||||
<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>
|
||||
<TextBlock TextWrapping="Wrap">
|
||||
<Run FontWeight="Bold" Foreground="Red" Text="WARNING:" />
|
||||
<Run Text="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>
|
||||
@@ -431,12 +488,14 @@
|
||||
<!-- Accept / Cancel -->
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
|
||||
<UniformGrid Columns="4" Margin="5,5,5,5" Height="28">
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
<Button Name="AcceptButton" Height="25" Width="80" IsDefault="True" Content="Accept"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Button Name="CancelButton" Height="25" Width="80" IsCancel="True" Content="Cancel"
|
||||
Style="{DynamicResource CustomButtonStyle}" />
|
||||
<Label/> <!-- Empty label for padding -->
|
||||
<Label/>
|
||||
<!-- Empty label for padding -->
|
||||
</UniformGrid>
|
||||
</GroupBox>
|
||||
</StackPanel>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user