mirror of
https://github.com/SabreTools/MPF.git
synced 2026-02-04 13:45:29 +00:00
Compare commits
171 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
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
|
||||
182
CHANGELIST.md
182
CHANGELIST.md
@@ -1,3 +1,185 @@
|
||||
### 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
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.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>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.1.3</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Title>MPF Check</Title>
|
||||
<Description>Validator for various dumping programs</Description>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>3.0.0</VersionPrefix>
|
||||
<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>
|
||||
@@ -21,10 +32,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.0" GeneratePathProperty="true">
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.1" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.2.0" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -45,8 +45,12 @@ namespace MPF.Check
|
||||
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
|
||||
// Validate the supplied credentials
|
||||
#if NETFRAMEWORK
|
||||
(bool? _, string? message) = RedumpWebClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty);
|
||||
#else
|
||||
(bool? _, string? message) = RedumpHttpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
if (!string.IsNullOrWhiteSpace(message))
|
||||
#endif
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
Console.WriteLine(message);
|
||||
|
||||
// Loop through all the rest of the args
|
||||
@@ -64,13 +68,19 @@ namespace MPF.Check
|
||||
|
||||
// Now populate an environment
|
||||
Drive? drive = null;
|
||||
if (!string.IsNullOrWhiteSpace(path))
|
||||
drive = Drive.Create(null, path);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using System;
|
||||
#if NET20 || NET35
|
||||
using System.Collections.Generic;
|
||||
#else
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using MPF.Core.Data;
|
||||
@@ -34,7 +38,11 @@ namespace MPF.Core.Converters
|
||||
/// <summary>
|
||||
/// Long name method cache
|
||||
/// </summary>
|
||||
private static readonly ConcurrentDictionary<Type, MethodInfo?> LongNameMethods = new();
|
||||
#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
|
||||
@@ -50,11 +58,14 @@ namespace MPF.Core.Converters
|
||||
|
||||
if (!LongNameMethods.TryGetValue(sourceType, out var method))
|
||||
{
|
||||
method = typeof(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)
|
||||
@@ -90,6 +101,7 @@ namespace MPF.Core.Converters
|
||||
|
||||
InternalProgram.CleanRip => "CleanRip",
|
||||
InternalProgram.DCDumper => "DCDumper",
|
||||
InternalProgram.PS3CFW => "PS3 CFW",
|
||||
InternalProgram.UmdImageCreator => "UmdImageCreator",
|
||||
|
||||
#endregion
|
||||
@@ -99,7 +111,7 @@ namespace MPF.Core.Converters
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Convert From String
|
||||
|
||||
@@ -130,6 +142,11 @@ namespace MPF.Core.Converters
|
||||
"dc"
|
||||
or "dcd"
|
||||
or "dcdumper" => InternalProgram.DCDumper,
|
||||
"ps3cfw"
|
||||
or "ps3"
|
||||
or "getkey"
|
||||
or "managunz"
|
||||
or "multiman" => InternalProgram.PS3CFW,
|
||||
"uic"
|
||||
or "umd"
|
||||
or "umdcreator"
|
||||
|
||||
@@ -14,21 +14,33 @@ 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];
|
||||
|
||||
// Lists of known drive speed ranges
|
||||
#if NET20 || NET35 || NET40
|
||||
public static IList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
public static IList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
public static IList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
public static IList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
|
||||
public static IList<int> Unknown { get; } = new List<int> { 1 };
|
||||
#else
|
||||
public static IReadOnlyList<int> CD { get; } = new List<int> { 1, 2, 3, 4, 6, 8, 12, 16, 20, 24, 32, 40, 44, 48, 52, 56, 72 };
|
||||
public static IReadOnlyList<int> DVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
public static IReadOnlyList<int> HDDVD { get; } = CD.Where(s => s <= 24).ToList();
|
||||
public static IReadOnlyList<int> BD { get; } = CD.Where(s => s <= 16).ToList();
|
||||
public static IReadOnlyList<int> Unknown { get; } = new List<int> { 1 };
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Get list of all drive speeds for a given MediaType
|
||||
/// </summary>
|
||||
/// <param name="type">MediaType? that represents the current item</param>
|
||||
/// <returns>Read-only list of drive speeds</returns>
|
||||
#if NET20 || NET35 || NET40
|
||||
public static IList<int> GetSpeedsForMediaType(MediaType? type)
|
||||
#else
|
||||
public static IReadOnlyList<int> GetSpeedsForMediaType(MediaType? type)
|
||||
#endif
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
@@ -49,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,8 +2,10 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using Microsoft.Management.Infrastructure;
|
||||
using Microsoft.Management.Infrastructure.Generic;
|
||||
#endif
|
||||
using MPF.Core.Converters;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
@@ -56,22 +58,40 @@ 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
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -104,12 +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("\\\\.\\"))
|
||||
devicePath = devicePath["\\\\.\\".Length..];
|
||||
devicePath = devicePath.Substring("\\\\.\\".Length);
|
||||
|
||||
// Create and validate the drive info object
|
||||
var driveInfo = new DriveInfo(devicePath);
|
||||
@@ -159,7 +179,7 @@ namespace MPF.Core.Data
|
||||
public static List<Drive> CreateListOfDrives(bool ignoreFixedDrives)
|
||||
{
|
||||
var drives = GetDriveList(ignoreFixedDrives);
|
||||
drives = drives.OrderBy(i => i == null ? "\0" : i.Name).ToList();
|
||||
drives = [.. drives.OrderBy(i => i == null ? "\0" : i.Name)];
|
||||
return drives;
|
||||
}
|
||||
|
||||
@@ -249,7 +269,7 @@ namespace MPF.Core.Data
|
||||
public RedumpSystem? GetRedumpSystem(RedumpSystem? defaultValue)
|
||||
{
|
||||
// If we can't read the media in that drive, we can't do anything
|
||||
if (!Directory.Exists(this.Name))
|
||||
if (string.IsNullOrEmpty(this.Name) || !Directory.Exists(this.Name))
|
||||
return defaultValue;
|
||||
|
||||
// We're going to assume for floppies, HDDs, and removable drives
|
||||
@@ -257,17 +277,32 @@ namespace MPF.Core.Data
|
||||
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
|
||||
{
|
||||
#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)))
|
||||
@@ -277,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 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;
|
||||
}
|
||||
@@ -287,7 +351,11 @@ namespace MPF.Core.Data
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(this.Name, "$SystemUpdate"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "$SystemUpdate")).Any()
|
||||
#endif
|
||||
&& this.TotalSize <= 500_000_000)
|
||||
{
|
||||
return RedumpSystem.MicrosoftXbox360;
|
||||
@@ -299,7 +367,11 @@ namespace MPF.Core.Data
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(this.Name, "MSXC"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "MSXC")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "MSXC")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.MicrosoftXboxOne;
|
||||
}
|
||||
@@ -313,10 +385,17 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
// Sega Mega-CD / Sega-CD
|
||||
#if NET20 || NET35
|
||||
if (File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "IP.BIN"))
|
||||
|| File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "SP.BIN"))
|
||||
|| File.Exists(Path.Combine(Path.Combine(this.Name, "_BOOT"), "SP_AS.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
|
||||
#else
|
||||
if (File.Exists(Path.Combine(this.Name, "_BOOT", "IP.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "_BOOT", "SP_AS.BIN"))
|
||||
|| File.Exists(Path.Combine(this.Name, "FILESYSTEM.BIN")))
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.SegaMegaCDSegaCD;
|
||||
}
|
||||
@@ -362,7 +441,11 @@ 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 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;
|
||||
}
|
||||
@@ -398,13 +481,21 @@ namespace MPF.Core.Data
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(this.Name, "AUDIO_TS"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "AUDIO_TS")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.DVDAudio;
|
||||
}
|
||||
|
||||
else if (Directory.Exists(Path.Combine(this.Name, "VIDEO_TS"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VIDEO_TS")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.DVDVideo;
|
||||
}
|
||||
@@ -415,18 +506,41 @@ namespace MPF.Core.Data
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(this.Name, "HVDVD_TS"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "HVDVD_TS")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.HDDVDVideo;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// Photo CD
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(this.Name, "PHOTO_CD"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "PHOTO_CD")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "PHOTO_CD")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.PhotoCD;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// VCD
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(Path.Combine(this.Name, "VCD"))
|
||||
#if NET20 || NET35
|
||||
&& Directory.GetFiles(Path.Combine(this.Name, "VCD")).Any())
|
||||
#else
|
||||
&& Directory.EnumerateFiles(Path.Combine(this.Name, "VCD")).Any())
|
||||
#endif
|
||||
{
|
||||
return RedumpSystem.VideoCD;
|
||||
}
|
||||
@@ -443,48 +557,48 @@ 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;
|
||||
@@ -541,6 +655,7 @@ namespace MPF.Core.Data
|
||||
}
|
||||
|
||||
// Find and update all floppy drives
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
CimSession session = CimSession.Create(null);
|
||||
@@ -561,6 +676,7 @@ namespace MPF.Core.Data
|
||||
{
|
||||
// No-op
|
||||
}
|
||||
#endif
|
||||
|
||||
return drives;
|
||||
}
|
||||
|
||||
@@ -6,14 +6,18 @@
|
||||
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>
|
||||
@@ -42,6 +46,7 @@
|
||||
// Verification support only
|
||||
CleanRip,
|
||||
DCDumper,
|
||||
PS3CFW,
|
||||
UmdImageCreator,
|
||||
}
|
||||
|
||||
|
||||
@@ -8,23 +8,23 @@ namespace MPF.Core.Data
|
||||
{
|
||||
public class IniFile : IDictionary<string, string>
|
||||
{
|
||||
private Dictionary<string, string> _keyValuePairs = new();
|
||||
private Dictionary<string, string> _keyValuePairs = [];
|
||||
|
||||
public string this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
_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 string.Empty;
|
||||
}
|
||||
set
|
||||
{
|
||||
_keyValuePairs ??= new Dictionary<string, string>();
|
||||
_keyValuePairs ??= [];
|
||||
|
||||
key = key.ToLowerInvariant();
|
||||
_keyValuePairs[key] = value;
|
||||
@@ -102,13 +102,13 @@ namespace MPF.Core.Data
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
|
||||
// Empty lines are skipped
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
// No-op, we don't process empty lines
|
||||
}
|
||||
|
||||
// Comments start with ';'
|
||||
else if (line.StartsWith(";"))
|
||||
else if (line!.StartsWith(";"))
|
||||
{
|
||||
// No-op, we don't process comments
|
||||
}
|
||||
@@ -127,7 +127,7 @@ namespace MPF.Core.Data
|
||||
|
||||
// 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();
|
||||
string value = string.Join("=", data.Skip(1).ToArray()).Trim();
|
||||
|
||||
// Section names are prepended to the key with a '.' separating
|
||||
if (!string.IsNullOrEmpty(section))
|
||||
@@ -196,7 +196,7 @@ namespace MPF.Core.Data
|
||||
|
||||
// If the key contains an '.', we need to put them back in
|
||||
string newSection = data[0].Trim();
|
||||
key = string.Join(".", data.Skip(1)).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))
|
||||
|
||||
@@ -56,9 +56,9 @@ namespace MPF.Core.Data
|
||||
{
|
||||
get
|
||||
{
|
||||
var 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
|
||||
{
|
||||
@@ -321,7 +321,7 @@ 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(); }
|
||||
}
|
||||
|
||||
@@ -500,6 +500,15 @@ namespace MPF.Core.Data
|
||||
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
|
||||
@@ -553,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
|
||||
@@ -598,7 +616,7 @@ namespace MPF.Core.Data
|
||||
/// <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
|
||||
|
||||
@@ -608,7 +626,7 @@ namespace MPF.Core.Data
|
||||
/// <param name="settings"></param>
|
||||
public Options(Dictionary<string, string?>? settings = null)
|
||||
{
|
||||
this.Settings = settings ?? new Dictionary<string, string?>();
|
||||
this.Settings = settings ?? [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -617,7 +635,7 @@ namespace MPF.Core.Data
|
||||
/// <param name="source"></param>
|
||||
public Options(Options? source)
|
||||
{
|
||||
Settings = new Dictionary<string, string?>(source?.Settings ?? new Dictionary<string, string?>());
|
||||
Settings = new Dictionary<string, string?>(source?.Settings ?? []);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
using System;
|
||||
#if NET20 || NET35
|
||||
using System.Collections.Generic;
|
||||
#else
|
||||
using System.Collections.Concurrent;
|
||||
#endif
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -10,7 +14,11 @@ namespace MPF.Core.Data
|
||||
/// <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,7 +57,7 @@ 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 (item != null && !this.TokenSource.IsCancellationRequested)
|
||||
@@ -54,7 +72,11 @@ namespace MPF.Core.Data
|
||||
while (true)
|
||||
{
|
||||
// Nothing in the queue means we get to idle
|
||||
#if NET20 || NET35
|
||||
if (InternalQueue.Count == 0)
|
||||
#else
|
||||
if (InternalQueue.IsEmpty)
|
||||
#endif
|
||||
{
|
||||
if (this.TokenSource.IsCancellationRequested)
|
||||
break;
|
||||
@@ -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 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
|
||||
@@ -44,6 +46,11 @@
|
||||
/// <param name="message">String to add as a message</param>
|
||||
public static Result Failure(string? message) => new(false, message ?? string.Empty);
|
||||
|
||||
internal static Result Success(object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Results can be compared to boolean values based on the success value
|
||||
/// </summary>
|
||||
|
||||
@@ -4,10 +4,10 @@ using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Modules;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core
|
||||
@@ -65,7 +65,11 @@ namespace MPF.Core
|
||||
/// <summary>
|
||||
/// Generic way of reporting a message
|
||||
/// </summary>
|
||||
#if NET20 || NET35 || NET40
|
||||
public EventHandler<BaseParameters.StringEventArgs>? ReportStatus;
|
||||
#else
|
||||
public EventHandler<string>? ReportStatus;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Queue of items that need to be logged
|
||||
@@ -75,12 +79,20 @@ namespace MPF.Core
|
||||
/// <summary>
|
||||
/// Event handler for data returned from a process
|
||||
/// </summary>
|
||||
#if NET20 || NET35 || NET40
|
||||
private void OutputToLog(object? proc, BaseParameters.StringEventArgs args) => outputQueue?.Enqueue(args.Value);
|
||||
#else
|
||||
private void OutputToLog(object? proc, string args) => outputQueue?.Enqueue(args);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Process the outputs in the queue
|
||||
/// </summary>
|
||||
#if NET20 || NET35 || NET40
|
||||
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, new BaseParameters.StringEventArgs { Value = nextOutput });
|
||||
#else
|
||||
private void ProcessOutputs(string nextOutput) => ReportStatus?.Invoke(this, nextOutput);
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -136,10 +148,14 @@ namespace MPF.Core
|
||||
// Verification support only
|
||||
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 },
|
||||
|
||||
// If no dumping program found, set to null
|
||||
InternalProgram.NONE => null,
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
_ => new Modules.DiscImageCreator.Parameters(parameters) { ExecutablePath = Options.DiscImageCreatorPath },
|
||||
_ => new Modules.Redumper.Parameters(parameters) { ExecutablePath = Options.RedumperPath },
|
||||
};
|
||||
|
||||
// Set system and type
|
||||
@@ -171,12 +187,15 @@ namespace MPF.Core
|
||||
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),
|
||||
|
||||
// If no dumping program found, set to null
|
||||
InternalProgram.NONE => null,
|
||||
|
||||
// This should never happen, but it needs a fallback
|
||||
_ => new Modules.DiscImageCreator.Parameters(System, Type, Drive.Name, OutputPath, driveSpeed, Options),
|
||||
_ => 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;
|
||||
@@ -207,7 +226,11 @@ namespace MPF.Core
|
||||
/// Execute the initial invocation of the dumping programs
|
||||
/// </summary>
|
||||
/// <param name="progress">Optional result progress callback</param>
|
||||
#if NET40
|
||||
public Result Run(IProgress<Result>? progress = null)
|
||||
#else
|
||||
public async Task<Result> Run(IProgress<Result>? progress = null)
|
||||
#endif
|
||||
{
|
||||
// If we don't have parameters
|
||||
if (Parameters == null)
|
||||
@@ -230,10 +253,15 @@ namespace MPF.Core
|
||||
progress?.Report(Result.Success($"Executing {InternalProgram}... {(Options.ToolsInSeparateWindow ? "please wait!" : "see log for output!")}"));
|
||||
|
||||
var directoryName = Path.GetDirectoryName(OutputPath);
|
||||
if (!string.IsNullOrWhiteSpace(directoryName))
|
||||
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
|
||||
@@ -256,10 +284,13 @@ namespace MPF.Core
|
||||
/// <returns>Result instance with the outcome</returns>
|
||||
public async Task<Result> VerifyAndSaveDumpOutput(
|
||||
IProgress<Result>? resultProgress = null,
|
||||
IProgress<ProtectionProgress>? protectionProgress = 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
|
||||
@@ -267,10 +298,10 @@ namespace MPF.Core
|
||||
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, 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!");
|
||||
}
|
||||
|
||||
@@ -291,7 +322,7 @@ namespace MPF.Core
|
||||
if (seedInfo != null)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Injecting user-supplied information..."));
|
||||
SubmissionInfoTool.InjectSubmissionInformation(submissionInfo, seedInfo);
|
||||
Builder.InjectSubmissionInformation(submissionInfo, seedInfo);
|
||||
resultProgress?.Report(Result.Success("Information injection complete!"));
|
||||
}
|
||||
|
||||
@@ -325,16 +356,16 @@ namespace MPF.Core
|
||||
|
||||
// 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..."));
|
||||
(var formattedValues, var formatResult) = InfoTool.FormatOutputData(submissionInfo, 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;
|
||||
@@ -353,7 +384,7 @@ namespace MPF.Core
|
||||
if (Options.ScanForProtection && Options.OutputSeparateProtectionFile)
|
||||
{
|
||||
resultProgress?.Report(Result.Success("Writing protection to !protectionInfo.txt..."));
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo);
|
||||
bool scanSuccess = InfoTool.WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo, Options.HideDriveLetters);
|
||||
if (scanSuccess)
|
||||
resultProgress?.Report(Result.Success("Writing complete!"));
|
||||
else
|
||||
@@ -394,6 +425,17 @@ namespace MPF.Core
|
||||
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();
|
||||
}
|
||||
@@ -426,14 +468,18 @@ namespace MPF.Core
|
||||
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,
|
||||
@@ -477,7 +523,7 @@ namespace MPF.Core
|
||||
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);
|
||||
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!");
|
||||
|
||||
@@ -492,7 +538,7 @@ namespace MPF.Core
|
||||
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
|
||||
|
||||
@@ -1,313 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Hashing;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using MPF.Core.Data;
|
||||
|
||||
namespace MPF.Core.Hashing
|
||||
{
|
||||
public sealed class Hasher : IDisposable
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Hash type associated with the current state
|
||||
/// </summary>
|
||||
public Hash HashType { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Current hash in bytes
|
||||
/// </summary>
|
||||
public byte[]? CurrentHashBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
return (_hasher) switch
|
||||
{
|
||||
HashAlgorithm ha => ha.Hash,
|
||||
NonCryptographicHashAlgorithm ncha => ncha.GetCurrentHash().Reverse().ToArray(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Current hash as a string
|
||||
/// </summary>
|
||||
public string? CurrentHashString => ByteArrayToString(CurrentHashBytes);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Fields
|
||||
|
||||
/// <summary>
|
||||
/// Internal hasher being used for processing
|
||||
/// </summary>
|
||||
/// <remarks>May be either a HashAlgorithm or NonCryptographicHashAlgorithm</remarks>
|
||||
private object? _hasher;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="hashType">Hash type to instantiate</param>
|
||||
public Hasher(Hash hashType)
|
||||
{
|
||||
this.HashType = hashType;
|
||||
GetHasher();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the correct hashing class based on the hash type
|
||||
/// </summary>
|
||||
private void GetHasher()
|
||||
{
|
||||
_hasher = HashType switch
|
||||
{
|
||||
Hash.CRC32 => new Crc32(),
|
||||
Hash.CRC64 => new Crc64(),
|
||||
Hash.MD5 => MD5.Create(),
|
||||
Hash.SHA1 => SHA1.Create(),
|
||||
Hash.SHA256 => SHA256.Create(),
|
||||
Hash.SHA384 => SHA384.Create(),
|
||||
Hash.SHA512 => SHA512.Create(),
|
||||
Hash.XxHash32 => new XxHash32(),
|
||||
Hash.XxHash64 => new XxHash64(),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_hasher is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Hashing
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input file path
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <returns>True if hashing was successful, false otherwise</returns>
|
||||
public static bool GetFileHashes(string filename, out long size, out string? crc32, out string? md5, out string? sha1)
|
||||
{
|
||||
// Set all initial values
|
||||
crc32 = null; md5 = null; sha1 = null;
|
||||
|
||||
// Get all file hashes
|
||||
var fileHashes = GetFileHashes(filename, out size);
|
||||
if (fileHashes == null)
|
||||
return false;
|
||||
|
||||
// Assign the file hashes and return
|
||||
crc32 = fileHashes[Hash.CRC32];
|
||||
md5 = fileHashes[Hash.MD5];
|
||||
sha1 = fileHashes[Hash.SHA1];
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input file path
|
||||
/// </summary>
|
||||
/// <param name="filename">Path to the input file</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<Hash, string?>? GetFileHashes(string filename, out long size)
|
||||
{
|
||||
// If the file doesn't exist, we can't do anything
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
size = -1;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Set the file size
|
||||
size = new FileInfo(filename).Length;
|
||||
|
||||
// Open the input file
|
||||
var input = File.OpenRead(filename);
|
||||
|
||||
// Return the hashes from the stream
|
||||
return GetStreamHashes(input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hashes from an input Stream
|
||||
/// </summary>
|
||||
/// <param name="input">Stream to hash</param>
|
||||
/// <returns>Dictionary containing hashes on success, null on error</returns>
|
||||
public static Dictionary<Hash, string?>? GetStreamHashes(Stream input)
|
||||
{
|
||||
// Create the output dictionary
|
||||
var hashDict = new Dictionary<Hash, string?>();
|
||||
|
||||
try
|
||||
{
|
||||
// Get a list of hashers to run over the buffer
|
||||
var hashers = new Dictionary<Hash, Hasher>
|
||||
{
|
||||
{ Hash.CRC32, new Hasher(Hash.CRC32) },
|
||||
{ Hash.CRC64, new Hasher(Hash.CRC64) },
|
||||
{ Hash.MD5, new Hasher(Hash.MD5) },
|
||||
{ Hash.SHA1, new Hasher(Hash.SHA1) },
|
||||
{ Hash.SHA256, new Hasher(Hash.SHA256) },
|
||||
{ Hash.SHA384, new Hasher(Hash.SHA384) },
|
||||
{ Hash.SHA512, new Hasher(Hash.SHA512) },
|
||||
{ Hash.XxHash32, new Hasher(Hash.XxHash32) },
|
||||
{ Hash.XxHash64, new Hasher(Hash.XxHash64) },
|
||||
};
|
||||
|
||||
// Initialize the hashing helpers
|
||||
var loadBuffer = new ThreadLoadBuffer(input);
|
||||
int buffersize = 3 * 1024 * 1024;
|
||||
byte[] buffer0 = new byte[buffersize];
|
||||
byte[] buffer1 = new byte[buffersize];
|
||||
|
||||
/*
|
||||
Please note that some of the following code is adapted from
|
||||
RomVault. This is a modified version of how RomVault does
|
||||
threaded hashing. As such, some of the terminology and code
|
||||
is the same, though variable names and comments may have
|
||||
been tweaked to better fit this code base.
|
||||
*/
|
||||
|
||||
// Pre load the first buffer
|
||||
long refsize = input.Length;
|
||||
int next = refsize > buffersize ? buffersize : (int)refsize;
|
||||
input.Read(buffer0, 0, next);
|
||||
int current = next;
|
||||
refsize -= next;
|
||||
bool bufferSelect = true;
|
||||
|
||||
while (current > 0)
|
||||
{
|
||||
// Trigger the buffer load on the second buffer
|
||||
next = refsize > buffersize ? buffersize : (int)refsize;
|
||||
if (next > 0)
|
||||
loadBuffer.Trigger(bufferSelect ? buffer1 : buffer0, next);
|
||||
|
||||
byte[] buffer = bufferSelect ? buffer0 : buffer1;
|
||||
|
||||
// Run hashes in parallel
|
||||
Parallel.ForEach(hashers, h => h.Value.Process(buffer, current));
|
||||
|
||||
// Wait for the load buffer worker, if needed
|
||||
if (next > 0)
|
||||
loadBuffer.Wait();
|
||||
|
||||
// Setup for the next hashing step
|
||||
current = next;
|
||||
refsize -= next;
|
||||
bufferSelect = !bufferSelect;
|
||||
}
|
||||
|
||||
// Finalize all hashing helpers
|
||||
loadBuffer.Finish();
|
||||
Parallel.ForEach(hashers, h => h.Value.Terminate());
|
||||
|
||||
// Get the results
|
||||
hashDict[Hash.CRC32] = hashers[Hash.CRC32].CurrentHashString;
|
||||
hashDict[Hash.CRC64] = hashers[Hash.CRC64].CurrentHashString;
|
||||
hashDict[Hash.MD5] = hashers[Hash.MD5].CurrentHashString;
|
||||
hashDict[Hash.SHA1] = hashers[Hash.SHA1].CurrentHashString;
|
||||
hashDict[Hash.SHA256] = hashers[Hash.SHA256].CurrentHashString;
|
||||
hashDict[Hash.SHA384] = hashers[Hash.SHA384].CurrentHashString;
|
||||
hashDict[Hash.SHA512] = hashers[Hash.SHA512].CurrentHashString;
|
||||
hashDict[Hash.XxHash32] = hashers[Hash.XxHash32].CurrentHashString;
|
||||
hashDict[Hash.XxHash64] = hashers[Hash.XxHash64].CurrentHashString;
|
||||
hashDict[Hash.CRC64] = hashers[Hash.CRC64].CurrentHashString;
|
||||
|
||||
// Dispose of the hashers
|
||||
loadBuffer.Dispose();
|
||||
foreach (var hasher in hashers.Values)
|
||||
{
|
||||
hasher.Dispose();
|
||||
}
|
||||
|
||||
return hashDict;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
input.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Hashing
|
||||
|
||||
/// <summary>
|
||||
/// Process a buffer of some length with the internal hash algorithm
|
||||
/// </summary>
|
||||
public void Process(byte[] buffer, int size)
|
||||
{
|
||||
switch (_hasher)
|
||||
{
|
||||
case HashAlgorithm ha:
|
||||
ha.TransformBlock(buffer, 0, size, null, 0);
|
||||
break;
|
||||
case NonCryptographicHashAlgorithm ncha:
|
||||
var bufferSpan = new ReadOnlySpan<byte>(buffer, 0, size);
|
||||
ncha.Append(bufferSpan);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalize the internal hash algorigthm
|
||||
/// </summary>
|
||||
/// <remarks>NonCryptographicHashAlgorithm implementations do not need finalization</remarks>
|
||||
public void Terminate()
|
||||
{
|
||||
byte[] emptyBuffer = Array.Empty<byte>();
|
||||
switch (_hasher)
|
||||
{
|
||||
case HashAlgorithm ha:
|
||||
ha.TransformFinalBlock(emptyBuffer, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Convert a byte array to a hex string
|
||||
/// </summary>
|
||||
/// <param name="bytes">Byte array to convert</param>
|
||||
/// <returns>Hex string representing the byte array</returns>
|
||||
/// <link>http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa</link>
|
||||
private static string? ByteArrayToString(byte[]? bytes)
|
||||
{
|
||||
// If we get null in, we send null out
|
||||
if (bytes == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
string hex = BitConverter.ToString(bytes);
|
||||
return hex.Replace("-", string.Empty).ToLowerInvariant();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
|
||||
//namespace Compress.ThreadReaders
|
||||
namespace MPF.Core.Hashing
|
||||
{
|
||||
public sealed class ThreadLoadBuffer : IDisposable
|
||||
{
|
||||
private readonly AutoResetEvent _waitEvent;
|
||||
private readonly AutoResetEvent _outEvent;
|
||||
private readonly Thread _tWorker;
|
||||
|
||||
private byte[]? _buffer;
|
||||
private int _size;
|
||||
private readonly Stream _ds;
|
||||
private bool _finished;
|
||||
public bool errorState;
|
||||
|
||||
public int SizeRead;
|
||||
|
||||
public ThreadLoadBuffer(Stream ds)
|
||||
{
|
||||
_waitEvent = new AutoResetEvent(false);
|
||||
_outEvent = new AutoResetEvent(false);
|
||||
_finished = false;
|
||||
_ds = ds;
|
||||
errorState = false;
|
||||
|
||||
_tWorker = new Thread(MainLoop);
|
||||
_tWorker.Start();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_waitEvent.Close();
|
||||
_outEvent.Close();
|
||||
}
|
||||
|
||||
private void MainLoop()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
_waitEvent.WaitOne();
|
||||
if (_finished)
|
||||
{
|
||||
break;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (_buffer != null)
|
||||
SizeRead = _ds.Read(_buffer, 0, _size);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
errorState = true;
|
||||
}
|
||||
_outEvent.Set();
|
||||
}
|
||||
}
|
||||
|
||||
public void Trigger(byte[] buffer, int size)
|
||||
{
|
||||
_buffer = buffer;
|
||||
_size = size;
|
||||
_waitEvent.Set();
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
_outEvent.WaitOne();
|
||||
}
|
||||
|
||||
public void Finish()
|
||||
{
|
||||
_finished = true;
|
||||
_waitEvent.Set();
|
||||
_tWorker.Join();
|
||||
}
|
||||
}
|
||||
}
|
||||
1132
MPF.Core/InfoTool.cs
1132
MPF.Core/InfoTool.cs
File diff suppressed because it is too large
Load Diff
@@ -1,32 +1,63 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.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.3</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>3.0.0</VersionPrefix>
|
||||
<Description>Common code for all MPF implementations</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="MPF.Test" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.0" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Management.Infrastructure" Version="3.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="psxt001z" Version="0.21.0-beta2" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.2.0" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.2.0" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.2.0" />
|
||||
<!-- 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.IO.Hashing" Version="8.0.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.1" 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>
|
||||
|
||||
@@ -10,11 +10,11 @@ using System.Xml.Serialization;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using SabreTools.Models.CueSheets;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Schemas;
|
||||
|
||||
#pragma warning disable CS0618 // Ignore "Type or member is obsolete"
|
||||
#pragma warning disable IDE0051 // Remove unused private members
|
||||
#pragma warning disable IDE0059 // Unnecessary assignment of a value
|
||||
|
||||
namespace MPF.Core.Modules.Aaru
|
||||
@@ -202,7 +202,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
var outputDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
// Ensure that required sections exist
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
info = Builder.EnsureAllSections(info);
|
||||
|
||||
// TODO: Determine if there's an Aaru version anywhere
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
@@ -223,12 +223,12 @@ namespace MPF.Core.Modules.Aaru
|
||||
if (GetDiscType(sidecar, out var discType, out var discSubType))
|
||||
{
|
||||
string fullDiscType = string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
|
||||
if (!string.IsNullOrEmpty(discType) && !string.IsNullOrEmpty(discSubType))
|
||||
fullDiscType = $"{discType} ({discSubType})";
|
||||
else if (!string.IsNullOrWhiteSpace(discType) && string.IsNullOrWhiteSpace(discSubType))
|
||||
fullDiscType = discType;
|
||||
else if (string.IsNullOrWhiteSpace(discType) && !string.IsNullOrWhiteSpace(discSubType))
|
||||
fullDiscType = discSubType;
|
||||
else if (!string.IsNullOrEmpty(discType) && string.IsNullOrEmpty(discSubType))
|
||||
fullDiscType = discType!;
|
||||
else if (string.IsNullOrEmpty(discType) && !string.IsNullOrEmpty(discSubType))
|
||||
fullDiscType = discSubType!;
|
||||
|
||||
info.DumpingInfo.ReportedDiscType = fullDiscType;
|
||||
}
|
||||
@@ -285,7 +285,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
layerbreak = info.SizeAndChecksums!.Size > 25_025_314_816 ? "25025314816" : null;
|
||||
|
||||
// If we have a single-layer disc
|
||||
if (string.IsNullOrWhiteSpace(layerbreak))
|
||||
if (string.IsNullOrEmpty(layerbreak))
|
||||
{
|
||||
// Currently no-op
|
||||
}
|
||||
@@ -409,7 +409,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
info.Artifacts ??= new Dictionary<string, string>();
|
||||
info.Artifacts ??= [];
|
||||
if (File.Exists(basePath + ".cicm.xml"))
|
||||
info.Artifacts["cicm"] = GetBase64(GetFullFile(basePath + ".cicm.xml")) ?? string.Empty;
|
||||
if (File.Exists(basePath + ".ibg"))
|
||||
@@ -456,7 +456,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
|
||||
BaseCommand ??= CommandStrings.NONE;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(BaseCommand))
|
||||
if (!string.IsNullOrEmpty(BaseCommand))
|
||||
parameters.Add(BaseCommand);
|
||||
else
|
||||
return null;
|
||||
@@ -1067,10 +1067,8 @@ namespace MPF.Core.Modules.Aaru
|
||||
// Handle filenames based on command, if necessary
|
||||
switch (BaseCommand)
|
||||
{
|
||||
// Input value only
|
||||
// Input value only (file path)
|
||||
case CommandStrings.ArchivePrefixLong + " " + CommandStrings.ArchiveInfo:
|
||||
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceInfo:
|
||||
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceReport:
|
||||
case CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemInfo:
|
||||
case CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemListLong:
|
||||
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageChecksumLong:
|
||||
@@ -1080,45 +1078,62 @@ namespace MPF.Core.Modules.Aaru
|
||||
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageInfo:
|
||||
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImagePrint:
|
||||
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageVerify:
|
||||
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaInfo:
|
||||
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaScan:
|
||||
if (string.IsNullOrWhiteSpace(InputValue))
|
||||
if (string.IsNullOrEmpty(InputValue))
|
||||
return null;
|
||||
|
||||
parameters.Add($"\"{InputValue}\"");
|
||||
break;
|
||||
|
||||
// Input value only (device path)
|
||||
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceInfo:
|
||||
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceReport:
|
||||
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaInfo:
|
||||
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaScan:
|
||||
if (string.IsNullOrEmpty(InputValue))
|
||||
return null;
|
||||
|
||||
parameters.Add(InputValue!.TrimEnd('\\'));
|
||||
break;
|
||||
|
||||
// Two input values
|
||||
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageCompareLong:
|
||||
if (string.IsNullOrWhiteSpace(Input1Value) || string.IsNullOrWhiteSpace(Input2Value))
|
||||
if (string.IsNullOrEmpty(Input1Value) || string.IsNullOrEmpty(Input2Value))
|
||||
return null;
|
||||
|
||||
parameters.Add($"\"{Input1Value}\"");
|
||||
parameters.Add($"\"{Input2Value}\"");
|
||||
break;
|
||||
|
||||
// Input and Output value
|
||||
// Input and Output value (file path)
|
||||
case CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemExtract:
|
||||
case CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageConvert:
|
||||
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaDump:
|
||||
if (string.IsNullOrWhiteSpace(InputValue) || string.IsNullOrWhiteSpace(OutputValue))
|
||||
if (string.IsNullOrEmpty(InputValue) || string.IsNullOrEmpty(OutputValue))
|
||||
return null;
|
||||
|
||||
parameters.Add($"\"{InputValue}\"");
|
||||
parameters.Add($"\"{OutputValue}\"");
|
||||
break;
|
||||
|
||||
// Input and Output value (device path)
|
||||
case CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaDump:
|
||||
if (string.IsNullOrEmpty(InputValue) || string.IsNullOrEmpty(OutputValue))
|
||||
return null;
|
||||
|
||||
parameters.Add(InputValue!.TrimEnd('\\'));
|
||||
parameters.Add($"\"{OutputValue}\"");
|
||||
break;
|
||||
|
||||
// Remote host value only
|
||||
case CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceList:
|
||||
case CommandStrings.Remote:
|
||||
if (string.IsNullOrWhiteSpace(RemoteHostValue))
|
||||
if (string.IsNullOrEmpty(RemoteHostValue))
|
||||
return null;
|
||||
|
||||
parameters.Add($"\"{RemoteHostValue}\"");
|
||||
break;
|
||||
}
|
||||
|
||||
return string.Join(" ", parameters);
|
||||
return string.Join(" ", [.. parameters]);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -1128,48 +1143,42 @@ namespace MPF.Core.Modules.Aaru
|
||||
{
|
||||
#region Archive Family
|
||||
|
||||
[CommandStrings.ArchivePrefixLong + " " + CommandStrings.ArchiveInfo] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.ArchivePrefixLong + " " + CommandStrings.ArchiveInfo] = [],
|
||||
|
||||
#endregion
|
||||
|
||||
#region Database Family
|
||||
|
||||
[CommandStrings.DatabasePrefixLong + " " + CommandStrings.DatabaseStats] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.DatabasePrefixLong + " " + CommandStrings.DatabaseStats] = [],
|
||||
|
||||
[CommandStrings.DatabasePrefixLong + " " + CommandStrings.DatabaseUpdate] = new List<string>()
|
||||
{
|
||||
[CommandStrings.DatabasePrefixLong + " " + CommandStrings.DatabaseUpdate] =
|
||||
[
|
||||
FlagStrings.ClearLong,
|
||||
FlagStrings.ClearAllLong,
|
||||
},
|
||||
],
|
||||
|
||||
#endregion
|
||||
|
||||
#region Device Family
|
||||
|
||||
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceInfo] = new List<string>()
|
||||
{
|
||||
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceInfo] =
|
||||
[
|
||||
FlagStrings.OutputPrefixLong,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceList] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceList] = [],
|
||||
|
||||
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceReport] = new List<string>()
|
||||
{
|
||||
[CommandStrings.DevicePrefixLong + " " + CommandStrings.DeviceReport] =
|
||||
[
|
||||
FlagStrings.TrapDiscLong,
|
||||
},
|
||||
],
|
||||
|
||||
#endregion
|
||||
|
||||
#region Filesystem Family
|
||||
|
||||
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemExtract] = new List<string>()
|
||||
{
|
||||
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemExtract] =
|
||||
[
|
||||
FlagStrings.EncodingLong,
|
||||
FlagStrings.EncodingShort,
|
||||
FlagStrings.ExtendedAttributesLong,
|
||||
@@ -1178,10 +1187,10 @@ namespace MPF.Core.Modules.Aaru
|
||||
FlagStrings.NamespaceShort,
|
||||
FlagStrings.OptionsLong,
|
||||
FlagStrings.OptionsShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemInfo] = new List<string>()
|
||||
{
|
||||
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemInfo] =
|
||||
[
|
||||
FlagStrings.EncodingLong,
|
||||
FlagStrings.EncodingShort,
|
||||
FlagStrings.ExtendedAttributesLong,
|
||||
@@ -1190,10 +1199,10 @@ namespace MPF.Core.Modules.Aaru
|
||||
FlagStrings.NamespaceShort,
|
||||
FlagStrings.OptionsLong,
|
||||
FlagStrings.OptionsShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemListLong] = new List<string>()
|
||||
{
|
||||
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemListLong] =
|
||||
[
|
||||
FlagStrings.EncodingLong,
|
||||
FlagStrings.EncodingShort,
|
||||
FlagStrings.FilesystemsLong,
|
||||
@@ -1202,18 +1211,16 @@ namespace MPF.Core.Modules.Aaru
|
||||
FlagStrings.LongFormatShort,
|
||||
FlagStrings.PartitionsLong,
|
||||
FlagStrings.PartitionsShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemOptions] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.FilesystemPrefixLong + " " + CommandStrings.FilesystemOptions] = [],
|
||||
|
||||
#endregion
|
||||
|
||||
#region Image Family
|
||||
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageChecksumLong] = new List<string>()
|
||||
{
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageChecksumLong] =
|
||||
[
|
||||
FlagStrings.Adler32Long,
|
||||
FlagStrings.Adler32Short,
|
||||
FlagStrings.CRC16Long,
|
||||
@@ -1235,14 +1242,12 @@ namespace MPF.Core.Modules.Aaru
|
||||
FlagStrings.SpamSumShort,
|
||||
FlagStrings.WholeDiscLong,
|
||||
FlagStrings.WholeDiscShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageCompareLong] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageCompareLong] = [],
|
||||
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageConvert] = new List<string>()
|
||||
{
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageConvert] =
|
||||
[
|
||||
FlagStrings.CommentsLong,
|
||||
FlagStrings.CountLong,
|
||||
FlagStrings.CountShort,
|
||||
@@ -1275,20 +1280,20 @@ namespace MPF.Core.Modules.Aaru
|
||||
FlagStrings.ResumeFileShort,
|
||||
FlagStrings.XMLSidecarLong,
|
||||
FlagStrings.XMLSidecarShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageCreateSidecar] = new List<string>()
|
||||
{
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageCreateSidecar] =
|
||||
[
|
||||
FlagStrings.BlockSizeLong,
|
||||
FlagStrings.BlockSizeShort,
|
||||
FlagStrings.EncodingLong,
|
||||
FlagStrings.EncodingShort,
|
||||
FlagStrings.TapeLong,
|
||||
FlagStrings.TapeShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageDecode] = new List<string>()
|
||||
{
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageDecode] =
|
||||
[
|
||||
FlagStrings.DiskTagsLong,
|
||||
FlagStrings.DiskTagsShort,
|
||||
FlagStrings.LengthLong,
|
||||
@@ -1297,28 +1302,24 @@ namespace MPF.Core.Modules.Aaru
|
||||
FlagStrings.SectorTagsShort,
|
||||
FlagStrings.StartLong,
|
||||
FlagStrings.StartShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageEntropy] = new List<string>()
|
||||
{
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageEntropy] =
|
||||
[
|
||||
FlagStrings.DuplicatedSectorsLong,
|
||||
FlagStrings.DuplicatedSectorsShort,
|
||||
FlagStrings.SeparatedTracksLong,
|
||||
FlagStrings.SeparatedTracksShort,
|
||||
FlagStrings.WholeDiscLong,
|
||||
FlagStrings.WholeDiscShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageInfo] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageInfo] = [],
|
||||
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageOptions] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageOptions] = [],
|
||||
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImagePrint] = new List<string>()
|
||||
{
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImagePrint] =
|
||||
[
|
||||
FlagStrings.LengthLong,
|
||||
FlagStrings.LengthShort,
|
||||
FlagStrings.LongSectorsLong,
|
||||
@@ -1327,22 +1328,22 @@ namespace MPF.Core.Modules.Aaru
|
||||
FlagStrings.StartShort,
|
||||
FlagStrings.WidthLong,
|
||||
FlagStrings.WidthShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageVerify] = new List<string>()
|
||||
{
|
||||
[CommandStrings.ImagePrefixLong + " " + CommandStrings.ImageVerify] =
|
||||
[
|
||||
FlagStrings.VerifyDiscLong,
|
||||
FlagStrings.VerifyDiscShort,
|
||||
FlagStrings.VerifySectorsLong,
|
||||
FlagStrings.VerifySectorsShort,
|
||||
},
|
||||
],
|
||||
|
||||
#endregion
|
||||
|
||||
#region Media Family
|
||||
|
||||
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaDump] = new List<string>()
|
||||
{
|
||||
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaDump] =
|
||||
[
|
||||
FlagStrings.EjectLong,
|
||||
FlagStrings.EncodingLong,
|
||||
FlagStrings.EncodingShort,
|
||||
@@ -1380,29 +1381,29 @@ namespace MPF.Core.Modules.Aaru
|
||||
FlagStrings.UseBufferedReadsLong,
|
||||
FlagStrings.XMLSidecarLong,
|
||||
FlagStrings.XMLSidecarShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaInfo] = new List<string>()
|
||||
{
|
||||
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaInfo] =
|
||||
[
|
||||
FlagStrings.OutputPrefixLong,
|
||||
FlagStrings.OutputPrefixShort,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaScan] = new List<string>()
|
||||
{
|
||||
[CommandStrings.MediaPrefixLong + " " + CommandStrings.MediaScan] =
|
||||
[
|
||||
FlagStrings.ImgBurnLogLong,
|
||||
FlagStrings.ImgBurnLogShort,
|
||||
FlagStrings.MHDDLogLong,
|
||||
FlagStrings.MHDDLogShort,
|
||||
FlagStrings.UseBufferedReadsLong,
|
||||
},
|
||||
],
|
||||
|
||||
#endregion
|
||||
|
||||
#region Standalone Commands
|
||||
|
||||
[CommandStrings.NONE] = new List<string>()
|
||||
{
|
||||
[CommandStrings.NONE] =
|
||||
[
|
||||
FlagStrings.DebugLong,
|
||||
FlagStrings.DebugShort,
|
||||
FlagStrings.HelpLong,
|
||||
@@ -1411,27 +1412,17 @@ namespace MPF.Core.Modules.Aaru
|
||||
FlagStrings.VerboseLong,
|
||||
FlagStrings.VerboseShort,
|
||||
FlagStrings.VersionLong,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.Configure] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Configure] = [],
|
||||
|
||||
[CommandStrings.Formats] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Formats] = [],
|
||||
|
||||
[CommandStrings.ListEncodings] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.ListEncodings] = [],
|
||||
|
||||
[CommandStrings.ListNamespaces] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.ListNamespaces] = [],
|
||||
|
||||
[CommandStrings.Remote] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Remote] = [],
|
||||
|
||||
#endregion
|
||||
};
|
||||
@@ -1500,7 +1491,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
flags = new Dictionary<string, bool?>();
|
||||
flags = [];
|
||||
|
||||
BlockSizeValue = null;
|
||||
CommentsValue = null;
|
||||
@@ -1625,12 +1616,12 @@ namespace MPF.Core.Modules.Aaru
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
// The string has to be valid by itself first
|
||||
if (string.IsNullOrWhiteSpace(parameters))
|
||||
if (string.IsNullOrEmpty(parameters))
|
||||
return false;
|
||||
|
||||
// Now split the string into parts for easier validation
|
||||
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
|
||||
parameters = parameters.Trim();
|
||||
parameters = parameters!.Trim();
|
||||
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Value)
|
||||
@@ -1670,7 +1661,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
|
||||
// Determine what the commandline should look like given the first item
|
||||
BaseCommand = NormalizeCommand(parts, ref start);
|
||||
if (string.IsNullOrWhiteSpace(BaseCommand))
|
||||
if (string.IsNullOrEmpty(BaseCommand))
|
||||
return false;
|
||||
|
||||
// Set the start position
|
||||
@@ -2175,7 +2166,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
private static string? NormalizeCommand(string baseCommand)
|
||||
{
|
||||
// If the base command is inavlid, just return nulls
|
||||
if (string.IsNullOrWhiteSpace(baseCommand))
|
||||
if (string.IsNullOrEmpty(baseCommand))
|
||||
return null;
|
||||
|
||||
// Split the command otherwise
|
||||
@@ -2297,11 +2288,11 @@ namespace MPF.Core.Modules.Aaru
|
||||
}
|
||||
|
||||
// If the command itself is invalid, then return null
|
||||
if (string.IsNullOrWhiteSpace(command))
|
||||
if (string.IsNullOrEmpty(command))
|
||||
return null;
|
||||
|
||||
// Combine the result
|
||||
if (!string.IsNullOrWhiteSpace(family))
|
||||
if (!string.IsNullOrEmpty(family))
|
||||
return $"{family} {command}";
|
||||
else
|
||||
return command;
|
||||
@@ -2384,7 +2375,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
var cueFiles = new List<CueFile>();
|
||||
var cueSheet = new CueSheet
|
||||
{
|
||||
Performer = string.Join(", ", cicmSidecar.Performer ?? Array.Empty<string>()),
|
||||
Performer = string.Join(", ", cicmSidecar.Performer ?? []),
|
||||
};
|
||||
|
||||
// Only care about OpticalDisc types
|
||||
@@ -2466,14 +2457,14 @@ namespace MPF.Core.Modules.Aaru
|
||||
}
|
||||
}
|
||||
|
||||
cueTrack.Indices = cueIndicies.ToArray();
|
||||
cueTrack.Indices = [.. cueIndicies];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default if index data missing from sidecar
|
||||
cueTrack.Indices = new CueIndex[]
|
||||
{
|
||||
new CueIndex
|
||||
new()
|
||||
{
|
||||
Index = 1,
|
||||
Minutes = 0,
|
||||
@@ -2492,7 +2483,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
}
|
||||
|
||||
// If we have a cuesheet to write out, do so
|
||||
cueSheet.Files = cueFiles.ToArray();
|
||||
cueSheet.Files = [.. cueFiles];
|
||||
if (cueSheet != null && cueSheet != default)
|
||||
{
|
||||
var ms = new SabreTools.Serialization.Streams.CueSheet().Serialize(cueSheet);
|
||||
@@ -2722,7 +2713,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
|
||||
// Assign the roms to a new game
|
||||
datafile.Games = new Game[1];
|
||||
datafile.Games[0] = new Game { Roms = roms.ToArray() };
|
||||
datafile.Games[0] = new Game { Roms = [.. roms] };
|
||||
|
||||
return datafile;
|
||||
}
|
||||
@@ -2777,12 +2768,28 @@ namespace MPF.Core.Modules.Aaru
|
||||
|
||||
// Build each row in consecutive order
|
||||
string pvd = string.Empty;
|
||||
pvd += GenerateSectorOutputLine("0320", new ReadOnlySpan<byte>(pvdData, 0, 16));
|
||||
pvd += GenerateSectorOutputLine("0330", new ReadOnlySpan<byte>(pvdData, 16, 16));
|
||||
pvd += GenerateSectorOutputLine("0340", new ReadOnlySpan<byte>(pvdData, 32, 16));
|
||||
pvd += GenerateSectorOutputLine("0350", new ReadOnlySpan<byte>(pvdData, 48, 16));
|
||||
pvd += GenerateSectorOutputLine("0360", new ReadOnlySpan<byte>(pvdData, 64, 16));
|
||||
pvd += GenerateSectorOutputLine("0370", new ReadOnlySpan<byte>(pvdData, 80, 16));
|
||||
#if NET20 || NET35 || NET40
|
||||
byte[] pvdLine = new byte[16];
|
||||
Array.Copy(pvdData, 0, pvdLine, 0, 16);
|
||||
pvd += GenerateSectorOutputLine("0320", pvdLine);
|
||||
Array.Copy(pvdData, 16, pvdLine, 0, 16);
|
||||
pvd += GenerateSectorOutputLine("0330", pvdLine);
|
||||
Array.Copy(pvdData, 32, pvdLine, 0, 16);
|
||||
pvd += GenerateSectorOutputLine("0340", pvdLine);
|
||||
Array.Copy(pvdData, 48, pvdLine, 0, 16);
|
||||
pvd += GenerateSectorOutputLine("0350", pvdLine);
|
||||
Array.Copy(pvdData, 64, pvdLine, 0, 16);
|
||||
pvd += GenerateSectorOutputLine("0360", pvdLine);
|
||||
Array.Copy(pvdData, 80, pvdLine, 0, 16);
|
||||
pvd += GenerateSectorOutputLine("0370", pvdLine);
|
||||
#else
|
||||
pvd += GenerateSectorOutputLine("0320", new ReadOnlySpan<byte>(pvdData, 0, 16).ToArray());
|
||||
pvd += GenerateSectorOutputLine("0330", new ReadOnlySpan<byte>(pvdData, 16, 16).ToArray());
|
||||
pvd += GenerateSectorOutputLine("0340", new ReadOnlySpan<byte>(pvdData, 32, 16).ToArray());
|
||||
pvd += GenerateSectorOutputLine("0350", new ReadOnlySpan<byte>(pvdData, 48, 16).ToArray());
|
||||
pvd += GenerateSectorOutputLine("0360", new ReadOnlySpan<byte>(pvdData, 64, 16).ToArray());
|
||||
pvd += GenerateSectorOutputLine("0370", new ReadOnlySpan<byte>(pvdData, 80, 16).ToArray());
|
||||
#endif
|
||||
|
||||
return pvd;
|
||||
}
|
||||
@@ -2883,7 +2890,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
pvdData.AddRange(new string((char)0, 14).ToCharArray().Select(c => (byte)c));
|
||||
|
||||
// Return the filled array
|
||||
return pvdData.ToArray();
|
||||
return [.. pvdData];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2924,27 +2931,27 @@ namespace MPF.Core.Modules.Aaru
|
||||
// Get and return the byte array
|
||||
List<byte> dateTimeList = dateTimeString.ToCharArray().Select(c => (byte)c).ToList();
|
||||
dateTimeList.Add(timeZoneNumber);
|
||||
return dateTimeList.ToArray();
|
||||
return [.. dateTimeList];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a single 16-byte sector line from a byte array
|
||||
/// </summary>
|
||||
/// <param name="row">Row ID for outputting</param>
|
||||
/// <param name="bytes">Byte span representing the data to write</param>
|
||||
/// <param name="bytes">Bytes representing the data to write</param>
|
||||
/// <returns>Formatted string representing the sector line</returns>
|
||||
private static string? GenerateSectorOutputLine(string row, ReadOnlySpan<byte> bytes)
|
||||
private static string? GenerateSectorOutputLine(string row, byte[] bytes)
|
||||
{
|
||||
// If the data isn't correct, return null
|
||||
if (bytes == null || bytes.Length != 16)
|
||||
return null;
|
||||
|
||||
string pvdLine = $"{row} : ";
|
||||
pvdLine += BitConverter.ToString(bytes[..8].ToArray()).Replace("-", " ");
|
||||
pvdLine += BitConverter.ToString(bytes.Take(8).ToArray()).Replace("-", " ");
|
||||
pvdLine += " ";
|
||||
pvdLine += BitConverter.ToString(bytes.Slice(8, 8).ToArray().ToArray()).Replace("-", " ");
|
||||
pvdLine += BitConverter.ToString(bytes.Skip(8).Take(8).ToArray()).Replace("-", " ");
|
||||
pvdLine += " ";
|
||||
pvdLine += Encoding.ASCII.GetString(bytes.ToArray()).Replace((char)0, '.').Replace('?', '.');
|
||||
pvdLine += Encoding.ASCII.GetString([.. bytes]).Replace((char)0, '.').Replace('?', '.');
|
||||
pvdLine += "\n";
|
||||
|
||||
return pvdLine;
|
||||
@@ -2965,7 +2972,9 @@ namespace MPF.Core.Modules.Aaru
|
||||
XmlReader xtr = XmlReader.Create(cicmSidecar, new XmlReaderSettings
|
||||
{
|
||||
CheckCharacters = false,
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
DtdProcessing = DtdProcessing.Ignore,
|
||||
#endif
|
||||
IgnoreComments = true,
|
||||
IgnoreWhitespace = true,
|
||||
ValidationFlags = XmlSchemaValidationFlags.None,
|
||||
@@ -3032,7 +3041,7 @@ namespace MPF.Core.Modules.Aaru
|
||||
// Loop through each OpticalDisc in the metadata
|
||||
foreach (OpticalDiscType opticalDisc in cicmSidecar.OpticalDisc)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(opticalDisc.CopyProtection))
|
||||
if (!string.IsNullOrEmpty(opticalDisc.CopyProtection))
|
||||
copyrightProtectionSystemType += $", {opticalDisc.CopyProtection}";
|
||||
}
|
||||
|
||||
@@ -3084,9 +3093,9 @@ namespace MPF.Core.Modules.Aaru
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
|
||||
// Initialize on seeing the open tag
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
if (string.IsNullOrEmpty(line))
|
||||
continue;
|
||||
else if (line.StartsWith("<BadBlocks>"))
|
||||
else if (line!.StartsWith("<BadBlocks>"))
|
||||
totalErrors = 0;
|
||||
else if (line.StartsWith("</BadBlocks>"))
|
||||
return totalErrors ?? -1;
|
||||
@@ -3400,6 +3409,6 @@ namespace MPF.Core.Modules.Aaru
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -14,11 +15,27 @@ namespace MPF.Core.Modules
|
||||
{
|
||||
#region Event Handlers
|
||||
|
||||
#if NET20 || NET35 || NET40
|
||||
/// <summary>
|
||||
/// Wrapper event args class for old .NET
|
||||
/// </summary>
|
||||
public class StringEventArgs : EventArgs
|
||||
{
|
||||
public string? Value { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Geneeic way of reporting a message
|
||||
/// </summary>
|
||||
/// <param name="message">String value to report</param>
|
||||
public EventHandler<StringEventArgs>? ReportStatus;
|
||||
#else
|
||||
/// <summary>
|
||||
/// Geneeic way of reporting a message
|
||||
/// </summary>
|
||||
/// <param name="message">String value to report</param>
|
||||
public EventHandler<string>? ReportStatus;
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -32,7 +49,7 @@ namespace MPF.Core.Modules
|
||||
/// <summary>
|
||||
/// Set of flags to pass to the executable
|
||||
/// </summary>
|
||||
protected Dictionary<string, bool?> flags = new();
|
||||
protected Dictionary<string, bool?> flags = [];
|
||||
protected internal IEnumerable<string> Keys => flags.Keys;
|
||||
|
||||
/// <summary>
|
||||
@@ -42,8 +59,8 @@ namespace MPF.Core.Modules
|
||||
{
|
||||
get
|
||||
{
|
||||
if (flags.ContainsKey(key))
|
||||
return flags[key];
|
||||
if (flags.TryGetValue(key, out bool? val))
|
||||
return val;
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -58,6 +75,11 @@ namespace MPF.Core.Modules
|
||||
/// </summary>
|
||||
private Process? process;
|
||||
|
||||
/// <summary>
|
||||
/// All found volume labels and their corresponding file systems
|
||||
/// </summary>
|
||||
public Dictionary<string, List<string>>? VolumeLabels;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Dumping Information
|
||||
@@ -216,9 +238,9 @@ namespace MPF.Core.Modules
|
||||
return false;
|
||||
if (this.BaseCommand == null)
|
||||
return false;
|
||||
if (!CommandSupport.ContainsKey(this.BaseCommand))
|
||||
if (!CommandSupport.TryGetValue(this.BaseCommand, out var supported))
|
||||
return false;
|
||||
return CommandSupport[this.BaseCommand].Contains(flag);
|
||||
return supported.Contains(flag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -246,7 +268,7 @@ namespace MPF.Core.Modules
|
||||
/// </summary>
|
||||
/// <param name="parameters">String possibly representing parameters</param>
|
||||
/// <returns>True if the parameters were set correctly, false otherwise</returns>
|
||||
protected virtual bool ValidateAndSetParameters(string? parameters) => !string.IsNullOrWhiteSpace(parameters);
|
||||
protected virtual bool ValidateAndSetParameters(string? parameters) => !string.IsNullOrEmpty(parameters);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -261,7 +283,7 @@ namespace MPF.Core.Modules
|
||||
// Create the start info
|
||||
var startInfo = new ProcessStartInfo()
|
||||
{
|
||||
FileName = ExecutablePath,
|
||||
FileName = ExecutablePath!,
|
||||
Arguments = GenerateParameters() ?? "",
|
||||
CreateNoWindow = !separateWindow,
|
||||
UseShellExecute = separateWindow,
|
||||
@@ -278,8 +300,13 @@ namespace MPF.Core.Modules
|
||||
// Start processing tasks, if necessary
|
||||
if (!separateWindow)
|
||||
{
|
||||
#if NET40
|
||||
Logging.OutputToLog(process.StandardOutput, this, ReportStatus);
|
||||
Logging.OutputToLog(process.StandardError, this, ReportStatus);
|
||||
#else
|
||||
_ = Logging.OutputToLog(process.StandardOutput, this, ReportStatus);
|
||||
_ = Logging.OutputToLog(process.StandardError, this, ReportStatus);
|
||||
#endif
|
||||
}
|
||||
|
||||
process.WaitForExit();
|
||||
@@ -302,7 +329,7 @@ namespace MPF.Core.Modules
|
||||
{ }
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Parameter Parsing
|
||||
|
||||
@@ -613,7 +640,12 @@ namespace MPF.Core.Modules
|
||||
i++;
|
||||
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return (sbyte)(sbyte.Parse(value) * factor);
|
||||
if (sbyte.TryParse(value, out sbyte sByteValue))
|
||||
return (sbyte)(sByteValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (sbyte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out sbyte sByteHexValue))
|
||||
return (sbyte)(sByteHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
@@ -628,7 +660,12 @@ namespace MPF.Core.Modules
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (sbyte)(sbyte.Parse(value) * factor);
|
||||
if (sbyte.TryParse(value, out sbyte sByteValue))
|
||||
return (sbyte)(sByteValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (sbyte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out sbyte sByteHexValue))
|
||||
return (sbyte)(sByteHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
|
||||
return SByte.MinValue;
|
||||
@@ -690,7 +727,12 @@ namespace MPF.Core.Modules
|
||||
this[longFlagString] = true;
|
||||
i++;
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return (short)(short.Parse(value) * factor);
|
||||
if (short.TryParse(value, out short shortValue))
|
||||
return (short)(shortValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (short.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out short shortHexValue))
|
||||
return (short)(shortHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
@@ -705,7 +747,12 @@ namespace MPF.Core.Modules
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (short)(short.Parse(value) * factor);
|
||||
if (short.TryParse(value, out short shortValue))
|
||||
return (short)(shortValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (short.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out short shortHexValue))
|
||||
return (short)(shortHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
|
||||
return Int16.MinValue;
|
||||
@@ -767,7 +814,12 @@ namespace MPF.Core.Modules
|
||||
this[longFlagString] = true;
|
||||
i++;
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return (int)(int.Parse(value) * factor);
|
||||
if (int.TryParse(value, out int intValue))
|
||||
return (int)(intValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (int.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out int intHexValue))
|
||||
return (int)(intHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
@@ -782,7 +834,12 @@ namespace MPF.Core.Modules
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (int)(int.Parse(value) * factor);
|
||||
if (int.TryParse(value, out int intValue))
|
||||
return (int)(intValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (int.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out int intHexValue))
|
||||
return (int)(intHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
|
||||
return Int32.MinValue;
|
||||
@@ -844,7 +901,12 @@ namespace MPF.Core.Modules
|
||||
this[longFlagString] = true;
|
||||
i++;
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return long.Parse(value) * factor;
|
||||
if (long.TryParse(value, out long longValue))
|
||||
return (long)(longValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long longHexValue))
|
||||
return (long)(longHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
@@ -859,7 +921,12 @@ namespace MPF.Core.Modules
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return long.Parse(value) * factor;
|
||||
if (long.TryParse(value, out long longValue))
|
||||
return (long)(longValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (long.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out long longHexValue))
|
||||
return (long)(longHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
|
||||
return Int64.MinValue;
|
||||
@@ -910,7 +977,7 @@ namespace MPF.Core.Modules
|
||||
|
||||
return null;
|
||||
}
|
||||
else if (string.IsNullOrWhiteSpace(parts[i + 1]))
|
||||
else if (string.IsNullOrEmpty(parts[i + 1]))
|
||||
{
|
||||
if (missingAllowed)
|
||||
this[longFlagString] = true;
|
||||
@@ -997,7 +1064,12 @@ namespace MPF.Core.Modules
|
||||
i++;
|
||||
|
||||
(string value, long factor) = ExtractFactorFromValue(parts[i]);
|
||||
return (byte)(byte.Parse(value) * factor);
|
||||
if (byte.TryParse(value, out byte byteValue))
|
||||
return (byte)(byteValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (byte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out byte byteHexValue))
|
||||
return (byte)(byteHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
else if (parts[i].StartsWith(shortFlagString + "=") || parts[i].StartsWith(longFlagString + "="))
|
||||
{
|
||||
@@ -1012,14 +1084,19 @@ namespace MPF.Core.Modules
|
||||
|
||||
this[longFlagString] = true;
|
||||
(string value, long factor) = ExtractFactorFromValue(valuePart);
|
||||
return (byte)(byte.Parse(value) * factor);
|
||||
if (byte.TryParse(value, out byte byteValue))
|
||||
return (byte)(byteValue * factor);
|
||||
string hexValue = RemoveHexIdentifier(value);
|
||||
if (byte.TryParse(hexValue, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out byte byteHexValue))
|
||||
return (byte)(byteHexValue * factor);
|
||||
return null;
|
||||
}
|
||||
|
||||
return Byte.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get yhe trimmed value and multiplication factor from a value
|
||||
/// Get the trimmed value and multiplication factor from a value
|
||||
/// </summary>
|
||||
/// <param name="value">String value to treat as suffixed number</param>
|
||||
/// <returns>Trimmed value and multiplication factor</returns>
|
||||
@@ -1080,6 +1157,23 @@ namespace MPF.Core.Modules
|
||||
return (value, factor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a leading 0x if it exists, case insensitive
|
||||
/// </summary>
|
||||
/// <param name="value">String with removed leading 0x</param>
|
||||
/// <returns></returns>
|
||||
private static string RemoveHexIdentifier(string value)
|
||||
{
|
||||
if (value.Length <= 2)
|
||||
return value;
|
||||
if (value[0] != '0')
|
||||
return value;
|
||||
if (value[1] != 'x' && value[1] != 'X')
|
||||
return value;
|
||||
|
||||
return value.Substring(2);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods to Move
|
||||
@@ -1104,7 +1198,7 @@ namespace MPF.Core.Modules
|
||||
return null;
|
||||
|
||||
if (trimLength > -1)
|
||||
hex = hex[..trimLength];
|
||||
hex = hex.Substring(0, trimLength);
|
||||
|
||||
return Regex.Replace(hex, ".{32}", "$0\n", RegexOptions.Compiled);
|
||||
}
|
||||
@@ -1115,8 +1209,6 @@ namespace MPF.Core.Modules
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
#pragma warning disable IDE0051 // Remove unused private members
|
||||
|
||||
namespace MPF.Core.Modules.CleanRip
|
||||
{
|
||||
/// <summary>
|
||||
@@ -64,14 +63,18 @@ namespace MPF.Core.Modules.CleanRip
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
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))
|
||||
{
|
||||
@@ -94,11 +97,12 @@ namespace MPF.Core.Modules.CleanRip
|
||||
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))
|
||||
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;
|
||||
@@ -107,7 +111,7 @@ namespace MPF.Core.Modules.CleanRip
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
info.Artifacts ??= new Dictionary<string, string>();
|
||||
info.Artifacts ??= [];
|
||||
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
info.Artifacts["bca"] = GetBase64(GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty;
|
||||
@@ -168,28 +172,28 @@ namespace MPF.Core.Modules.CleanRip
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
if (string.IsNullOrEmpty(line))
|
||||
continue;
|
||||
else if (line.StartsWith("CRC32"))
|
||||
crc = line[7..].ToLowerInvariant();
|
||||
else if (line!.StartsWith("CRC32"))
|
||||
crc = line.Substring(7).ToLowerInvariant();
|
||||
else if (line.StartsWith("MD5"))
|
||||
md5 = line[5..];
|
||||
md5 = line.Substring(5);
|
||||
else if (line.StartsWith("SHA-1"))
|
||||
sha1 = line[7..];
|
||||
sha1 = line.Substring(7);
|
||||
}
|
||||
|
||||
return new Datafile
|
||||
{
|
||||
Games = new Game[]
|
||||
{
|
||||
new Game
|
||||
Games =
|
||||
[
|
||||
new()
|
||||
{
|
||||
Roms = new Rom[]
|
||||
{
|
||||
Roms =
|
||||
[
|
||||
new Rom { Name = Path.GetFileName(iso), Size = size.ToString(), Crc = crc, Md5 = md5, Sha1 = sha1 },
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
catch
|
||||
@@ -254,14 +258,14 @@ namespace MPF.Core.Modules.CleanRip
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
if (string.IsNullOrEmpty(line))
|
||||
continue;
|
||||
else if (line.StartsWith("CRC32"))
|
||||
crc = line[7..].ToLowerInvariant();
|
||||
else if (line!.StartsWith("CRC32"))
|
||||
crc = line.Substring(7).ToLowerInvariant();
|
||||
else if (line.StartsWith("MD5"))
|
||||
md5 = line[5..];
|
||||
md5 = line.Substring(5);
|
||||
else if (line.StartsWith("SHA-1"))
|
||||
sha1 = line[7..];
|
||||
sha1 = line.Substring(7);
|
||||
}
|
||||
|
||||
return $"<rom name=\"{Path.GetFileName(iso)}\" size=\"{size}\" crc=\"{crc}\" md5=\"{md5}\" sha1=\"{sha1}\" />";
|
||||
@@ -280,10 +284,11 @@ namespace MPF.Core.Modules.CleanRip
|
||||
/// <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)
|
||||
private static bool GetGameCubeWiiInformation(string dumpinfo, out Region? region, out string? version, out string? name, out string? serial)
|
||||
{
|
||||
region = null; version = null; name = null;
|
||||
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))
|
||||
@@ -300,21 +305,23 @@ namespace MPF.Core.Modules.CleanRip
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
var line = sr.ReadLine()?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(line))
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (line.StartsWith("Version"))
|
||||
else if (line!.StartsWith("Version"))
|
||||
{
|
||||
version = line["Version: ".Length..];
|
||||
version = line.Substring("Version: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("Internal Name"))
|
||||
{
|
||||
name = line["Internal Name: ".Length..];
|
||||
name = line.Substring("Internal Name: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("Filename"))
|
||||
{
|
||||
string serial = line["Filename: ".Length..];
|
||||
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];
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Modules.DiscImageCreator
|
||||
@@ -384,7 +384,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
var outputDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
// Ensure that required sections exist
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
info = Builder.EnsureAllSections(info);
|
||||
|
||||
// Get the dumping program and version
|
||||
var (dicCmd, dicVersion) = GetCommandFilePathAndVersion(basePath);
|
||||
@@ -409,6 +409,10 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// Fill in the hash data
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
|
||||
// Fill in the volume labels
|
||||
if (GetVolumeLabels($"{basePath}_volDesc.txt", out var volLabels))
|
||||
VolumeLabels = volLabels;
|
||||
|
||||
// Extract info based generically on MediaType
|
||||
switch (this.Type)
|
||||
{
|
||||
@@ -442,7 +446,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
// Attempt to get multisession data
|
||||
string cdMultiSessionInfo = GetMultisessionInformation($"{basePath}_disc.txt") ?? string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(cdMultiSessionInfo))
|
||||
if (!string.IsNullOrEmpty(cdMultiSessionInfo))
|
||||
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.Multisession] = cdMultiSessionInfo;
|
||||
|
||||
break;
|
||||
@@ -496,7 +500,12 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
trimLength = 264;
|
||||
if (info.SizeAndChecksums!.Layerbreak3 != default)
|
||||
trimLength = 520;
|
||||
else if (info.SizeAndChecksums!.Layerbreak2 != default)
|
||||
trimLength = 392;
|
||||
else
|
||||
trimLength = 264;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -521,20 +530,25 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
info.CopyProtection!.SecuROMData = GetFullFile($"{basePath}_subIntention.txt") ?? string.Empty;
|
||||
}
|
||||
|
||||
// Needed for some odd copy protections
|
||||
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt", false) ?? string.Empty;
|
||||
|
||||
break;
|
||||
|
||||
case RedumpSystem.DVDAudio:
|
||||
case RedumpSystem.DVDVideo:
|
||||
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt") ?? string.Empty;
|
||||
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}_CSSKey.txt", $"{basePath}_disc.txt", true) ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiPython2:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name));
|
||||
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= pythonTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
@@ -543,7 +557,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
case RedumpSystem.MicrosoftXbox:
|
||||
|
||||
string xmidString;
|
||||
if (string.IsNullOrWhiteSpace(outputDirectory))
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
xmidString = GetXGD1XMID($"{basePath}_DMI.bin");
|
||||
else
|
||||
xmidString = GetXGD1XMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
|
||||
@@ -592,7 +606,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
case RedumpSystem.MicrosoftXbox360:
|
||||
string xemidString;
|
||||
if (string.IsNullOrWhiteSpace(outputDirectory))
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
xemidString = GetXGD23XeMID($"{basePath}_DMI.bin");
|
||||
else
|
||||
xemidString = GetXGD23XeMID(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"));
|
||||
@@ -646,7 +660,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
// Take only the first 16 lines for GD-ROM
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
|
||||
|
||||
if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate))
|
||||
{
|
||||
@@ -664,7 +678,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
// Take only the last 16 lines for Sega CD
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Skip(16));
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Skip(16).ToArray());
|
||||
|
||||
if (GetSegaCDBuildInfo(info.Extras.Header, out var scdSerial, out var fixedDate))
|
||||
{
|
||||
@@ -682,7 +696,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
// Take only the first 16 lines for GD-ROM
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
|
||||
|
||||
if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate))
|
||||
{
|
||||
@@ -702,7 +716,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
// Take only the first 16 lines for GD-ROM
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
|
||||
|
||||
if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate))
|
||||
{
|
||||
@@ -722,7 +736,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
// Take only the first 16 lines for GD-ROM
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
|
||||
|
||||
if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate))
|
||||
{
|
||||
@@ -742,7 +756,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
// Take only the first 16 lines for GD-ROM
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
|
||||
|
||||
if (GetGDROMBuildInfo(info.Extras.Header, out var gdSerial, out var gdVersion, out var gdDate))
|
||||
{
|
||||
@@ -760,7 +774,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
// Take only the first 16 lines for Saturn
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
|
||||
|
||||
if (GetSaturnBuildInfo(info.Extras.Header, out var saturnSerial, out var saturnVersion, out var buildDate))
|
||||
{
|
||||
@@ -773,12 +787,14 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name), true);
|
||||
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= playstationDate;
|
||||
}
|
||||
|
||||
bool? psEdcStatus = null;
|
||||
@@ -792,12 +808,14 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetPlayStationEXEDate($"{basePath}_volDesc.txt", InfoTool.GetPlayStationExecutableName(drive?.Name));
|
||||
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= playstationTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
@@ -825,7 +843,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
info.Artifacts ??= new Dictionary<string, string>();
|
||||
info.Artifacts ??= [];
|
||||
|
||||
//if (File.Exists($"{basePath}.c2"))
|
||||
// info.Artifacts["c2"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.c2")) ?? string.Empty;
|
||||
@@ -887,7 +905,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
BaseCommand ??= CommandStrings.NONE;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(BaseCommand))
|
||||
if (!string.IsNullOrEmpty(BaseCommand))
|
||||
parameters.Add(BaseCommand);
|
||||
else
|
||||
return null;
|
||||
@@ -1016,7 +1034,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// BE Opcode
|
||||
if (IsFlagSupported(FlagStrings.BEOpcode))
|
||||
{
|
||||
if (this[FlagStrings.BEOpcode] == true && this[FlagStrings.D8Opcode] == false)
|
||||
if (this[FlagStrings.BEOpcode] == true && this[FlagStrings.D8Opcode] != true)
|
||||
{
|
||||
parameters.Add(FlagStrings.BEOpcode);
|
||||
if (BEOpcodeValue != null
|
||||
@@ -1360,7 +1378,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
parameters.Add(FlagStrings.VideoNowXP);
|
||||
}
|
||||
|
||||
return string.Join(" ", parameters);
|
||||
return string.Join(" ", [.. parameters]);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -1368,8 +1386,8 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
{
|
||||
return new Dictionary<string, List<string>>()
|
||||
{
|
||||
[CommandStrings.Audio] = new List<string>()
|
||||
{
|
||||
[CommandStrings.Audio] =
|
||||
[
|
||||
FlagStrings.BEOpcode,
|
||||
FlagStrings.C2Opcode,
|
||||
FlagStrings.D8Opcode,
|
||||
@@ -1385,23 +1403,21 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
FlagStrings.ScanSectorProtect,
|
||||
FlagStrings.SkipSector,
|
||||
FlagStrings.SubchannelReadLevel,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.BluRay] = new List<string>()
|
||||
{
|
||||
[CommandStrings.BluRay] =
|
||||
[
|
||||
FlagStrings.DatExpand,
|
||||
FlagStrings.DisableBeep,
|
||||
FlagStrings.DVDReread,
|
||||
FlagStrings.ForceUnitAccess,
|
||||
FlagStrings.UseAnchorVolumeDescriptorPointer,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.Close] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Close] = [],
|
||||
|
||||
[CommandStrings.CompactDisc] = new List<string>()
|
||||
{
|
||||
[CommandStrings.CompactDisc] =
|
||||
[
|
||||
FlagStrings.AddOffset,
|
||||
FlagStrings.AMSF,
|
||||
FlagStrings.AtariJaguar,
|
||||
@@ -1426,10 +1442,10 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
FlagStrings.VideoNow,
|
||||
FlagStrings.VideoNowColor,
|
||||
FlagStrings.VideoNowXP,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.Data] = new List<string>()
|
||||
{
|
||||
[CommandStrings.Data] =
|
||||
[
|
||||
FlagStrings.BEOpcode,
|
||||
FlagStrings.C2Opcode,
|
||||
FlagStrings.D8Opcode,
|
||||
@@ -1445,10 +1461,10 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
FlagStrings.ScanSectorProtect,
|
||||
FlagStrings.SkipSector,
|
||||
FlagStrings.SubchannelReadLevel,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.DigitalVideoDisc] = new List<string>()
|
||||
{
|
||||
[CommandStrings.DigitalVideoDisc] =
|
||||
[
|
||||
FlagStrings.CopyrightManagementInformation,
|
||||
FlagStrings.DatExpand,
|
||||
FlagStrings.DisableBeep,
|
||||
@@ -1463,28 +1479,24 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
FlagStrings.ScanFileProtect,
|
||||
FlagStrings.SkipSector,
|
||||
FlagStrings.UseAnchorVolumeDescriptorPointer,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.Disk] = new List<string>()
|
||||
{
|
||||
[CommandStrings.Disk] =
|
||||
[
|
||||
FlagStrings.DatExpand,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.DriveSpeed] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.DriveSpeed] = [],
|
||||
|
||||
[CommandStrings.Eject] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Eject] = [],
|
||||
|
||||
[CommandStrings.Floppy] = new List<string>()
|
||||
{
|
||||
[CommandStrings.Floppy] =
|
||||
[
|
||||
FlagStrings.DatExpand,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.GDROM] = new List<string>()
|
||||
{
|
||||
[CommandStrings.GDROM] =
|
||||
[
|
||||
FlagStrings.BEOpcode,
|
||||
FlagStrings.C2Opcode,
|
||||
FlagStrings.D8Opcode,
|
||||
@@ -1495,40 +1507,28 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
FlagStrings.NoFixSubQ,
|
||||
FlagStrings.NoFixSubRtoW,
|
||||
FlagStrings.SubchannelReadLevel,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.MDS] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.MDS] = [],
|
||||
|
||||
[CommandStrings.Merge] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Merge] = [],
|
||||
|
||||
[CommandStrings.Reset] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Reset] = [],
|
||||
|
||||
[CommandStrings.SACD] = new List<string>()
|
||||
{
|
||||
[CommandStrings.SACD] =
|
||||
[
|
||||
FlagStrings.DatExpand,
|
||||
FlagStrings.DisableBeep,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.Start] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Start] = [],
|
||||
|
||||
[CommandStrings.Stop] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Stop] = [],
|
||||
|
||||
[CommandStrings.Sub] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Sub] = [],
|
||||
|
||||
[CommandStrings.Swap] = new List<string>()
|
||||
{
|
||||
[CommandStrings.Swap] =
|
||||
[
|
||||
FlagStrings.AddOffset,
|
||||
FlagStrings.BEOpcode,
|
||||
FlagStrings.C2Opcode,
|
||||
@@ -1549,48 +1549,44 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
FlagStrings.VideoNow,
|
||||
FlagStrings.VideoNowColor,
|
||||
FlagStrings.VideoNowXP,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.Tape] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Tape] = [],
|
||||
|
||||
[CommandStrings.Version] = new List<string>()
|
||||
{
|
||||
},
|
||||
[CommandStrings.Version] = [],
|
||||
|
||||
[CommandStrings.XBOX] = new List<string>()
|
||||
{
|
||||
[CommandStrings.XBOX] =
|
||||
[
|
||||
FlagStrings.DatExpand,
|
||||
FlagStrings.DisableBeep,
|
||||
FlagStrings.DVDReread,
|
||||
FlagStrings.ForceUnitAccess,
|
||||
FlagStrings.NoSkipSS,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.XBOXSwap] = new List<string>()
|
||||
{
|
||||
[CommandStrings.XBOXSwap] =
|
||||
[
|
||||
FlagStrings.DatExpand,
|
||||
FlagStrings.DisableBeep,
|
||||
FlagStrings.ForceUnitAccess,
|
||||
FlagStrings.NoSkipSS,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.XGD2Swap] = new List<string>()
|
||||
{
|
||||
[CommandStrings.XGD2Swap] =
|
||||
[
|
||||
FlagStrings.DatExpand,
|
||||
FlagStrings.DisableBeep,
|
||||
FlagStrings.ForceUnitAccess,
|
||||
FlagStrings.NoSkipSS,
|
||||
},
|
||||
],
|
||||
|
||||
[CommandStrings.XGD3Swap] = new List<string>()
|
||||
{
|
||||
[CommandStrings.XGD3Swap] =
|
||||
[
|
||||
FlagStrings.DatExpand,
|
||||
FlagStrings.DisableBeep,
|
||||
FlagStrings.ForceUnitAccess,
|
||||
FlagStrings.NoSkipSS,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1813,7 +1809,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
StartLBAValue = null;
|
||||
EndLBAValue = null;
|
||||
|
||||
flags = new Dictionary<string, bool?>();
|
||||
flags = [];
|
||||
|
||||
AddOffsetValue = null;
|
||||
BEOpcodeValue = null;
|
||||
@@ -1935,12 +1931,12 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
// The string has to be valid by itself first
|
||||
if (string.IsNullOrWhiteSpace(parameters))
|
||||
if (string.IsNullOrEmpty(parameters))
|
||||
return false;
|
||||
|
||||
// Now split the string into parts for easier validation
|
||||
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
|
||||
parameters = parameters.Trim();
|
||||
parameters = parameters!.Trim();
|
||||
List<string> parts = Regex.Matches(parameters, @"[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Value)
|
||||
@@ -2578,7 +2574,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
private static (string?, string?) GetCommandFilePathAndVersion(string basePath)
|
||||
{
|
||||
// If we have an invalid base path, we can do nothing
|
||||
if (string.IsNullOrWhiteSpace(basePath))
|
||||
if (string.IsNullOrEmpty(basePath))
|
||||
return (null, null);
|
||||
|
||||
// Generate the matching regex based on the base path
|
||||
@@ -2587,12 +2583,12 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
// Find the first match for the command file
|
||||
var parentDirectory = Path.GetDirectoryName(basePath);
|
||||
if (string.IsNullOrWhiteSpace(parentDirectory))
|
||||
if (string.IsNullOrEmpty(parentDirectory))
|
||||
return (null, null);
|
||||
|
||||
var currentFiles = Directory.GetFiles(parentDirectory);
|
||||
var commandPath = currentFiles.FirstOrDefault(f => cmdFilenameRegex.IsMatch(f));
|
||||
if (string.IsNullOrWhiteSpace(commandPath))
|
||||
if (string.IsNullOrEmpty(commandPath))
|
||||
return (null, null);
|
||||
|
||||
// Extract the version string
|
||||
@@ -2697,25 +2693,25 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (line.StartsWith("DiscType:"))
|
||||
{
|
||||
// DiscType: <discType>
|
||||
string identifier = line["DiscType: ".Length..];
|
||||
string identifier = line.Substring("DiscType: ".Length);
|
||||
discTypeOrBookTypeSet.Add(identifier);
|
||||
}
|
||||
else if (line.StartsWith("DiscTypeIdentifier:"))
|
||||
{
|
||||
// DiscTypeIdentifier: <discType>
|
||||
string identifier = line["DiscTypeIdentifier: ".Length..];
|
||||
string identifier = line.Substring("DiscTypeIdentifier: ".Length);
|
||||
discTypeOrBookTypeSet.Add(identifier);
|
||||
}
|
||||
else if (line.StartsWith("DiscTypeSpecific:"))
|
||||
{
|
||||
// DiscTypeSpecific: <discType>
|
||||
string identifier = line["DiscTypeSpecific: ".Length..];
|
||||
string identifier = line.Substring("DiscTypeSpecific: ".Length);
|
||||
discTypeOrBookTypeSet.Add(identifier);
|
||||
}
|
||||
else if (line.StartsWith("BookType:"))
|
||||
{
|
||||
// BookType: <discType>
|
||||
string identifier = line["BookType: ".Length..];
|
||||
string identifier = line.Substring("BookType: ".Length);
|
||||
discTypeOrBookTypeSet.Add(identifier);
|
||||
}
|
||||
|
||||
@@ -2724,7 +2720,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
// Create the output string
|
||||
if (discTypeOrBookTypeSet.Any())
|
||||
discTypeOrBookType = string.Join(", ", discTypeOrBookTypeSet.OrderBy(s => s));
|
||||
discTypeOrBookType = string.Join(", ", [.. discTypeOrBookTypeSet.OrderBy(s => s)]);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -2736,13 +2732,97 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all Volume Identifiers
|
||||
/// </summary>
|
||||
/// <param name="volDesc">_volDesc.txt file location</param>
|
||||
/// <returns>Volume labels (by type), or null if none present</returns>
|
||||
private static bool GetVolumeLabels(string volDesc, out Dictionary<string, List<string>> volLabels)
|
||||
{
|
||||
// If the file doesn't exist, can't get the volume labels
|
||||
volLabels = [];
|
||||
if (!File.Exists(volDesc))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using var sr = File.OpenText(volDesc);
|
||||
var line = sr.ReadLine();
|
||||
|
||||
string volType = "UNKNOWN";
|
||||
string label;
|
||||
while (line != null)
|
||||
{
|
||||
// Trim the line for later use
|
||||
line = line.Trim();
|
||||
|
||||
// ISO9660 and extensions section
|
||||
if (line.StartsWith("Volume Descriptor Type: "))
|
||||
{
|
||||
Int32.TryParse(line.Substring("Volume Descriptor Type: ".Length), out int volTypeInt);
|
||||
volType = volTypeInt switch
|
||||
{
|
||||
// 0 => "Boot Record" // Should not not contain a Volume Identifier
|
||||
1 => "ISO", // ISO9660
|
||||
2 => "Joliet",
|
||||
// 3 => "Volume Partition Descriptor" // Should not not contain a Volume Identifier
|
||||
// 255 => "???" // Should not not contain a Volume Identifier
|
||||
_ => "UNKNOWN" // Should not contain a Volume Identifier
|
||||
};
|
||||
}
|
||||
// UDF section
|
||||
else if (line.StartsWith("Primary Volume Descriptor Number:"))
|
||||
{
|
||||
volType = "UDF";
|
||||
}
|
||||
// Identifier
|
||||
else if (line.StartsWith("Volume Identifier: "))
|
||||
{
|
||||
label = line.Substring("Volume Identifier: ".Length);
|
||||
|
||||
// Remove leading non-printable character (unsure why DIC outputs this)
|
||||
if (Convert.ToUInt32(label[0]) == 0x7F || Convert.ToUInt32(label[0]) < 0x20)
|
||||
label = label.Substring(1);
|
||||
|
||||
// Skip if label is blank
|
||||
if (label == null || label.Length <= 0)
|
||||
{
|
||||
volType = "UNKNOWN";
|
||||
line = sr.ReadLine();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (volLabels.ContainsKey(label))
|
||||
volLabels[label].Add(volType);
|
||||
else
|
||||
volLabels.Add(label, [volType]);
|
||||
|
||||
// Reset volume type
|
||||
volType = "UNKNOWN";
|
||||
}
|
||||
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
// Return true if a volume label was found
|
||||
return volLabels.Count > 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
volLabels = [];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the DVD protection information, if possible
|
||||
/// </summary>
|
||||
/// <param name="cssKey">_CSSKey.txt file location</param>
|
||||
/// <param name="disc">_disc.txt file location</param>
|
||||
/// <param name="includeAlways">Indicates whether region and protection type are always included</param>
|
||||
/// <returns>Formatted string representing the DVD protection, null on error</returns>
|
||||
private static string? GetDVDProtection(string cssKey, string disc)
|
||||
private static string? GetDVDProtection(string cssKey, string disc, bool includeAlways)
|
||||
{
|
||||
// If one of the files doesn't exist, we can't get info from them
|
||||
if (!File.Exists(disc))
|
||||
@@ -2767,9 +2847,9 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
break;
|
||||
|
||||
if (line.StartsWith("CopyrightProtectionType"))
|
||||
copyrightProtectionSystemType = line["CopyrightProtectionType: ".Length..];
|
||||
copyrightProtectionSystemType = line.Substring("CopyrightProtectionType: ".Length);
|
||||
else if (line.StartsWith("RegionManagementInformation"))
|
||||
region = line["RegionManagementInformation: ".Length..];
|
||||
region = line.Substring("RegionManagementInformation: ".Length);
|
||||
|
||||
line = sr.ReadLine()?.Trim();
|
||||
}
|
||||
@@ -2792,7 +2872,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
|
||||
if (line.StartsWith("DecryptedDiscKey"))
|
||||
{
|
||||
decryptedDiscKey = line["DecryptedDiscKey[020]: ".Length..];
|
||||
decryptedDiscKey = line.Substring("DecryptedDiscKey[020]: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("LBA:"))
|
||||
{
|
||||
@@ -2805,7 +2885,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), No TitleKey$", RegexOptions.Compiled);
|
||||
string matchedFilename = match.Groups[1].Value;
|
||||
if (matchedFilename.EndsWith(";1"))
|
||||
matchedFilename = matchedFilename[..^2];
|
||||
matchedFilename = matchedFilename.Substring(0, matchedFilename.Length - 2);
|
||||
|
||||
vobKeys += $"{matchedFilename} Title Key: No Title Key\n";
|
||||
}
|
||||
@@ -2814,7 +2894,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
var match = Regex.Match(line, @"^LBA:\s*[0-9]+, Filename: (.*?), EncryptedTitleKey: .*?, DecryptedTitleKey: (.*?)$", RegexOptions.Compiled);
|
||||
string matchedFilename = match.Groups[1].Value;
|
||||
if (matchedFilename.EndsWith(";1"))
|
||||
matchedFilename = matchedFilename[..^2];
|
||||
matchedFilename = matchedFilename.Substring(0, matchedFilename.Length - 2);
|
||||
|
||||
vobKeys += $"{matchedFilename} Title Key: {match.Groups[2].Value}\n";
|
||||
}
|
||||
@@ -2824,6 +2904,15 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Filter out if we're not always including information
|
||||
if (!includeAlways)
|
||||
{
|
||||
if (region == "1 2 3 4 5 6 7 8")
|
||||
region = null;
|
||||
if (copyrightProtectionSystemType == "No")
|
||||
copyrightProtectionSystemType = null;
|
||||
}
|
||||
|
||||
// Now we format everything we can
|
||||
string protection = string.Empty;
|
||||
if (!string.IsNullOrEmpty(region))
|
||||
@@ -2876,14 +2965,14 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
{
|
||||
totalErrors ??= 0;
|
||||
|
||||
if (Int64.TryParse(line["Total errors: ".Length..].Trim(), out long te))
|
||||
if (Int64.TryParse(line.Substring("Total errors: ".Length).Trim(), out long te))
|
||||
totalErrors += te;
|
||||
}
|
||||
else if (line.StartsWith("Total warnings"))
|
||||
{
|
||||
totalErrors ??= 0;
|
||||
|
||||
if (Int64.TryParse(line["Total warnings: ".Length..].Trim(), out long tw))
|
||||
if (Int64.TryParse(line.Substring("Total warnings: ".Length).Trim(), out long tw))
|
||||
totalErrors += tw;
|
||||
}
|
||||
}
|
||||
@@ -2898,6 +2987,72 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the PSX/PS2/KP2 EXE Date from the log, if possible
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <param name="serial">Internal serial</param>
|
||||
/// <param name="psx">True if PSX disc, false otherwise</param>
|
||||
/// <returns>EXE date if possible, null otherwise</returns>
|
||||
public static string? GetPlayStationEXEDate(string log, string? exeName, bool psx = false)
|
||||
{
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(log))
|
||||
return null;
|
||||
|
||||
// If the EXE name is not valid, we can't get the info
|
||||
if (string.IsNullOrEmpty(exeName))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
string? exeDate = null;
|
||||
using var sr = File.OpenText(log);
|
||||
var line = sr.ReadLine();
|
||||
while (line != null)
|
||||
{
|
||||
// Trim the line for later use
|
||||
line = line.Trim();
|
||||
|
||||
// The exe date is listed in a single line, File Identifier: ABCD_123.45;1
|
||||
if (line.Length >= "File Identifier: ".Length + 11 &&
|
||||
line.StartsWith("File Identifier:") &&
|
||||
line.Substring("File Identifier: ".Length) == exeName)
|
||||
{
|
||||
// Account for Y2K date problem
|
||||
if (exeDate != null && exeDate!.Substring(0, 2) == "19")
|
||||
{
|
||||
string decade = exeDate!.Substring(2, 1);
|
||||
// Does only PSX need to account for 1920s-60s?
|
||||
if (decade == "0" || decade == "1" ||
|
||||
psx && (decade == "2" || decade == "3" || decade == "4" || decade == "5" || decade == "6"))
|
||||
exeDate = $"20{exeDate!.Substring(2)}";
|
||||
}
|
||||
|
||||
// Currently stored date is the EXE date, return it
|
||||
return exeDate;
|
||||
}
|
||||
|
||||
// The exe datetime is listed in a single line
|
||||
if (line.Length >= "Recording Date and Time: ".Length + 10 &&
|
||||
line.StartsWith("Recording Date and Time:"))
|
||||
{
|
||||
// exe date: ISO datetime (yyyy-MM-ddT.....)
|
||||
exeDate = line.Substring("Recording Date and Time: ".Length, 10);
|
||||
}
|
||||
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the build info from a GD-ROM LD area, if possible
|
||||
/// </summary>
|
||||
@@ -2908,18 +3063,18 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
serial = null; version = null; date = null;
|
||||
|
||||
// If the input header is null, we can't do a thing
|
||||
if (string.IsNullOrWhiteSpace(segaHeader))
|
||||
if (string.IsNullOrEmpty(segaHeader))
|
||||
return false;
|
||||
|
||||
// Now read it in cutting it into lines for easier parsing
|
||||
try
|
||||
{
|
||||
string[] header = segaHeader.Split('\n');
|
||||
string versionLine = header[4][58..];
|
||||
string dateLine = header[5][58..];
|
||||
serial = versionLine[..10].TrimEnd();
|
||||
string[] header = segaHeader!.Split('\n');
|
||||
string versionLine = header[4].Substring(58);
|
||||
string dateLine = header[5].Substring(58);
|
||||
serial = versionLine.Substring(0, 10).TrimEnd();
|
||||
version = versionLine.Substring(10, 6).TrimStart('V', 'v');
|
||||
date = dateLine[..8];
|
||||
date = dateLine.Substring(0, 8);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
@@ -2956,17 +3111,17 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (string.IsNullOrEmpty(manufacturer) && line.StartsWith("VendorId"))
|
||||
{
|
||||
// VendorId: <manufacturer>
|
||||
manufacturer = line["VendorId: ".Length..];
|
||||
manufacturer = line.Substring("VendorId: ".Length);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(model) && line.StartsWith("ProductId"))
|
||||
{
|
||||
// ProductId: <model>
|
||||
model = line["ProductId: ".Length..];
|
||||
model = line.Substring("ProductId: ".Length);
|
||||
}
|
||||
else if (string.IsNullOrEmpty(firmware) && line.StartsWith("ProductRevisionLevel"))
|
||||
{
|
||||
// ProductRevisionLevel: <firmware>
|
||||
firmware = line["ProductRevisionLevel: ".Length..];
|
||||
firmware = line.Substring("ProductRevisionLevel: ".Length);
|
||||
}
|
||||
|
||||
line = sr.ReadLine();
|
||||
@@ -3117,7 +3272,7 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// TODO: Are there any examples of 3+ session discs?
|
||||
|
||||
// Read the first session lead-out
|
||||
var firstSessionLeadOutLengthString = line?["Lead-out length of 1st session: ".Length..];
|
||||
var firstSessionLeadOutLengthString = line?.Substring("Lead-out length of 1st session: ".Length);
|
||||
line = sr.ReadLine()?.Trim();
|
||||
if (line == null)
|
||||
return null;
|
||||
@@ -3126,12 +3281,12 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
string? secondSessionLeadInLengthString = null;
|
||||
while (line?.StartsWith("Lead-in length") == false)
|
||||
{
|
||||
secondSessionLeadInLengthString = line?["Lead-in length of 2nd session: ".Length..];
|
||||
secondSessionLeadInLengthString = line?.Substring("Lead-in length of 2nd session: ".Length);
|
||||
line = sr.ReadLine()?.Trim();
|
||||
}
|
||||
|
||||
// Read the second session pregap
|
||||
var secondSessionPregapLengthString = line?["Pregap length of 1st track of 2nd session: ".Length..];
|
||||
var secondSessionPregapLengthString = line?.Substring("Pregap length of 1st track of 2nd session: ".Length);
|
||||
|
||||
// Calculate the session gap total
|
||||
if (!int.TryParse(firstSessionLeadOutLengthString, out int firstSessionLeadOutLength))
|
||||
@@ -3348,18 +3503,18 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
serial = null; version = null; date = null;
|
||||
|
||||
// If the input header is null, we can't do a thing
|
||||
if (string.IsNullOrWhiteSpace(segaHeader))
|
||||
if (string.IsNullOrEmpty(segaHeader))
|
||||
return false;
|
||||
|
||||
// Now read it in cutting it into lines for easier parsing
|
||||
try
|
||||
{
|
||||
string[] header = segaHeader.Split('\n');
|
||||
string serialVersionLine = header[2][58..];
|
||||
string dateLine = header[3][58..];
|
||||
serial = serialVersionLine[..10].Trim();
|
||||
string[] header = segaHeader!.Split('\n');
|
||||
string serialVersionLine = header[2].Substring(58);
|
||||
string dateLine = header[3].Substring(58);
|
||||
serial = serialVersionLine.Substring(0, 10).Trim();
|
||||
version = serialVersionLine.Substring(10, 6).TrimStart('V', 'v');
|
||||
date = dateLine[..8];
|
||||
date = dateLine.Substring(0, 8);
|
||||
date = $"{date[0]}{date[1]}{date[2]}{date[3]}-{date[4]}{date[5]}-{date[6]}{date[7]}";
|
||||
return true;
|
||||
}
|
||||
@@ -3381,23 +3536,23 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
serial = null; date = null;
|
||||
|
||||
// If the input header is null, we can't do a thing
|
||||
if (string.IsNullOrWhiteSpace(segaHeader))
|
||||
if (string.IsNullOrEmpty(segaHeader))
|
||||
return false;
|
||||
|
||||
// Now read it in cutting it into lines for easier parsing
|
||||
try
|
||||
{
|
||||
string[] header = segaHeader.Split('\n');
|
||||
string serialVersionLine = header[8][58..];
|
||||
string dateLine = header[1][58..];
|
||||
string[] header = segaHeader!.Split('\n');
|
||||
string serialVersionLine = header[8].Substring(58);
|
||||
string dateLine = header[1].Substring(58);
|
||||
serial = serialVersionLine.Substring(3, 8).TrimEnd('-', ' ');
|
||||
date = dateLine[8..].Trim();
|
||||
date = dateLine.Substring(8).Trim();
|
||||
|
||||
// Properly format the date string, if possible
|
||||
string[] dateSplit = date.Split('.');
|
||||
|
||||
if (dateSplit.Length == 1)
|
||||
dateSplit = new string[] { date[..4], date[4..] };
|
||||
dateSplit = [date.Substring(0, 4), date.Substring(4)];
|
||||
|
||||
string month = dateSplit[1];
|
||||
dateSplit[1] = month switch
|
||||
@@ -3575,6 +3730,9 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// This flag is needed because recent versions of DIC include security data twice
|
||||
bool foundSecuritySectors = false;
|
||||
|
||||
// SS version for all Kreon DIC dumps is v1
|
||||
ssver = "01";
|
||||
|
||||
try
|
||||
{
|
||||
using var sr = File.OpenText(disc);
|
||||
@@ -3584,11 +3742,13 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
// Security Sector version
|
||||
// XGD version (1 = Xbox, 2 = Xbox360)
|
||||
/*
|
||||
if (line.StartsWith("Version of challenge table"))
|
||||
{
|
||||
ssver = line.Split(' ')[4]; // "Version of challenge table: <VER>"
|
||||
xgdver = line.Split(' ')[4]; // "Version of challenge table: <VER>"
|
||||
}
|
||||
*/
|
||||
|
||||
// Security Sector ranges
|
||||
else if (line.StartsWith("Number of security sector ranges:") && !foundSecuritySectors)
|
||||
@@ -3663,6 +3823,9 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
// This flag is needed because recent versions of DIC include security data twice
|
||||
bool foundSecuritySectors = false;
|
||||
|
||||
// SS version for all Kreon DIC dumps is v1
|
||||
ssver = "01";
|
||||
|
||||
try
|
||||
{
|
||||
using var sr = File.OpenText(disc);
|
||||
@@ -3672,11 +3835,13 @@ namespace MPF.Core.Modules.DiscImageCreator
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
// Security Sector version
|
||||
// XGD version (1 = Xbox, 2 = Xbox360)
|
||||
/*
|
||||
if (line.StartsWith("Version of challenge table"))
|
||||
{
|
||||
ssver = line.Split(' ')[4]; // "Version of challenge table: <VER>"
|
||||
xgdver = line.Split(' ')[4]; // "Version of challenge table: <VER>"
|
||||
}
|
||||
*/
|
||||
|
||||
// Security Sector ranges
|
||||
else if (line.StartsWith("Number of security sector ranges:") && !foundSecuritySectors)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -10,14 +10,19 @@ namespace MPF.Core.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.Core.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.Core.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.Core.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";
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,10 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.Models.CueSheets;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
#pragma warning disable IDE0051 // Remove unused private members
|
||||
|
||||
namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
/// <summary>
|
||||
@@ -147,6 +146,11 @@ namespace MPF.Core.Modules.Redumper
|
||||
/// </summary>
|
||||
public string? SkipValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Write offset for dumps when reading as data
|
||||
/// </summary>
|
||||
public int? DumpWriteOffsetValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of sectors to read at once on initial dump, DVD only (Default 32)
|
||||
/// </summary>
|
||||
@@ -192,7 +196,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (!File.Exists($"{basePath}.log"))
|
||||
missingFiles.Add($"{basePath}.log");
|
||||
else if (GetDatfile($"{basePath}.log") == null)
|
||||
missingFiles.Add($"{basePath}.dat");
|
||||
missingFiles.Add($"{basePath}.log (dat section)");
|
||||
if (!File.Exists($"{basePath}.state"))
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
if (!File.Exists($"{basePath}.subcode"))
|
||||
@@ -206,6 +210,14 @@ namespace MPF.Core.Modules.Redumper
|
||||
// // Depends on the disc
|
||||
// if (!File.Exists($"{basePath}.cdtext"))
|
||||
// missingFiles.Add($"{basePath}.cdtext");
|
||||
//
|
||||
// // Not available in all versions
|
||||
// if (!File.Exists($"{basePath}.hash"))
|
||||
// missingFiles.Add($"{basePath}.hash");
|
||||
// // Also: "{basePath} (Track X).hash" (get from cuesheet)
|
||||
// if (!File.Exists($"{basePath}.skeleton"))
|
||||
// missingFiles.Add($"{basePath}.skeleton");
|
||||
// // Also: "{basePath} (Track X).skeleton" (get from cuesheet)
|
||||
//}
|
||||
|
||||
break;
|
||||
@@ -225,6 +237,15 @@ namespace MPF.Core.Modules.Redumper
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
}
|
||||
|
||||
// Removed or inconsistent files
|
||||
//{
|
||||
// // Not available in all versions
|
||||
// if (!File.Exists($"{basePath}.hash"))
|
||||
// missingFiles.Add($"{basePath}.hash");
|
||||
// if (!File.Exists($"{basePath}.skeleton"))
|
||||
// missingFiles.Add($"{basePath}.skeleton");
|
||||
//}
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.HDDVD: // TODO: Verify that this is output
|
||||
@@ -241,6 +262,15 @@ namespace MPF.Core.Modules.Redumper
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
}
|
||||
|
||||
// Removed or inconsistent files
|
||||
//{
|
||||
// // Not available in all versions
|
||||
// if (!File.Exists($"{basePath}.hash"))
|
||||
// missingFiles.Add($"{basePath}.hash");
|
||||
// if (!File.Exists($"{basePath}.skeleton"))
|
||||
// missingFiles.Add($"{basePath}.skeleton");
|
||||
//}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -255,7 +285,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
info = Builder.EnsureAllSections(info);
|
||||
|
||||
// Get the dumping program and version
|
||||
info.DumpingInfo!.DumpingProgram = $"{EnumConverter.LongName(this.InternalProgram)} {GetVersion($"{basePath}.log") ?? "Unknown Version"}";
|
||||
@@ -273,6 +303,10 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (GetDiscType($"{basePath}.log", out var discTypeOrBookType))
|
||||
info.DumpingInfo.ReportedDiscType = discTypeOrBookType;
|
||||
|
||||
// Fill in the volume labels
|
||||
if (GetVolumeLabels($"{basePath}.log", out var volLabels))
|
||||
VolumeLabels = volLabels;
|
||||
|
||||
switch (this.Type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
@@ -291,7 +325,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// Attempt to get multisession data
|
||||
string cdMultiSessionInfo = GetMultisessionInformation($"{basePath}.log") ?? string.Empty;
|
||||
if (!string.IsNullOrWhiteSpace(cdMultiSessionInfo))
|
||||
if (!string.IsNullOrEmpty(cdMultiSessionInfo))
|
||||
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.Multisession] = cdMultiSessionInfo;
|
||||
|
||||
// Attempt to get the universal hash, if it's an audio disc
|
||||
@@ -342,11 +376,19 @@ namespace MPF.Core.Modules.Redumper
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
trimLength = 264;
|
||||
if (info.SizeAndChecksums!.Layerbreak3 != default)
|
||||
trimLength = 520;
|
||||
else if (info.SizeAndChecksums!.Layerbreak2 != default)
|
||||
trimLength = 392;
|
||||
else
|
||||
trimLength = 264;
|
||||
break;
|
||||
}
|
||||
|
||||
info.Extras!.PIC = GetPIC($"{basePath}.physical", trimLength) ?? string.Empty;
|
||||
|
||||
var di = InfoTool.GetDiscInformation($"{basePath}.physical");
|
||||
info.SizeAndChecksums!.PICIdentifier = InfoTool.GetPICIdentifier(di);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -360,20 +402,24 @@ namespace MPF.Core.Modules.Redumper
|
||||
case RedumpSystem.RainbowDisc:
|
||||
case RedumpSystem.SonyElectronicBook:
|
||||
info.CopyProtection!.SecuROMData = GetSecuROMData($"{basePath}.log") ?? string.Empty;
|
||||
|
||||
// Needed for some odd copy protections
|
||||
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log", false) ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.DVDAudio:
|
||||
case RedumpSystem.DVDVideo:
|
||||
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log") ?? string.Empty;
|
||||
info.CopyProtection!.Protection = GetDVDProtection($"{basePath}.log", true) ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiPython2:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var pythonTwoSerial, out Region? pythonTwoRegion, out var pythonTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = pythonTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? pythonTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = pythonTwoDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= pythonTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
@@ -419,7 +465,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// Take only the first 16 lines for Saturn
|
||||
if (!string.IsNullOrEmpty(info.Extras.Header))
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16));
|
||||
info.Extras.Header = string.Join("\n", info.Extras.Header.Split('\n').Take(16).ToArray());
|
||||
|
||||
if (GetSaturnBuildInfo(info.Extras.Header, out var saturnSerial, out var saturnVersion, out var buildDate))
|
||||
{
|
||||
@@ -432,12 +478,13 @@ namespace MPF.Core.Modules.Redumper
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationSerial, out Region? playstationRegion, out var playstationDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= playstationDate;
|
||||
}
|
||||
|
||||
info.CopyProtection!.AntiModchip = GetPlayStationAntiModchipDetected($"{basePath}.log").ToYesNo();
|
||||
@@ -447,12 +494,13 @@ namespace MPF.Core.Modules.Redumper
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = GetEXEDate($"{basePath}.log");
|
||||
if (InfoTool.GetPlayStationExecutableInfo(drive?.Name, out var playstationTwoSerial, out Region? playstationTwoRegion, out var playstationTwoDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = playstationTwoSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.Region = info.CommonDiscInfo.Region ?? playstationTwoRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = playstationTwoDate;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= playstationTwoDate;
|
||||
}
|
||||
|
||||
info.VersionAndEditions!.Version = InfoTool.GetPlayStation2Version(drive?.Name) ?? string.Empty;
|
||||
@@ -480,7 +528,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
info.Artifacts ??= new Dictionary<string, string>();
|
||||
info.Artifacts ??= [];
|
||||
|
||||
if (File.Exists($"{basePath}.cdtext"))
|
||||
info.Artifacts["cdtext"] = GetBase64(GetFullFile($"{basePath}.cdtext")) ?? string.Empty;
|
||||
@@ -488,6 +536,9 @@ namespace MPF.Core.Modules.Redumper
|
||||
info.Artifacts["cue"] = GetBase64(GetFullFile($"{basePath}.cue")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.fulltoc"))
|
||||
info.Artifacts["fulltoc"] = GetBase64(GetFullFile($"{basePath}.fulltoc")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
info.Artifacts["hash"] = GetBase64(GetFullFile($"{basePath}.hash")) ?? string.Empty;
|
||||
// TODO: "{basePath} (Track X).hash" (get from cuesheet)
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
info.Artifacts["log"] = GetBase64(GetFullFile($"{basePath}.log")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.manufacturer"))
|
||||
@@ -502,6 +553,9 @@ namespace MPF.Core.Modules.Redumper
|
||||
info.Artifacts["physical1"] = GetBase64(GetFullFile($"{basePath}.1.physical")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.2.physical"))
|
||||
info.Artifacts["physical2"] = GetBase64(GetFullFile($"{basePath}.2.physical")) ?? string.Empty;
|
||||
// if (File.Exists($"{basePath}.skeleton"))
|
||||
// info.Artifacts["skeleton"] = GetBase64(GetFullFile($"{basePath}.skeleton")) ?? string.Empty;
|
||||
// // Also: "{basePath} (Track X).skeleton" (get from cuesheet)
|
||||
// if (File.Exists($"{basePath}.scram"))
|
||||
// info.Artifacts["scram"] = GetBase64(GetFullFile($"{basePath}.scram")) ?? string.Empty;
|
||||
// if (File.Exists($"{basePath}.scrap"))
|
||||
@@ -525,7 +579,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
var parameters = new List<string>();
|
||||
|
||||
ModeValues ??= new List<string> { CommandStrings.NONE };
|
||||
ModeValues ??= [CommandStrings.NONE];
|
||||
|
||||
// Modes
|
||||
parameters.AddRange(ModeValues);
|
||||
@@ -536,6 +590,10 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (this[FlagStrings.HelpLong] == true)
|
||||
parameters.Add(FlagStrings.HelpLong);
|
||||
|
||||
// Version
|
||||
if (this[FlagStrings.Version] == true)
|
||||
parameters.Add(FlagStrings.Version);
|
||||
|
||||
// Verbose
|
||||
if (this[FlagStrings.Verbose] == true)
|
||||
parameters.Add(FlagStrings.Verbose);
|
||||
@@ -634,8 +692,8 @@ namespace MPF.Core.Modules.Redumper
|
||||
#region Drive Specific
|
||||
|
||||
// Plextor Leadin Skip
|
||||
if (this[FlagStrings.PlextorLeadinSkip] == true)
|
||||
parameters.Add(FlagStrings.PlextorLeadinSkip);
|
||||
if (this[FlagStrings.PlextorSkipLeadin] == true)
|
||||
parameters.Add(FlagStrings.PlextorSkipLeadin);
|
||||
|
||||
// Plextor Leadin Retries
|
||||
if (this[FlagStrings.PlextorLeadinRetries] == true)
|
||||
@@ -726,10 +784,17 @@ namespace MPF.Core.Modules.Redumper
|
||||
// Skip
|
||||
if (this[FlagStrings.Skip] == true)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(SkipValue))
|
||||
if (!string.IsNullOrEmpty(SkipValue))
|
||||
parameters.Add($"{FlagStrings.Skip}={SkipValue}");
|
||||
}
|
||||
|
||||
// Dump Write Offset
|
||||
if (this[FlagStrings.DumpWriteOffset] == true)
|
||||
{
|
||||
if (DumpWriteOffsetValue != null)
|
||||
parameters.Add($"{FlagStrings.DumpWriteOffset}={DumpWriteOffsetValue}");
|
||||
}
|
||||
|
||||
// Dump Read Size
|
||||
if (this[FlagStrings.DumpReadSize] == true)
|
||||
{
|
||||
@@ -741,9 +806,17 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (this[FlagStrings.OverreadLeadout] == true)
|
||||
parameters.Add(FlagStrings.OverreadLeadout);
|
||||
|
||||
// Legacy Subs
|
||||
if (this[FlagStrings.LegacySubs] == true)
|
||||
parameters.Add(FlagStrings.LegacySubs);
|
||||
|
||||
// Disable CD Text
|
||||
if (this[FlagStrings.DisableCDText] == true)
|
||||
parameters.Add(FlagStrings.DisableCDText);
|
||||
|
||||
#endregion
|
||||
|
||||
return string.Join(" ", parameters);
|
||||
return string.Join(" ", [.. parameters]);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -752,11 +825,12 @@ namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
return new Dictionary<string, List<string>>()
|
||||
{
|
||||
[CommandStrings.NONE] = new List<string>
|
||||
{
|
||||
[CommandStrings.NONE] =
|
||||
[
|
||||
// General
|
||||
FlagStrings.HelpLong,
|
||||
FlagStrings.HelpShort,
|
||||
FlagStrings.Version,
|
||||
FlagStrings.Verbose,
|
||||
FlagStrings.Debug,
|
||||
FlagStrings.Drive,
|
||||
@@ -775,7 +849,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
FlagStrings.DriveSectorOrder,
|
||||
|
||||
// Drive Specific
|
||||
FlagStrings.PlextorLeadinSkip,
|
||||
FlagStrings.PlextorSkipLeadin,
|
||||
FlagStrings.PlextorLeadinRetries,
|
||||
FlagStrings.AsusSkipLeadout,
|
||||
|
||||
@@ -797,9 +871,12 @@ namespace MPF.Core.Modules.Redumper
|
||||
FlagStrings.LBAEnd,
|
||||
FlagStrings.RefineSubchannel,
|
||||
FlagStrings.Skip,
|
||||
FlagStrings.DumpWriteOffset,
|
||||
FlagStrings.DumpReadSize,
|
||||
FlagStrings.OverreadLeadout,
|
||||
},
|
||||
FlagStrings.LegacySubs,
|
||||
FlagStrings.DisableCDText,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -839,23 +916,62 @@ namespace MPF.Core.Modules.Redumper
|
||||
logFiles.Add($"{basePath}.subcode");
|
||||
if (File.Exists($"{basePath}.toc"))
|
||||
logFiles.Add($"{basePath}.toc");
|
||||
|
||||
// Include .hash and .skeleton for all files in cuesheet
|
||||
var cueSheet = new SabreTools.Serialization.Files.CueSheet().Deserialize($"{basePath}.cue");
|
||||
string? baseDir = Path.GetDirectoryName(basePath);
|
||||
if (cueSheet?.Files != null && baseDir != null)
|
||||
{
|
||||
foreach (CueFile? file in cueSheet.Files)
|
||||
{
|
||||
string? trackName = Path.GetFileNameWithoutExtension(file?.FileName);
|
||||
if (trackName == null)
|
||||
continue;
|
||||
|
||||
string trackPath = Path.Combine(baseDir, trackName);
|
||||
if (File.Exists($"{trackPath}.hash"))
|
||||
logFiles.Add($"{trackPath}.hash");
|
||||
if (File.Exists($"{trackPath}.skeleton"))
|
||||
logFiles.Add($"{trackPath}.skeleton");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
logFiles.Add($"{basePath}.hash");
|
||||
if (File.Exists($"{basePath}.skeleton"))
|
||||
logFiles.Add($"{basePath}.skeleton");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.DVD:
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
logFiles.Add($"{basePath}.hash");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
if (File.Exists($"{basePath}.manufacturer"))
|
||||
logFiles.Add($"{basePath}.manufacturer");
|
||||
if (File.Exists($"{basePath}.1.manufacturer"))
|
||||
logFiles.Add($"{basePath}.1.manufacturer");
|
||||
if (File.Exists($"{basePath}.2.manufacturer"))
|
||||
logFiles.Add($"{basePath}.2.manufacturer");
|
||||
if (File.Exists($"{basePath}.physical"))
|
||||
logFiles.Add($"{basePath}.physical");
|
||||
if (File.Exists($"{basePath}.1.physical"))
|
||||
logFiles.Add($"{basePath}.1.physical");
|
||||
if (File.Exists($"{basePath}.2.physical"))
|
||||
logFiles.Add($"{basePath}.2.physical");
|
||||
if (File.Exists($"{basePath}.skeleton"))
|
||||
logFiles.Add($"{basePath}.skeleton");
|
||||
if (File.Exists($"{basePath}.state"))
|
||||
logFiles.Add($"{basePath}.state");
|
||||
break;
|
||||
|
||||
case MediaType.HDDVD: // TODO: Confirm that this information outputs
|
||||
case MediaType.BluRay:
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
logFiles.Add($"{basePath}.hash");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
if (File.Exists($"{basePath}.physical"))
|
||||
@@ -864,6 +980,8 @@ namespace MPF.Core.Modules.Redumper
|
||||
logFiles.Add($"{basePath}.1.physical");
|
||||
if (File.Exists($"{basePath}.2.physical"))
|
||||
logFiles.Add($"{basePath}.2.physical");
|
||||
if (File.Exists($"{basePath}.skeleton"))
|
||||
logFiles.Add($"{basePath}.skeleton");
|
||||
if (File.Exists($"{basePath}.state"))
|
||||
logFiles.Add($"{basePath}.state");
|
||||
break;
|
||||
@@ -888,7 +1006,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
flags = new Dictionary<string, bool?>();
|
||||
flags = [];
|
||||
|
||||
// General
|
||||
DriveValue = null;
|
||||
@@ -937,18 +1055,18 @@ namespace MPF.Core.Modules.Redumper
|
||||
case MediaType.CDROM:
|
||||
ModeValues = this.System switch
|
||||
{
|
||||
RedumpSystem.SuperAudioCD => new List<string> { CommandStrings.SACD },
|
||||
_ => new List<string> { CommandStrings.CD },
|
||||
RedumpSystem.SuperAudioCD => [CommandStrings.SACD],
|
||||
_ => [CommandStrings.CD],
|
||||
};
|
||||
break;
|
||||
case MediaType.DVD:
|
||||
ModeValues = new List<string> { CommandStrings.DVD };
|
||||
ModeValues = [CommandStrings.DVD];
|
||||
break;
|
||||
case MediaType.HDDVD: // TODO: Keep in sync if another command string shows up
|
||||
ModeValues = new List<string> { CommandStrings.DVD };
|
||||
ModeValues = [CommandStrings.DVD];
|
||||
break;
|
||||
case MediaType.BluRay:
|
||||
ModeValues = new List<string> { CommandStrings.BluRay };
|
||||
ModeValues = [CommandStrings.BluRay];
|
||||
break;
|
||||
default:
|
||||
BaseCommand = null;
|
||||
@@ -974,21 +1092,21 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (options.RedumperUseGenericDriveType)
|
||||
{
|
||||
this[FlagStrings.DriveType] = true;
|
||||
DriveTypeValue = "Generic";
|
||||
DriveTypeValue = "GENERIC";
|
||||
}
|
||||
|
||||
// Set the output paths
|
||||
if (!string.IsNullOrWhiteSpace(filename))
|
||||
if (!string.IsNullOrEmpty(filename))
|
||||
{
|
||||
var imagePath = Path.GetDirectoryName(filename);
|
||||
if (!string.IsNullOrWhiteSpace(imagePath))
|
||||
if (!string.IsNullOrEmpty(imagePath))
|
||||
{
|
||||
this[FlagStrings.ImagePath] = true;
|
||||
ImagePathValue = $"\"{imagePath}\"";
|
||||
}
|
||||
|
||||
string imageName = Path.GetFileNameWithoutExtension(filename);
|
||||
if (!string.IsNullOrWhiteSpace(imageName))
|
||||
if (!string.IsNullOrEmpty(imageName))
|
||||
{
|
||||
this[FlagStrings.ImageName] = true;
|
||||
ImageNameValue = $"\"{imageName}\"";
|
||||
@@ -1005,19 +1123,19 @@ namespace MPF.Core.Modules.Redumper
|
||||
BaseCommand = CommandStrings.NONE;
|
||||
|
||||
// The string has to be valid by itself first
|
||||
if (string.IsNullOrWhiteSpace(parameters))
|
||||
if (string.IsNullOrEmpty(parameters))
|
||||
return false;
|
||||
|
||||
// Now split the string into parts for easier validation
|
||||
// https://stackoverflow.com/questions/14655023/split-a-string-that-has-white-spaces-unless-they-are-enclosed-within-quotes
|
||||
parameters = parameters.Trim();
|
||||
parameters = parameters!.Trim();
|
||||
List<string> parts = Regex.Matches(parameters, @"([a-zA-Z\-]*=)?[\""].+?[\""]|[^ ]+", RegexOptions.Compiled)
|
||||
.Cast<Match>()
|
||||
.Select(m => m.Value)
|
||||
.ToList();
|
||||
|
||||
// Setup the modes
|
||||
ModeValues = new List<string>();
|
||||
ModeValues = [];
|
||||
|
||||
// All modes should be cached separately
|
||||
int index = 0;
|
||||
@@ -1033,14 +1151,19 @@ namespace MPF.Core.Modules.Redumper
|
||||
case CommandStrings.DVD:
|
||||
case CommandStrings.BluRay:
|
||||
case CommandStrings.SACD:
|
||||
case CommandStrings.Rings:
|
||||
case CommandStrings.Dump:
|
||||
case CommandStrings.Protection:
|
||||
case CommandStrings.DumpNew: // Temporary command, to be removed later
|
||||
case CommandStrings.Refine:
|
||||
case CommandStrings.Split:
|
||||
case CommandStrings.RefineNew: // Temporary command, to be removed later
|
||||
case CommandStrings.Verify:
|
||||
case CommandStrings.DVDKey:
|
||||
case CommandStrings.DVDIsoKey:
|
||||
case CommandStrings.Protection:
|
||||
case CommandStrings.Split:
|
||||
case CommandStrings.Hash:
|
||||
case CommandStrings.Info:
|
||||
case CommandStrings.Skeleton:
|
||||
ModeValues.Add(part);
|
||||
break;
|
||||
|
||||
@@ -1075,6 +1198,9 @@ namespace MPF.Core.Modules.Redumper
|
||||
// Help
|
||||
ProcessFlagParameter(parts, FlagStrings.HelpShort, FlagStrings.HelpLong, ref i);
|
||||
|
||||
// Version
|
||||
ProcessFlagParameter(parts, FlagStrings.Version, ref i);
|
||||
|
||||
// Verbose
|
||||
ProcessFlagParameter(parts, FlagStrings.Verbose, ref i);
|
||||
|
||||
@@ -1083,7 +1209,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// Drive
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.Drive, ref i);
|
||||
if (!string.IsNullOrWhiteSpace(stringValue))
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
DriveValue = stringValue;
|
||||
|
||||
// Speed
|
||||
@@ -1098,13 +1224,13 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// Image Path
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.ImagePath, ref i);
|
||||
if (!string.IsNullOrWhiteSpace(stringValue))
|
||||
ImagePathValue = $"\"{stringValue.Trim('"')}\"";
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
ImagePathValue = $"\"{stringValue!.Trim('"')}\"";
|
||||
|
||||
// Image Name
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.ImageName, ref i);
|
||||
if (!string.IsNullOrWhiteSpace(stringValue))
|
||||
ImageNameValue = $"\"{stringValue.Trim('"')}\"";
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
ImageNameValue = $"\"{stringValue!.Trim('"')}\"";
|
||||
|
||||
// Overwrite
|
||||
ProcessFlagParameter(parts, FlagStrings.Overwrite, ref i);
|
||||
@@ -1115,7 +1241,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// Drive Type
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.DriveType, ref i);
|
||||
if (!string.IsNullOrWhiteSpace(stringValue))
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
DriveTypeValue = stringValue;
|
||||
|
||||
// Drive Read Offset
|
||||
@@ -1135,20 +1261,20 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// Drive Read Method
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.DriveReadMethod, ref i);
|
||||
if (!string.IsNullOrWhiteSpace(stringValue))
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
DriveReadMethodValue = stringValue;
|
||||
|
||||
// Drive Sector Order
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.DriveSectorOrder, ref i);
|
||||
if (!string.IsNullOrWhiteSpace(stringValue))
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
DriveSectorOrderValue = stringValue;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Drive Specific
|
||||
|
||||
// Plextor Leadin Skip
|
||||
ProcessFlagParameter(parts, FlagStrings.PlextorLeadinSkip, ref i);
|
||||
// Plextor Skip Leadin
|
||||
ProcessFlagParameter(parts, FlagStrings.PlextorSkipLeadin, ref i);
|
||||
|
||||
// Plextor Leadin Retries
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.PlextorLeadinRetries, ref i);
|
||||
@@ -1218,22 +1344,33 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// Skip
|
||||
stringValue = ProcessStringParameter(parts, FlagStrings.Skip, ref i);
|
||||
if (!string.IsNullOrWhiteSpace(stringValue))
|
||||
if (!string.IsNullOrEmpty(stringValue))
|
||||
SkipValue = stringValue;
|
||||
|
||||
// Skip
|
||||
// Dump Write Offset
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.DumpWriteOffset, ref i);
|
||||
if (intValue != null && intValue != Int32.MinValue)
|
||||
DumpWriteOffsetValue = intValue;
|
||||
|
||||
// Dump Read Size
|
||||
intValue = ProcessInt32Parameter(parts, FlagStrings.DumpReadSize, ref i);
|
||||
if (!string.IsNullOrWhiteSpace(stringValue))
|
||||
if (intValue != null && intValue != Int32.MinValue)
|
||||
DumpReadSizeValue = intValue;
|
||||
|
||||
// Overread Leadout
|
||||
ProcessFlagParameter(parts, FlagStrings.OverreadLeadout, ref i);
|
||||
|
||||
// Legacy Subs
|
||||
ProcessFlagParameter(parts, FlagStrings.LegacySubs, ref i);
|
||||
|
||||
// Disable CD Text
|
||||
ProcessFlagParameter(parts, FlagStrings.DisableCDText, ref i);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
// If the image name was not set, set it with a default value
|
||||
if (string.IsNullOrWhiteSpace(this.ImageNameValue))
|
||||
if (string.IsNullOrEmpty(this.ImageNameValue))
|
||||
this.ImageNameValue = "track";
|
||||
|
||||
return true;
|
||||
@@ -1264,7 +1401,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// Now that we're at the relevant entries, read each line in and concatenate
|
||||
string? cueString = string.Empty, line = sr.ReadLine()?.Trim();
|
||||
while (!string.IsNullOrWhiteSpace(line))
|
||||
while (!string.IsNullOrEmpty(line))
|
||||
{
|
||||
cueString += line + "\n";
|
||||
line = sr.ReadLine()?.Trim();
|
||||
@@ -1322,20 +1459,20 @@ namespace MPF.Core.Modules.Redumper
|
||||
/// <summary>
|
||||
/// Get reported disc type information, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">_disc.txt file location</param>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>True if disc type info was set, false otherwise</returns>
|
||||
private static bool GetDiscType(string drive, out string? discTypeOrBookType)
|
||||
private static bool GetDiscType(string log, out string? discTypeOrBookType)
|
||||
{
|
||||
// Set the default values
|
||||
discTypeOrBookType = null;
|
||||
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(drive))
|
||||
if (!File.Exists(log))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using var sr = File.OpenText(drive);
|
||||
using var sr = File.OpenText(log);
|
||||
var line = sr.ReadLine();
|
||||
while (line != null)
|
||||
{
|
||||
@@ -1346,7 +1483,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
if (line.StartsWith("current profile:"))
|
||||
{
|
||||
// current profile: <discType>
|
||||
discTypeOrBookType = line["current profile: ".Length..];
|
||||
discTypeOrBookType = line.Substring("current profile: ".Length);
|
||||
}
|
||||
|
||||
line = sr.ReadLine();
|
||||
@@ -1362,12 +1499,67 @@ namespace MPF.Core.Modules.Redumper
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all Volume Identifiers
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>Volume labels (by type), or null if none present</returns>
|
||||
private static bool GetVolumeLabels(string log, out Dictionary<string, List<string>> volLabels)
|
||||
{
|
||||
// If the file doesn't exist, can't get the volume labels
|
||||
volLabels = [];
|
||||
if (!File.Exists(log))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
using var sr = File.OpenText(log);
|
||||
var line = sr.ReadLine();
|
||||
|
||||
while (line != null)
|
||||
{
|
||||
// Trim the line for later use
|
||||
line = line.Trim();
|
||||
|
||||
// ISO9660 Volume Identifier
|
||||
if (line.StartsWith("volume identifier: "))
|
||||
{
|
||||
string label = line.Substring("volume identifier: ".Length);
|
||||
|
||||
// Skip if label is blank
|
||||
if (label == null || label.Length <= 0)
|
||||
break;
|
||||
|
||||
if (volLabels.ContainsKey(label))
|
||||
volLabels[label].Add("ISO");
|
||||
else
|
||||
volLabels[label] = ["ISO"];
|
||||
|
||||
// Redumper log currently only outputs ISO9660 label, end here
|
||||
break;
|
||||
}
|
||||
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
// Return true if a volume label was found
|
||||
return volLabels.Count > 0;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
volLabels = [];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the DVD protection information, if possible
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <param name="includeAlways">Indicates whether region and protection type are always included</param>
|
||||
/// <returns>Formatted string representing the DVD protection, null on error</returns>
|
||||
private static string? GetDVDProtection(string log)
|
||||
private static string? GetDVDProtection(string log, bool includeAlways)
|
||||
{
|
||||
// If one of the files doesn't exist, we can't get info from them
|
||||
if (!File.Exists(log))
|
||||
@@ -1388,24 +1580,24 @@ namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
if (line.StartsWith("protection system type"))
|
||||
{
|
||||
copyrightProtectionSystemType = line["protection system type: ".Length..];
|
||||
copyrightProtectionSystemType = line.Substring("protection system type: ".Length);
|
||||
if (copyrightProtectionSystemType == "none" || copyrightProtectionSystemType == "<none>")
|
||||
copyrightProtectionSystemType = "No";
|
||||
}
|
||||
else if (line.StartsWith("region management information:"))
|
||||
{
|
||||
region = line["region management information: ".Length..];
|
||||
region = line.Substring("region management information: ".Length);
|
||||
}
|
||||
else if (line.StartsWith("disc key"))
|
||||
{
|
||||
decryptedDiscKey = line["disc key: ".Length..].Replace(':', ' ');
|
||||
decryptedDiscKey = line.Substring("disc key: ".Length).Replace(':', ' ');
|
||||
}
|
||||
else if (line.StartsWith("title keys"))
|
||||
{
|
||||
vobKeys = string.Empty;
|
||||
|
||||
line = sr.ReadLine()?.Trim();
|
||||
while (!string.IsNullOrWhiteSpace(line))
|
||||
while (!string.IsNullOrEmpty(line))
|
||||
{
|
||||
var match = Regex.Match(line, @"^(.*?): (.*?)$", RegexOptions.Compiled);
|
||||
if (match.Success)
|
||||
@@ -1437,6 +1629,15 @@ namespace MPF.Core.Modules.Redumper
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Filter out if we're not always including information
|
||||
if (!includeAlways)
|
||||
{
|
||||
if (region == "1 2 3 4 5 6 7 8")
|
||||
region = null;
|
||||
if (copyrightProtectionSystemType == "No")
|
||||
copyrightProtectionSystemType = null;
|
||||
}
|
||||
|
||||
// Now we format everything we can
|
||||
string protection = string.Empty;
|
||||
if (!string.IsNullOrEmpty(region))
|
||||
@@ -1482,7 +1683,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
break;
|
||||
|
||||
// REDUMP.ORG errors: <error count>
|
||||
string[] parts = line.Split(' ');
|
||||
string[] parts = line!.Split(' ');
|
||||
if (long.TryParse(parts[2], out long redump))
|
||||
return redump;
|
||||
else
|
||||
@@ -1498,6 +1699,45 @@ namespace MPF.Core.Modules.Redumper
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the EXE Date from the log, if possible
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>EXE date if possible, null otherwise</returns>
|
||||
public static string? GetEXEDate(string log)
|
||||
{
|
||||
// If the file doesn't exist, we can't get the info
|
||||
if (!File.Exists(log))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
using var sr = File.OpenText(log);
|
||||
var line = sr.ReadLine();
|
||||
while (line != null)
|
||||
{
|
||||
// Trim the line for later use
|
||||
line = line.Trim();
|
||||
|
||||
// The exe date is listed in a single line
|
||||
if (line.StartsWith("EXE date:"))
|
||||
{
|
||||
// exe date: yyyy-MM-dd
|
||||
return line.Substring("EXE date: ".Length);
|
||||
}
|
||||
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hardware information from the input file, if possible
|
||||
/// </summary>
|
||||
@@ -1590,24 +1830,24 @@ namespace MPF.Core.Modules.Redumper
|
||||
else if (line.StartsWith("layer break:"))
|
||||
{
|
||||
// layer break: <layerbreak>
|
||||
layerbreak1 = line["layer break: ".Length..].Trim();
|
||||
layerbreak1 = line.Substring("layer break: ".Length).Trim();
|
||||
}
|
||||
|
||||
// Multi-layer discs have the layer in the name
|
||||
else if (line.StartsWith("layer break (layer: 0):"))
|
||||
{
|
||||
// layer break (layer: 0): <layerbreak>
|
||||
layerbreak1 = line["layer break (layer: 0): ".Length..].Trim();
|
||||
layerbreak1 = line.Substring("layer break (layer: 0): ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("layer break (layer: 1):"))
|
||||
{
|
||||
// layer break (layer: 1): <layerbreak>
|
||||
layerbreak2 = line["layer break (layer: 1): ".Length..].Trim();
|
||||
layerbreak2 = line.Substring("layer break (layer: 1): ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("layer break (layer: 2):"))
|
||||
{
|
||||
// layer break (layer: 2): <layerbreak>
|
||||
layerbreak3 = line["layer break (layer: 2): ".Length..].Trim();
|
||||
layerbreak3 = line.Substring("layer break (layer: 2): ".Length).Trim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1652,11 +1892,11 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
// Store the first session range
|
||||
if (line.Contains("session 1:"))
|
||||
firstSession = line["session 1: ".Length..].Trim();
|
||||
firstSession = line.Substring("session 1: ".Length).Trim();
|
||||
|
||||
// Store the secomd session range
|
||||
else if (line.Contains("session 2:"))
|
||||
secondSession = line["session 2: ".Length..].Trim();
|
||||
secondSession = line.Substring("session 2: ".Length).Trim();
|
||||
}
|
||||
|
||||
// If either is blank, we don't have multisession
|
||||
@@ -1843,11 +2083,11 @@ namespace MPF.Core.Modules.Redumper
|
||||
return null;
|
||||
|
||||
// Now that we're at the relevant entries, read each line in and concatenate
|
||||
string? pvdString = "", line = sr.ReadLine()?.Trim();
|
||||
string? pvdString = "", line = sr.ReadLine();
|
||||
while (line?.StartsWith("03") == true)
|
||||
{
|
||||
pvdString += line + "\n";
|
||||
line = sr.ReadLine()?.Trim();
|
||||
line = sr.ReadLine();
|
||||
}
|
||||
|
||||
return pvdString.TrimEnd('\n');
|
||||
@@ -1878,7 +2118,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
string? line = sr.ReadLine()?.TrimStart();
|
||||
if (line?.StartsWith("non-zero data sample range") == true)
|
||||
return line["non-zero data sample range: [".Length..].Trim().Split(' ')[0];
|
||||
return line.Substring("non-zero data sample range: [".Length).Trim().Split(' ')[0];
|
||||
}
|
||||
|
||||
// We couldn't detect it then
|
||||
@@ -1902,18 +2142,18 @@ namespace MPF.Core.Modules.Redumper
|
||||
serial = null; version = null; date = null;
|
||||
|
||||
// If the input header is null, we can't do a thing
|
||||
if (string.IsNullOrWhiteSpace(segaHeader))
|
||||
if (string.IsNullOrEmpty(segaHeader))
|
||||
return false;
|
||||
|
||||
// Now read it in cutting it into lines for easier parsing
|
||||
try
|
||||
{
|
||||
string[] header = segaHeader.Split('\n');
|
||||
string serialVersionLine = header[2][58..];
|
||||
string dateLine = header[3][58..];
|
||||
serial = serialVersionLine[..10].Trim();
|
||||
string[] header = segaHeader!.Split('\n');
|
||||
string serialVersionLine = header[2].Substring(58);
|
||||
string dateLine = header[3].Substring(58);
|
||||
serial = serialVersionLine.Substring(0, 10).Trim();
|
||||
version = serialVersionLine.Substring(10, 6).TrimStart('V', 'v');
|
||||
date = dateLine[..8];
|
||||
date = dateLine.Substring(0, 8);
|
||||
date = $"{date[0]}{date[1]}{date[2]}{date[3]}-{date[4]}{date[5]}-{date[6]}{date[7]}";
|
||||
return true;
|
||||
}
|
||||
@@ -2006,7 +2246,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
lines.Add(line);
|
||||
}
|
||||
|
||||
return string.Join("\n", lines).TrimEnd('\n');
|
||||
return string.Join("\n", [.. lines]).TrimEnd('\n');
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -2046,19 +2286,19 @@ namespace MPF.Core.Modules.Redumper
|
||||
|
||||
if (line.StartsWith("build date:"))
|
||||
{
|
||||
buildDate = line["build date: ".Length..].Trim();
|
||||
buildDate = line.Substring("build date: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("serial:"))
|
||||
{
|
||||
serial = line["serial: ".Length..].Trim();
|
||||
serial = line.Substring("serial: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("region:"))
|
||||
{
|
||||
region = line["region: ".Length..].Trim();
|
||||
region = line.Substring("region: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("regions:"))
|
||||
{
|
||||
region = line["regions: ".Length..].Trim();
|
||||
region = line.Substring("regions: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("header:"))
|
||||
{
|
||||
@@ -2103,7 +2343,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
string? line = sr.ReadLine()?.TrimStart();
|
||||
if (line?.StartsWith("Universal Hash") == true)
|
||||
return line["Universal Hash (SHA-1): ".Length..].Trim();
|
||||
return line.Substring("Universal Hash (SHA-1): ".Length).Trim();
|
||||
}
|
||||
|
||||
// We couldn't detect it then
|
||||
@@ -2135,7 +2375,7 @@ namespace MPF.Core.Modules.Redumper
|
||||
{
|
||||
string? line = sr.ReadLine()?.TrimStart();
|
||||
if (line?.StartsWith("disc write offset") == true)
|
||||
return line["disc write offset: ".Length..].Trim();
|
||||
return line.Substring("disc write offset: ".Length).Trim();
|
||||
}
|
||||
|
||||
// We couldn't detect it then
|
||||
@@ -2169,6 +2409,11 @@ namespace MPF.Core.Modules.Redumper
|
||||
using var sr = File.OpenText(log);
|
||||
sr.ReadLine();
|
||||
|
||||
// Get the next non-warning line
|
||||
string nextLine = sr.ReadLine()?.Trim() ?? string.Empty;
|
||||
if (nextLine.StartsWith("warning:", StringComparison.OrdinalIgnoreCase))
|
||||
nextLine = sr.ReadLine()?.Trim() ?? string.Empty;
|
||||
|
||||
// Generate regex
|
||||
// Permissive
|
||||
var regex = new Regex(@"^redumper (v.+) \[.+\]", RegexOptions.Compiled);
|
||||
@@ -2176,9 +2421,9 @@ namespace MPF.Core.Modules.Redumper
|
||||
//var regex = new Regex(@"^redumper (v\d{4}\.\d{2}\.\d{2}(| build_\d+)) \[.+\]", RegexOptions.Compiled);
|
||||
|
||||
// Extract the version string
|
||||
var match = regex.Match(sr.ReadLine()?.Trim() ?? string.Empty);
|
||||
var match = regex.Match(nextLine);
|
||||
var version = match.Groups[1].Value;
|
||||
return string.IsNullOrWhiteSpace(version) ? null : version;
|
||||
return string.IsNullOrEmpty(version) ? null : version;
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@@ -4,7 +4,8 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Hashing;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Core.Modules.UmdImageCreator
|
||||
@@ -65,7 +66,7 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, Options options, string basePath, Drive? drive, bool includeArtifacts)
|
||||
{
|
||||
// Ensure that required sections exist
|
||||
info = SubmissionInfoTool.EnsureAllSections(info);
|
||||
info = Builder.EnsureAllSections(info);
|
||||
|
||||
// TODO: Determine if there's a UMDImageCreator version anywhere
|
||||
info.DumpingInfo!.DumpingProgram = EnumConverter.LongName(this.InternalProgram);
|
||||
@@ -77,8 +78,17 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
case MediaType.UMD:
|
||||
info.Extras!.PVD = GetPVD(basePath + "_mainInfo.txt") ?? string.Empty;
|
||||
|
||||
if (Hasher.GetFileHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
|
||||
if (HashTool.GetStandardHashes(basePath + ".iso", out long filesize, out var crc32, out var md5, out var sha1))
|
||||
{
|
||||
// Get the Datafile information
|
||||
var datafile = new Datafile
|
||||
{
|
||||
Games = [new Game { Roms = [new Rom { Name = string.Empty, Size = filesize.ToString(), Crc = crc32, Md5 = md5, Sha1 = sha1, }] }]
|
||||
};
|
||||
|
||||
// Fill in the hash data
|
||||
info.TracksAndWriteOffsets!.ClrMameProData = InfoTool.GenerateDatfile(datafile);
|
||||
|
||||
info.SizeAndChecksums!.Size = filesize;
|
||||
info.SizeAndChecksums.CRC32 = crc32;
|
||||
info.SizeAndChecksums.MD5 = md5;
|
||||
@@ -92,7 +102,7 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -102,7 +112,7 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
// Fill in any artifacts that exist, Base64-encoded, if we need to
|
||||
if (includeArtifacts)
|
||||
{
|
||||
info.Artifacts ??= new Dictionary<string, string>();
|
||||
info.Artifacts ??= [];
|
||||
|
||||
if (File.Exists(basePath + "_disc.txt"))
|
||||
info.Artifacts["disc"] = GetBase64(GetFullFile(basePath + "_disc.txt")) ?? string.Empty;
|
||||
@@ -203,7 +213,7 @@ namespace MPF.Core.Modules.UmdImageCreator
|
||||
break;
|
||||
|
||||
if (line.StartsWith("TITLE") && title == null)
|
||||
title = line["TITLE: ".Length..];
|
||||
title = line.Substring("TITLE: ".Length);
|
||||
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
|
||||
umdversion = line.Split(' ')[1];
|
||||
else if (line.StartsWith("pspUmdTypes"))
|
||||
|
||||
@@ -4,8 +4,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using BinaryObjectScanner;
|
||||
using BinaryObjectScanner.Protection;
|
||||
using psxt001z;
|
||||
|
||||
#pragma warning disable SYSLIB1045 // Convert to 'GeneratedRegexAttribute'.
|
||||
@@ -21,13 +19,14 @@ namespace MPF.Core
|
||||
/// <param name="options">Options object that determines what to scan</param>
|
||||
/// <param name="progress">Optional progress callback</param>
|
||||
/// <returns>Set of all detected copy protections with an optional error string</returns>
|
||||
public static async Task<(Dictionary<string, List<string>>?, string?)> RunProtectionScanOnPath(string path, Data.Options options, IProgress<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
|
||||
@@ -38,14 +37,37 @@ namespace MPF.Core
|
||||
|
||||
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());
|
||||
@@ -78,7 +100,7 @@ namespace MPF.Core
|
||||
|
||||
// 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;
|
||||
@@ -95,18 +117,19 @@ namespace MPF.Core
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return false;
|
||||
|
||||
return await Task.Run(() =>
|
||||
#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);
|
||||
var protection = antiModchip.CheckContents(file, fileContent, false);
|
||||
if (!string.IsNullOrWhiteSpace(protection))
|
||||
if (!string.IsNullOrEmpty(protection))
|
||||
return true;
|
||||
}
|
||||
catch { }
|
||||
@@ -116,6 +139,33 @@ namespace MPF.Core
|
||||
|
||||
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>
|
||||
@@ -129,7 +179,7 @@ namespace MPF.Core
|
||||
if (!File.Exists(sub))
|
||||
return null;
|
||||
|
||||
return LibCrypt.CheckSubfile(sub);
|
||||
return LibCrypt.DetectLibCrypt([sub]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -142,9 +192,15 @@ namespace MPF.Core
|
||||
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
|
||||
@@ -225,7 +281,16 @@ namespace MPF.Core
|
||||
.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
|
||||
@@ -327,7 +392,7 @@ namespace MPF.Core
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
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
@@ -217,11 +217,13 @@ namespace MPF.Core.UI.ViewModels
|
||||
/// TODO: Convert selected list item to binding
|
||||
public void Save()
|
||||
{
|
||||
if (SubmissionInfo.CommonDiscInfo == null) SubmissionInfo.CommonDiscInfo = new CommonDiscInfoSection();
|
||||
if (SubmissionInfo.CommonDiscInfo == null)
|
||||
SubmissionInfo.CommonDiscInfo = new CommonDiscInfoSection();
|
||||
SubmissionInfo.CommonDiscInfo.Languages = Languages.Where(l => l.IsChecked).Select(l => l?.Value).ToArray();
|
||||
if (!SubmissionInfo.CommonDiscInfo.Languages.Any())
|
||||
SubmissionInfo.CommonDiscInfo.Languages = new Language?[] { null };
|
||||
SubmissionInfo.CommonDiscInfo.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>
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Core.Converters;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.Modules;
|
||||
using MPF.Core.UI.ComboBoxItems;
|
||||
using MPF.Core.Utilities;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
@@ -49,7 +50,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// T1 - Title to display to the user
|
||||
/// T1 - Message to display to the user
|
||||
/// T2 - Message to display to the user
|
||||
/// T3 - Number of default options to display
|
||||
/// T4 - true for inquiry, false otherwise
|
||||
/// TResult - true for positive, false for negative, null for neutral
|
||||
@@ -75,6 +76,34 @@ namespace MPF.Core.UI.ViewModels
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the check dump menu item
|
||||
/// </summary>
|
||||
public bool CheckDumpMenuItemEnabled
|
||||
{
|
||||
get => _checkDumpMenuItemEnabled;
|
||||
set
|
||||
{
|
||||
_checkDumpMenuItemEnabled = value;
|
||||
TriggerPropertyChanged(nameof(CheckDumpMenuItemEnabled));
|
||||
}
|
||||
}
|
||||
private bool _checkDumpMenuItemEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the create IRD menu item
|
||||
/// </summary>
|
||||
public bool CreateIRDMenuItemEnabled
|
||||
{
|
||||
get => _createIRDMenuItemEnabled;
|
||||
set
|
||||
{
|
||||
_createIRDMenuItemEnabled = value;
|
||||
TriggerPropertyChanged(nameof(CreateIRDMenuItemEnabled));
|
||||
}
|
||||
}
|
||||
private bool _createIRDMenuItemEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates the status of the options menu item
|
||||
/// </summary>
|
||||
@@ -147,6 +176,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
|
||||
/// <summary>
|
||||
/// Currently provided output path
|
||||
/// Not guaranteed to be a valid path
|
||||
/// </summary>
|
||||
public string OutputPath
|
||||
{
|
||||
@@ -495,16 +525,18 @@ namespace MPF.Core.UI.ViewModels
|
||||
_options = OptionsLoader.LoadFromConfig();
|
||||
|
||||
// Added to clear warnings, all are set externally
|
||||
_drives = new List<Drive>();
|
||||
_driveSpeeds = new List<int>();
|
||||
_internalPrograms = new List<Element<InternalProgram>>();
|
||||
_drives = [];
|
||||
_driveSpeeds = [];
|
||||
_internalPrograms = [];
|
||||
_outputPath = string.Empty;
|
||||
_parameters = string.Empty;
|
||||
_startStopButtonText = string.Empty;
|
||||
_status = string.Empty;
|
||||
_systems = new List<RedumpSystemComboBoxItem>();
|
||||
_systems = [];
|
||||
|
||||
OptionsMenuItemEnabled = true;
|
||||
CheckDumpMenuItemEnabled = true;
|
||||
CreateIRDMenuItemEnabled = true;
|
||||
SystemTypeComboBoxEnabled = true;
|
||||
MediaTypeComboBoxEnabled = true;
|
||||
OutputPathTextBoxEnabled = true;
|
||||
@@ -517,9 +549,9 @@ namespace MPF.Core.UI.ViewModels
|
||||
EnableParametersCheckBoxEnabled = true;
|
||||
LogPanelExpanded = _options.OpenLogWindowAtStartup;
|
||||
|
||||
MediaTypes = new List<Element<MediaType>>();
|
||||
MediaTypes = [];
|
||||
Systems = RedumpSystemComboBoxItem.GenerateElements().ToList();
|
||||
InternalPrograms = new List<Element<InternalProgram>>();
|
||||
InternalPrograms = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -585,7 +617,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
|
||||
if (Drives.Count > 0)
|
||||
{
|
||||
VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Name))}");
|
||||
VerboseLogLn($"Found {Drives.Count} drives: {string.Join(", ", Drives.Select(d => d.Name).ToArray())}");
|
||||
|
||||
// Check for the last selected drive, if possible
|
||||
int index = -1;
|
||||
@@ -669,16 +701,23 @@ namespace MPF.Core.UI.ViewModels
|
||||
bool cachedCanExecuteSelectionChanged = CanExecuteSelectionChanged;
|
||||
DisableEventHandlers();
|
||||
|
||||
// Create a static list of supported programs, not everything
|
||||
InternalPrograms = Enum.GetValues(typeof(InternalProgram)).Cast<InternalProgram>().Where(ip => InternalProgramExists(ip)).Select(ip => new Element<InternalProgram>(ip)).ToList();
|
||||
|
||||
// Get the current internal program
|
||||
InternalProgram internalProgram = this.Options.InternalProgram;
|
||||
|
||||
// Create a static list of supported programs, not everything
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.Redumper };
|
||||
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);
|
||||
if (InternalPrograms.Count == 0)
|
||||
{
|
||||
// If no programs are found, default to InternalProgram.NONE
|
||||
this.CurrentProgram = InternalProgram.NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
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();
|
||||
@@ -764,7 +803,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
SchemaVersion = 1,
|
||||
FullyMatchedID = 3,
|
||||
PartiallyMatchedIDs = new List<int> { 0, 1, 2, 3 },
|
||||
PartiallyMatchedIDs = [0, 1, 2, 3],
|
||||
Added = DateTime.UtcNow,
|
||||
LastModified = DateTime.UtcNow,
|
||||
|
||||
@@ -778,8 +817,8 @@ namespace MPF.Core.UI.ViewModels
|
||||
DiscTitle = "Install Disc",
|
||||
Category = DiscCategory.Games,
|
||||
Region = Region.World,
|
||||
Languages = new Language?[] { Language.English, Language.Spanish, Language.French },
|
||||
LanguageSelection = new LanguageSelection?[] { LanguageSelection.BiosSettings },
|
||||
Languages = [Language.English, Language.Spanish, Language.French],
|
||||
LanguageSelection = [LanguageSelection.BiosSettings],
|
||||
Serial = "Disc Serial",
|
||||
Layer0MasteringRing = "L0 Mastering Ring",
|
||||
Layer0MasteringSID = "L0 Mastering SID",
|
||||
@@ -817,7 +856,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
Version = "Original",
|
||||
VersionDatfile = "Alt",
|
||||
CommonEditions = new string[] { "Taikenban" },
|
||||
CommonEditions = ["Taikenban"],
|
||||
OtherEditions = "Rerelease",
|
||||
},
|
||||
|
||||
@@ -855,7 +894,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
DumpersAndStatus = new DumpersAndStatusSection()
|
||||
{
|
||||
Status = DumpStatus.TwoOrMoreGreen,
|
||||
Dumpers = new string[] { "Dumper1", "Dumper2" },
|
||||
Dumpers = ["Dumper1", "Dumper2"],
|
||||
OtherDumpers = "Dumper3",
|
||||
},
|
||||
|
||||
@@ -863,7 +902,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
ClrMameProData = "Datfile",
|
||||
Cuesheet = "Cuesheet",
|
||||
CommonWriteOffsets = new int[] { 0, 12, -12 },
|
||||
CommonWriteOffsets = [0, 12, -12],
|
||||
OtherWriteOffsets = "-2",
|
||||
},
|
||||
|
||||
@@ -1189,7 +1228,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
return new DumpEnvironment(
|
||||
this.Options,
|
||||
this.OutputPath,
|
||||
EvaluateOutputPath(this.OutputPath),
|
||||
this.CurrentDrive,
|
||||
this.CurrentSystem,
|
||||
this.CurrentMediaType,
|
||||
@@ -1237,6 +1276,8 @@ namespace MPF.Core.UI.ViewModels
|
||||
public void DisableAllUIElements()
|
||||
{
|
||||
OptionsMenuItemEnabled = false;
|
||||
CheckDumpMenuItemEnabled = false;
|
||||
CreateIRDMenuItemEnabled = false;
|
||||
SystemTypeComboBoxEnabled = false;
|
||||
MediaTypeComboBoxEnabled = false;
|
||||
OutputPathTextBoxEnabled = false;
|
||||
@@ -1257,6 +1298,8 @@ namespace MPF.Core.UI.ViewModels
|
||||
public void EnableAllUIElements()
|
||||
{
|
||||
OptionsMenuItemEnabled = true;
|
||||
CheckDumpMenuItemEnabled = true;
|
||||
CreateIRDMenuItemEnabled = true;
|
||||
SystemTypeComboBoxEnabled = true;
|
||||
MediaTypeComboBoxEnabled = true;
|
||||
OutputPathTextBoxEnabled = true;
|
||||
@@ -1281,7 +1324,10 @@ namespace MPF.Core.UI.ViewModels
|
||||
|
||||
// Get the status to write out
|
||||
Result result = Tools.GetSupportStatus(_environment.System, _environment.Type);
|
||||
this.Status = result.Message;
|
||||
if (this.CurrentProgram == InternalProgram.NONE || _environment.Parameters == null)
|
||||
this.Status = "No dumping program found";
|
||||
else
|
||||
this.Status = result.Message;
|
||||
|
||||
// Enable or disable the button
|
||||
this.StartStopButtonEnabled = result && ShouldEnableDumpingButton();
|
||||
@@ -1298,6 +1344,49 @@ namespace MPF.Core.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces %-delimited variables inside a path string with their values
|
||||
/// </summary>
|
||||
/// <param name="outputPath">Path to be evaluated</param>
|
||||
/// <returns>String with %-delimited variables evaluated</returns>
|
||||
public string EvaluateOutputPath(string outputPath)
|
||||
{
|
||||
string systemLong = this._currentSystem.LongName() ?? "Unknown System";
|
||||
if (string.IsNullOrEmpty(systemLong))
|
||||
systemLong = "Unknown System";
|
||||
string systemShort = this._currentSystem.ShortName() ?? "unknown";
|
||||
if (string.IsNullOrEmpty(systemShort))
|
||||
systemShort = "unknown";
|
||||
string mediaLong = this._currentMediaType.LongName() ?? "Unknown Media";
|
||||
if (string.IsNullOrEmpty(mediaLong))
|
||||
mediaLong = "Unknown Media";
|
||||
string program = this._currentProgram.ToString() ?? "Unknown Program";
|
||||
if (string.IsNullOrEmpty(program))
|
||||
program = "Unknown Program";
|
||||
string programShort = program == "DiscImageCreator" ? "DIC" : program;
|
||||
if (string.IsNullOrEmpty(programShort))
|
||||
programShort = "Unknown Program";
|
||||
string label = this._currentDrive?.FormattedVolumeLabel ?? "track";
|
||||
if (string.IsNullOrEmpty(label))
|
||||
label = "track";
|
||||
string date = DateTime.Today.ToString("yyyyMMdd");
|
||||
if (string.IsNullOrEmpty(date))
|
||||
date = "UNKNOWN";
|
||||
string datetime = DateTime.Now.ToString("yyyyMMdd-HHmmss");
|
||||
if (string.IsNullOrEmpty(datetime))
|
||||
datetime = "UNKNOWN";
|
||||
|
||||
return outputPath
|
||||
.Replace("%SYSTEM%", systemLong)
|
||||
.Replace("%SYS%", systemShort)
|
||||
.Replace("%MEDIA%", mediaLong)
|
||||
.Replace("%PROGRAM%", program)
|
||||
.Replace("%PROG%", programShort)
|
||||
.Replace("%LABEL%", label)
|
||||
.Replace("%DATE%", date)
|
||||
.Replace("%DATETIME%", datetime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the default output directory name from the currently selected drive
|
||||
/// </summary>
|
||||
@@ -1325,7 +1414,11 @@ namespace MPF.Core.UI.ViewModels
|
||||
directory = Path.GetDirectoryName(directory);
|
||||
|
||||
if (directory != null && label != null)
|
||||
#if NET20 || NET35
|
||||
this.OutputPath = Path.Combine(Path.Combine(directory, label), filename);
|
||||
#else
|
||||
this.OutputPath = Path.Combine(directory, label, filename);
|
||||
#endif
|
||||
else
|
||||
this.OutputPath = filename;
|
||||
}
|
||||
@@ -1348,7 +1441,11 @@ namespace MPF.Core.UI.ViewModels
|
||||
directory = Path.GetDirectoryName(directory);
|
||||
|
||||
if (directory != null && label != null)
|
||||
#if NET20 || NET35
|
||||
this.OutputPath = Path.Combine(Path.Combine(directory, label), filename);
|
||||
#else
|
||||
this.OutputPath = Path.Combine(directory, label, filename);
|
||||
#endif
|
||||
else
|
||||
this.OutputPath = filename;
|
||||
}
|
||||
@@ -1410,7 +1507,11 @@ namespace MPF.Core.UI.ViewModels
|
||||
/// <summary>
|
||||
/// Scan and show copy protection for the current disc
|
||||
/// </summary>
|
||||
#if NET40
|
||||
public (string?, string?) ScanAndShowProtection()
|
||||
#else
|
||||
public async Task<(string?, string?)> ScanAndShowProtection()
|
||||
#endif
|
||||
{
|
||||
// Determine current environment, just in case
|
||||
_environment ??= DetermineEnvironment();
|
||||
@@ -1430,7 +1531,13 @@ namespace MPF.Core.UI.ViewModels
|
||||
|
||||
var progress = new Progress<ProtectionProgress>();
|
||||
progress.ProgressChanged += ProgressUpdated;
|
||||
#if NET40
|
||||
var protectionTask = Protection.RunProtectionScanOnPath(this.CurrentDrive.Name, this.Options, progress);
|
||||
protectionTask.Wait();
|
||||
var (protections, error) = protectionTask.Result;
|
||||
#else
|
||||
var (protections, error) = await Protection.RunProtectionScanOnPath(this.CurrentDrive.Name, this.Options, progress);
|
||||
#endif
|
||||
var output = Protection.FormatProtections(protections);
|
||||
|
||||
// If SmartE is detected on the current disc, remove `/sf` from the flags for DIC only -- Disabled until further notice
|
||||
@@ -1494,7 +1601,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
// Set the drive speed list that's appropriate
|
||||
this.DriveSpeeds = (List<int>)Interface.GetSpeedsForMediaType(CurrentMediaType);
|
||||
VerboseLogLn($"Supported media speeds: {string.Join(", ", this.DriveSpeeds)}");
|
||||
VerboseLogLn($"Supported media speeds: {string.Join(", ", this.DriveSpeeds.Select(ds => ds.ToString()).ToArray())}");
|
||||
|
||||
// Set the selected speed
|
||||
int speed = (CurrentMediaType) switch
|
||||
@@ -1577,8 +1684,8 @@ namespace MPF.Core.UI.ViewModels
|
||||
_environment.Drive?.RefreshDrive();
|
||||
|
||||
// Output to the label and log
|
||||
this.Status = "Starting dumping process... Please wait!";
|
||||
LogLn("Starting dumping process... Please wait!");
|
||||
this.Status = "Starting dumping process... please wait!";
|
||||
LogLn("Starting dumping process... please wait!");
|
||||
if (this.Options.ToolsInSeparateWindow)
|
||||
LogLn("Look for the separate command window for more details");
|
||||
else
|
||||
@@ -1592,7 +1699,11 @@ namespace MPF.Core.UI.ViewModels
|
||||
_environment.ReportStatus += ProgressUpdated;
|
||||
|
||||
// Run the program with the parameters
|
||||
#if NET40
|
||||
Result result = _environment.Run(resultProgress);
|
||||
#else
|
||||
Result result = await _environment.Run(resultProgress);
|
||||
#endif
|
||||
|
||||
// If we didn't execute a dumping command we cannot get submission output
|
||||
if (_environment.Parameters?.IsDumpingCommand() != true)
|
||||
@@ -1667,8 +1778,11 @@ namespace MPF.Core.UI.ViewModels
|
||||
/// <returns>True if dumping should start, false otherwise</returns>
|
||||
private bool ValidateBeforeDumping()
|
||||
{
|
||||
if (Parameters == null)
|
||||
return false;
|
||||
|
||||
// Validate that we have an output path of any sort
|
||||
if (string.IsNullOrWhiteSpace(_environment?.OutputPath))
|
||||
if (string.IsNullOrEmpty(_environment?.OutputPath))
|
||||
{
|
||||
if (_displayUserMessage != null)
|
||||
_ = _displayUserMessage("Missing Path", "No output path was provided so dumping cannot continue.", 1, false);
|
||||
@@ -1677,10 +1791,10 @@ namespace MPF.Core.UI.ViewModels
|
||||
}
|
||||
|
||||
// Validate that the user explicitly wants an inactive drive to be considered for dumping
|
||||
if (_environment.Drive?.MarkedActive != true && _displayUserMessage != null)
|
||||
if (_environment?.Drive?.MarkedActive != true && _displayUserMessage != null)
|
||||
{
|
||||
string message = "The currently selected drive does not appear to contain a disc! "
|
||||
+ (!_environment.System.DetectedByWindows() ? $"This is normal for {_environment.System.LongName()} as the discs may not be readable on Windows. " : string.Empty)
|
||||
+ (!_environment!.System.DetectedByWindows() ? $"This is normal for {_environment.System.LongName()} as the discs may not be readable on Windows. " : string.Empty)
|
||||
+ "Do you want to continue?";
|
||||
|
||||
bool? mbresult = _displayUserMessage("No Disc Detected", message, 2, false);
|
||||
@@ -1692,11 +1806,11 @@ namespace MPF.Core.UI.ViewModels
|
||||
}
|
||||
|
||||
// Pre-split the output path
|
||||
var outputDirectory = Path.GetDirectoryName(_environment.OutputPath);
|
||||
var outputDirectory = Path.GetDirectoryName(_environment!.OutputPath);
|
||||
string outputFilename = Path.GetFileName(_environment.OutputPath);
|
||||
|
||||
// If a complete dump already exists
|
||||
(bool foundFiles, List<string> _) = InfoTool.FoundAllFiles(outputDirectory, outputFilename, _environment.Parameters, true);
|
||||
(bool foundFiles, List<string> _) = _environment.Parameters.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
if (foundFiles && _displayUserMessage != null)
|
||||
{
|
||||
bool? mbresult = _displayUserMessage("Overwrite?", "A complete dump already exists! Are you sure you want to overwrite?", 2, true);
|
||||
@@ -1706,11 +1820,58 @@ namespace MPF.Core.UI.ViewModels
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a complete dump exists from a different program
|
||||
InternalProgram? programFound = null;
|
||||
if (programFound == null && _environment.InternalProgram != InternalProgram.Aaru)
|
||||
{
|
||||
Modules.Aaru.Parameters parameters = new("")
|
||||
{
|
||||
Type = _environment.Type,
|
||||
System = _environment.System
|
||||
};
|
||||
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
if (foundOtherFiles)
|
||||
programFound = InternalProgram.Aaru;
|
||||
}
|
||||
if (programFound == null && _environment.InternalProgram != InternalProgram.DiscImageCreator)
|
||||
{
|
||||
Modules.DiscImageCreator.Parameters parameters = new("")
|
||||
{
|
||||
Type = _environment.Type,
|
||||
System = _environment.System
|
||||
};
|
||||
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
if (foundOtherFiles)
|
||||
programFound = InternalProgram.DiscImageCreator;
|
||||
}
|
||||
if (programFound == null && _environment.InternalProgram != InternalProgram.Redumper)
|
||||
{
|
||||
Modules.Redumper.Parameters parameters = new("")
|
||||
{
|
||||
Type = _environment.Type,
|
||||
System = _environment.System
|
||||
};
|
||||
(bool foundOtherFiles, _) = parameters.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
if (foundOtherFiles)
|
||||
programFound = InternalProgram.Redumper;
|
||||
}
|
||||
if (programFound != null && _displayUserMessage != null)
|
||||
{
|
||||
bool? mbresult = _displayUserMessage("Overwrite?", $"A complete dump from {programFound} already exists! Dumping here may cause issues. Are you sure you want to overwrite?", 2, true);
|
||||
if (mbresult != true)
|
||||
{
|
||||
LogLn("Dumping aborted!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that at least some space exists
|
||||
// TODO: Tie this to the size of the disc, type of disc, etc.
|
||||
string fullPath;
|
||||
if (string.IsNullOrWhiteSpace(outputDirectory))
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
fullPath = Path.GetFullPath(_environment.OutputPath);
|
||||
else
|
||||
fullPath = Path.GetFullPath(outputDirectory);
|
||||
@@ -1730,6 +1891,29 @@ namespace MPF.Core.UI.ViewModels
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a internal program is found in its path
|
||||
/// </summary>
|
||||
/// <param name="program">Program to check for</param>
|
||||
/// <returns>True if the program is found, false otherwise</returns>
|
||||
private bool InternalProgramExists(InternalProgram program)
|
||||
{
|
||||
try
|
||||
{
|
||||
return program switch
|
||||
{
|
||||
InternalProgram.Redumper => File.Exists(this.Options.RedumperPath),
|
||||
InternalProgram.Aaru => File.Exists(this.Options.AaruPath),
|
||||
InternalProgram.DiscImageCreator => File.Exists(this.Options.DiscImageCreatorPath),
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Progress Reporting
|
||||
@@ -1737,12 +1921,21 @@ namespace MPF.Core.UI.ViewModels
|
||||
/// <summary>
|
||||
/// Handler for Result ProgressChanged event
|
||||
/// </summary>
|
||||
#if NET20 || NET35 || NET40
|
||||
private void ProgressUpdated(object? sender, BaseParameters.StringEventArgs value)
|
||||
#else
|
||||
private void ProgressUpdated(object? sender, string value)
|
||||
#endif
|
||||
{
|
||||
try
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
value.Value ??= string.Empty;
|
||||
LogLn(value.Value);
|
||||
#else
|
||||
value ??= string.Empty;
|
||||
LogLn(value);
|
||||
#endif
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@ using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace MPF.Core.UI.ViewModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public class OptionsViewModel : INotifyPropertyChanged
|
||||
{
|
||||
#region Fields
|
||||
@@ -56,7 +59,15 @@ namespace MPF.Core.UI.ViewModels
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// Constructor for pure view model
|
||||
/// </summary>
|
||||
public OptionsViewModel()
|
||||
{
|
||||
Options = new Options();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for in-code
|
||||
/// </summary>
|
||||
public OptionsViewModel(Options baseOptions)
|
||||
{
|
||||
@@ -70,7 +81,7 @@ namespace MPF.Core.UI.ViewModels
|
||||
/// </summary>
|
||||
private static List<Element<InternalProgram>> PopulateInternalPrograms()
|
||||
{
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.DiscImageCreator, InternalProgram.Aaru, InternalProgram.Redumper };
|
||||
var internalPrograms = new List<InternalProgram> { InternalProgram.Redumper, InternalProgram.DiscImageCreator, InternalProgram.Aaru };
|
||||
return internalPrograms.Select(ip => new Element<InternalProgram>(ip)).ToList();
|
||||
}
|
||||
|
||||
@@ -81,9 +92,19 @@ namespace MPF.Core.UI.ViewModels
|
||||
/// <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
|
||||
|
||||
@@ -8,33 +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)
|
||||
{
|
||||
return system switch
|
||||
{
|
||||
RedumpSystem.AmericanLaserGames3DO
|
||||
or RedumpSystem.AppleMacintosh
|
||||
or RedumpSystem.Atari3DO
|
||||
or RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem
|
||||
or RedumpSystem.NewJatreCDi
|
||||
or RedumpSystem.NintendoGameCube
|
||||
or RedumpSystem.NintendoWii
|
||||
or RedumpSystem.NintendoWiiU
|
||||
or RedumpSystem.PhilipsCDi
|
||||
or RedumpSystem.PhilipsCDiDigitalVideo
|
||||
or RedumpSystem.Panasonic3DOInteractiveMultiplayer
|
||||
or RedumpSystem.PanasonicM2
|
||||
or RedumpSystem.PioneerLaserActive
|
||||
or RedumpSystem.SuperAudioCD => false,
|
||||
_ => true,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the media supports drive speeds
|
||||
/// </summary>
|
||||
@@ -55,69 +28,6 @@ namespace MPF.Core.Utilities
|
||||
};
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
return system switch
|
||||
{
|
||||
RedumpSystem.SonyPlayStation2
|
||||
or RedumpSystem.SonyPlayStation3
|
||||
or RedumpSystem.SonyPlayStation4
|
||||
or RedumpSystem.SonyPlayStation5
|
||||
or RedumpSystem.SonyPlayStationPortable => true,
|
||||
_ => 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)
|
||||
{
|
||||
return system switch
|
||||
{
|
||||
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem
|
||||
or RedumpSystem.AudioCD
|
||||
or RedumpSystem.DVDAudio
|
||||
or RedumpSystem.HasbroiONEducationalGamingSystem
|
||||
or RedumpSystem.HasbroVideoNow
|
||||
or RedumpSystem.HasbroVideoNowColor
|
||||
or RedumpSystem.HasbroVideoNowJr
|
||||
or RedumpSystem.HasbroVideoNowXP
|
||||
or RedumpSystem.PhilipsCDi
|
||||
or RedumpSystem.PlayStationGameSharkUpdates
|
||||
or RedumpSystem.SuperAudioCD => true,
|
||||
_ => 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)
|
||||
{
|
||||
return system switch
|
||||
{
|
||||
RedumpSystem.MicrosoftXbox
|
||||
or RedumpSystem.MicrosoftXbox360
|
||||
or RedumpSystem.MicrosoftXboxOne
|
||||
or RedumpSystem.MicrosoftXboxSeriesXS => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all programs with their short usable names
|
||||
/// </summary>
|
||||
@@ -127,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>
|
||||
#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);
|
||||
@@ -37,22 +51,38 @@ namespace MPF.Core.Utilities
|
||||
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>
|
||||
#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,17 +152,26 @@ namespace MPF.Core.Utilities
|
||||
/// <param name="line">Current line to process</param>
|
||||
/// <param name="baseClass">Invoking class, passed on to the event handler</param>
|
||||
/// <param name="handler">Event handler to be invoked to write to log</param>
|
||||
#if 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[^1]}");
|
||||
sb.Append($"\r{split[split.Length - 1]}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ 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
|
||||
@@ -104,7 +105,7 @@ namespace MPF.Core.Utilities
|
||||
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)
|
||||
@@ -173,16 +174,22 @@ 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 = SubmissionInfoTool.CreateFromFile(seedInfo);
|
||||
info = Builder.CreateFromFile(seedInfo);
|
||||
}
|
||||
else if (args[startIndex] == "-l" || args[startIndex] == "--load-seed")
|
||||
{
|
||||
string seedInfo = args[startIndex + 1];
|
||||
info = SubmissionInfoTool.CreateFromFile(seedInfo);
|
||||
info = Builder.CreateFromFile(seedInfo);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
@@ -218,8 +225,9 @@ 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, info, parsedPath, startIndex);
|
||||
}
|
||||
@@ -237,6 +245,7 @@ namespace MPF.Core.Utilities
|
||||
"-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",
|
||||
@@ -247,7 +256,7 @@ namespace MPF.Core.Utilities
|
||||
return supportedArguments;
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Configuration
|
||||
|
||||
@@ -263,7 +272,8 @@ namespace MPF.Core.Utilities
|
||||
}
|
||||
|
||||
var serializer = JsonSerializer.Create();
|
||||
var reader = new StreamReader(ConfigurationPath);
|
||||
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
|
||||
@@ -189,7 +221,7 @@ namespace MPF.Core.Utilities
|
||||
|
||||
// Get the latest tag from GitHub
|
||||
var (tag, url) = GetRemoteVersionAndUrl();
|
||||
bool different = version != tag;
|
||||
bool different = version != tag && tag != null;
|
||||
|
||||
string message = $"Local version: {version}"
|
||||
+ $"{Environment.NewLine}Remote version: {tag}"
|
||||
@@ -230,13 +262,21 @@ namespace MPF.Core.Utilities
|
||||
/// </summary>
|
||||
private static (string? tag, string? url) GetRemoteVersionAndUrl()
|
||||
{
|
||||
#if NET20 || NET35 || NET40
|
||||
// Not supported in .NET Frameworks 2.0, 3.5, or 4.0
|
||||
return (null, null);
|
||||
#else
|
||||
using var hc = new System.Net.Http.HttpClient();
|
||||
#if NET452
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
|
||||
#endif
|
||||
|
||||
// TODO: Figure out a better way than having this hardcoded...
|
||||
string url = "https://api.github.com/repos/SabreTools/MPF/releases/latest";
|
||||
var message = new System.Net.Http.HttpRequestMessage(System.Net.Http.HttpMethod.Get, url);
|
||||
message.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0");
|
||||
var latestReleaseJsonString = hc.Send(message)?.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
var latestReleaseJsonString = hc.SendAsync(message)?.ConfigureAwait(false).GetAwaiter().GetResult()
|
||||
.Content?.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
if (latestReleaseJsonString == null)
|
||||
return (null, null);
|
||||
|
||||
@@ -248,6 +288,357 @@ namespace MPF.Core.Utilities
|
||||
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
|
||||
|
||||
@@ -15,12 +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,
|
||||
};
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Check that every supported DriveType maps to an InternalDriveType
|
||||
@@ -49,9 +49,9 @@ namespace MPF.Test.Core.Converters
|
||||
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;
|
||||
@@ -84,7 +84,7 @@ namespace MPF.Test.Core.Converters
|
||||
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;
|
||||
|
||||
@@ -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
|
||||
@@ -149,9 +150,9 @@ namespace MPF.Test.Core.Utilities
|
||||
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;
|
||||
@@ -167,9 +168,9 @@ namespace MPF.Test.Core.Utilities
|
||||
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;
|
||||
@@ -185,9 +186,9 @@ namespace MPF.Test.Core.Utilities
|
||||
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;
|
||||
@@ -203,9 +204,9 @@ namespace MPF.Test.Core.Utilities
|
||||
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;
|
||||
@@ -221,9 +222,9 @@ namespace MPF.Test.Core.Utilities
|
||||
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,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using MPF.Core;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
@@ -8,50 +9,9 @@ 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
|
||||
var 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")]
|
||||
@@ -60,7 +20,7 @@ namespace MPF.Test.Library
|
||||
[InlineData("superhero\\blah&foo.bin", "superhero\\blah&foo.bin")]
|
||||
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);
|
||||
@@ -90,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);
|
||||
@@ -117,7 +77,7 @@ namespace MPF.Test.Library
|
||||
};
|
||||
|
||||
// Process the special fields
|
||||
InfoTool.ProcessSpecialFields(info);
|
||||
Formatter.ProcessSpecialFields(info);
|
||||
|
||||
// Validate
|
||||
Assert.Null(info.CommonDiscInfo);
|
||||
@@ -146,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);
|
||||
@@ -180,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);
|
||||
|
||||
@@ -11,11 +11,11 @@ namespace MPF.Test.Library
|
||||
[Fact]
|
||||
public void SanitizeFoundProtectionsActiveMARKTest()
|
||||
{
|
||||
List<string> protections = new()
|
||||
{
|
||||
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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> 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> protections =
|
||||
[
|
||||
"XCP",
|
||||
"XCP v1.2.0",
|
||||
};
|
||||
];
|
||||
|
||||
string sanitized = Protection.SanitizeFoundProtections(protections);
|
||||
Assert.Equal("XCP v1.2.0", sanitized);
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0-windows;net8.0-windows</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -11,22 +14,22 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.9.0-preview-23531-01" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0-preview-23531-01" />
|
||||
<PackageReference Include="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.2.0" />
|
||||
<PackageReference Include="xunit" Version="2.6.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.5.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.6.1" />
|
||||
<PackageReference Include="xunit.core" Version="2.6.1" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.6.1" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.6.1" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.6.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.3">
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -269,7 +269,7 @@ namespace MPF.Test.RedumpLib
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
var code = language.TwoLetterCode();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
@@ -296,7 +296,7 @@ namespace MPF.Test.RedumpLib
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
var code = language.ThreeLetterCode();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
@@ -323,7 +323,7 @@ namespace MPF.Test.RedumpLib
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
var code = language.ThreeLetterCodeAlt();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
@@ -492,7 +492,7 @@ namespace MPF.Test.RedumpLib
|
||||
foreach (Region? region in fullRegions)
|
||||
{
|
||||
var code = region.ShortName();
|
||||
if (string.IsNullOrWhiteSpace(code))
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
|
||||
@@ -16,7 +16,12 @@ namespace MPF.UI.Core
|
||||
public static DoubleCollection SpeedsForDVDAsCollection { get; } = GetDoubleCollectionFromIntList(DVD);
|
||||
public static DoubleCollection SpeedsForHDDVDAsCollection { get; } = GetDoubleCollectionFromIntList(HDDVD);
|
||||
public static DoubleCollection SpeedsForBDAsCollection { get; } = GetDoubleCollectionFromIntList(BD);
|
||||
|
||||
#if NET20 || NET35 || NET40
|
||||
private static DoubleCollection GetDoubleCollectionFromIntList(IList<int> list)
|
||||
#else
|
||||
private static DoubleCollection GetDoubleCollectionFromIntList(IReadOnlyList<int> list)
|
||||
#endif
|
||||
=> new(list.Select(i => Convert.ToDouble(i)).ToList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -28,11 +32,19 @@ namespace WPFCustomMessageBox
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,11 +52,19 @@ namespace WPFCustomMessageBox
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,11 +72,19 @@ namespace WPFCustomMessageBox
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,11 +92,19 @@ namespace WPFCustomMessageBox
|
||||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,19 +112,59 @@ namespace WPFCustomMessageBox
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET35
|
||||
return _Label_No!.Content.ToString();
|
||||
#else
|
||||
return Label_No.Content.ToString();
|
||||
#endif
|
||||
}
|
||||
set
|
||||
{
|
||||
#if NET35
|
||||
_Label_No!.Content = value.TryAddKeyboardAccellerator();
|
||||
#else
|
||||
Label_No.Content = value.TryAddKeyboardAccellerator();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public MessageBoxResult Result { get; set; }
|
||||
|
||||
#if NET35
|
||||
|
||||
private Button? _Button_Cancel => ItemHelper.FindChild<Button>(this, "Button_Cancel");
|
||||
private Button? _Button_No => ItemHelper.FindChild<Button>(this, "Button_No");
|
||||
private Button? _Button_OK => ItemHelper.FindChild<Button>(this, "Button_OK");
|
||||
private Button? _Button_Yes => ItemHelper.FindChild<Button>(this, "Button_Yes");
|
||||
private System.Windows.Controls.Image? _Image_MessageBox => ItemHelper.FindChild<System.Windows.Controls.Image>(this, "Image_MessageBox");
|
||||
private Label? _Label_Cancel => ItemHelper.FindChild<Label>(this, "Label_Cancel");
|
||||
private Label? _Label_No => ItemHelper.FindChild<Label>(this, "Label_No");
|
||||
private Label? _Label_Ok => ItemHelper.FindChild<Label>(this, "Label_Ok");
|
||||
private Label? _Label_Yes => ItemHelper.FindChild<Label>(this, "Label_Yes");
|
||||
private TextBlock? _TextBlock_Message => ItemHelper.FindChild<TextBlock>(this, "TextBlock_Message");
|
||||
|
||||
#endif
|
||||
|
||||
internal CustomMessageBoxWindow(Window? owner, string? message, string? caption = null, MessageBoxButton? button = null, MessageBoxImage? image = null, bool removeTitleBarIcon = true)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
System.Windows.Media.TextOptions.SetTextFormattingMode(this, System.Windows.Media.TextFormattingMode.Display);
|
||||
System.Windows.Media.TextOptions.SetTextRenderingMode(this, System.Windows.Media.TextRenderingMode.ClearType);
|
||||
UseLayoutRounding = true;
|
||||
#endif
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
var chrome = new System.Windows.Shell.WindowChrome
|
||||
{
|
||||
CaptionHeight = 0,
|
||||
ResizeBorderThickness = new Thickness(0),
|
||||
};
|
||||
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
|
||||
#endif
|
||||
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
_removeTitleBarIcon = removeTitleBarIcon;
|
||||
Focusable = true;
|
||||
@@ -109,7 +185,11 @@ namespace WPFCustomMessageBox
|
||||
if (image.HasValue)
|
||||
DisplayImage(image.Value);
|
||||
else
|
||||
#if NET35
|
||||
_Image_MessageBox!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
Image_MessageBox.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
}
|
||||
|
||||
protected override void OnSourceInitialized(EventArgs e)
|
||||
@@ -126,39 +206,75 @@ namespace WPFCustomMessageBox
|
||||
{
|
||||
case MessageBoxButton.OKCancel:
|
||||
// Hide all but OK, Cancel
|
||||
#if NET35
|
||||
_Button_OK!.Visibility = Visibility.Visible;
|
||||
_Button_OK.Focus();
|
||||
_Button_Cancel!.Visibility = Visibility.Visible;
|
||||
|
||||
_Button_Yes!.Visibility = Visibility.Collapsed;
|
||||
_Button_No!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
Button_OK.Visibility = Visibility.Visible;
|
||||
Button_OK.Focus();
|
||||
Button_Cancel.Visibility = Visibility.Visible;
|
||||
|
||||
Button_Yes.Visibility = Visibility.Collapsed;
|
||||
Button_No.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
break;
|
||||
case MessageBoxButton.YesNo:
|
||||
// Hide all but Yes, No
|
||||
#if NET35
|
||||
_Button_Yes!.Visibility = Visibility.Visible;
|
||||
_Button_Yes.Focus();
|
||||
_Button_No!.Visibility = Visibility.Visible;
|
||||
|
||||
_Button_OK!.Visibility = Visibility.Collapsed;
|
||||
_Button_Cancel!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
Button_Yes.Visibility = Visibility.Visible;
|
||||
Button_Yes.Focus();
|
||||
Button_No.Visibility = Visibility.Visible;
|
||||
|
||||
Button_OK.Visibility = Visibility.Collapsed;
|
||||
Button_Cancel.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
break;
|
||||
case MessageBoxButton.YesNoCancel:
|
||||
// Hide only OK
|
||||
#if NET35
|
||||
_Button_Yes!.Visibility = Visibility.Visible;
|
||||
_Button_Yes.Focus();
|
||||
_Button_No!.Visibility = Visibility.Visible;
|
||||
_Button_Cancel!.Visibility = Visibility.Visible;
|
||||
|
||||
_Button_OK!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
Button_Yes.Visibility = Visibility.Visible;
|
||||
Button_Yes.Focus();
|
||||
Button_No.Visibility = Visibility.Visible;
|
||||
Button_Cancel.Visibility = Visibility.Visible;
|
||||
|
||||
Button_OK.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
// Hide all but OK
|
||||
#if NET35
|
||||
_Button_OK!.Visibility = Visibility.Visible;
|
||||
_Button_OK.Focus();
|
||||
|
||||
_Button_Yes!.Visibility = Visibility.Collapsed;
|
||||
_Button_No!.Visibility = Visibility.Collapsed;
|
||||
_Button_Cancel!.Visibility = Visibility.Collapsed;
|
||||
#else
|
||||
Button_OK.Visibility = Visibility.Visible;
|
||||
Button_OK.Focus();
|
||||
|
||||
Button_Yes.Visibility = Visibility.Collapsed;
|
||||
Button_No.Visibility = Visibility.Collapsed;
|
||||
Button_Cancel.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -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 = Visibility.Visible;
|
||||
#endif
|
||||
}
|
||||
|
||||
private void Button_OK_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
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,14 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0-windows;net8.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>
|
||||
<VersionPrefix>3.1.3</VersionPrefix>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>3.0.0</VersionPrefix>
|
||||
<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>
|
||||
@@ -20,8 +34,16 @@
|
||||
<ProjectReference Include="..\MPF.Core\MPF.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net3`))">
|
||||
<Reference Include="PresentationBuildTasks" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationBuildTasks.dll" />
|
||||
<Reference Include="PresentationCore" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll" />
|
||||
<Reference Include="PresentationFramework" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll" />
|
||||
<Reference Include="WindowsBase" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.2.0" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,6 +7,8 @@ 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
|
||||
@@ -31,9 +33,20 @@ namespace MPF.UI.Core.UserControls
|
||||
/// </summary>
|
||||
private Run? lastLine = null;
|
||||
|
||||
#if NET35
|
||||
|
||||
private Button? _ClearButton => ItemHelper.FindChild<Button>(this, "ClearButton");
|
||||
private RichTextBox? _Output => ItemHelper.FindChild<RichTextBox>(this, "Output");
|
||||
private ScrollViewer? _OutputViewer => ItemHelper.FindChild<ScrollViewer>(this, "OutputViewer");
|
||||
private Button? _SaveButton => ItemHelper.FindChild<Button>(this, "SaveButton");
|
||||
|
||||
#endif
|
||||
|
||||
public LogOutput()
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
// Update the internal state
|
||||
Document = new FlowDocument()
|
||||
@@ -47,13 +60,24 @@ namespace MPF.UI.Core.UserControls
|
||||
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
|
||||
@@ -93,18 +117,13 @@ namespace MPF.UI.Core.UserControls
|
||||
/// <returns>Brush representing the color</returns>
|
||||
public Brush GetForegroundColor()
|
||||
{
|
||||
switch (this.LogLevel)
|
||||
return this.LogLevel switch
|
||||
{
|
||||
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;
|
||||
}
|
||||
LogLevel.SECRET => Brushes.Blue,
|
||||
LogLevel.ERROR => Brushes.Red,
|
||||
LogLevel.VERBOSE => Brushes.Yellow,
|
||||
_ => Brushes.White,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -170,7 +189,7 @@ namespace MPF.UI.Core.UserControls
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
@@ -184,20 +203,22 @@ namespace MPF.UI.Core.UserControls
|
||||
/// </summary>
|
||||
private void SaveInlines()
|
||||
{
|
||||
using (var sw = new StreamWriter(File.OpenWrite("console.log")))
|
||||
using var sw = new StreamWriter(File.OpenWrite("console.log"));
|
||||
foreach (var inline in _paragraph.Inlines)
|
||||
{
|
||||
foreach (var inline in _paragraph.Inlines)
|
||||
{
|
||||
if (inline is Run run)
|
||||
sw.Write(run.Text);
|
||||
}
|
||||
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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -15,10 +15,7 @@
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical" Width="500" MaxHeight="650">
|
||||
<Grid Margin="0,10,0,0">
|
||||
@@ -34,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}}"
|
||||
@@ -175,6 +172,10 @@
|
||||
<controls:UserInput x:Name="CommentsTextBox" Label="Comments"
|
||||
Text="{Binding SubmissionInfo.CommonDiscInfo.Comments, Mode=TwoWay}" TextHeight="50"
|
||||
Enter="True" TextWrapping="Wrap" VerticalContentAlignmentValue="Top" />
|
||||
<controls:UserInput x:Name="DiscKeyTextBox" Label="Disc Key" Visibility="Collapsed"
|
||||
Text="{Binding Path=SubmissionInfo.Extras.DiscKey, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="DiscIDTextBox" Label="Disc ID" Visibility="Collapsed"
|
||||
Text="{Binding Path=SubmissionInfo.Extras.DiscID, Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="GenreTextBox" Label="Genre"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)Genre], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="ProtectionTextBox" Label="Protection" ToolTip="CAUTION: Only edit if you know what you are doing!"
|
||||
@@ -398,6 +399,23 @@
|
||||
<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="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"
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
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>
|
||||
@@ -12,6 +16,113 @@ 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>
|
||||
@@ -22,7 +133,19 @@ namespace MPF.UI.Core.Windows
|
||||
/// </summary>
|
||||
public DiscInformationWindow(Options options, SubmissionInfo? submissionInfo)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
var chrome = new System.Windows.Shell.WindowChrome
|
||||
{
|
||||
CaptionHeight = 0,
|
||||
ResizeBorderThickness = new Thickness(0),
|
||||
};
|
||||
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
|
||||
#endif
|
||||
|
||||
DataContext = new DiscInformationViewModel(options, submissionInfo);
|
||||
DiscInformationViewModel.Load();
|
||||
|
||||
@@ -34,9 +157,15 @@ namespace MPF.UI.Core.Windows
|
||||
}
|
||||
|
||||
// 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);
|
||||
@@ -70,9 +199,26 @@ namespace MPF.UI.Core.Windows
|
||||
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;
|
||||
@@ -84,30 +230,59 @@ namespace MPF.UI.Core.Windows
|
||||
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>
|
||||
@@ -121,12 +296,102 @@ namespace MPF.UI.Core.Windows
|
||||
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);
|
||||
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)
|
||||
@@ -135,13 +400,13 @@ namespace MPF.UI.Core.Windows
|
||||
DMIHash.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.EDC?.EDC == null)
|
||||
EDC.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.CommonDiscInfo?.ErrorsCount))
|
||||
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.ErrorsCount))
|
||||
ErrorsCount.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
|
||||
if (string.IsNullOrEmpty(submissionInfo.CommonDiscInfo?.EXEDateBuildDate))
|
||||
EXEDateBuildDate.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.Filename) != true)
|
||||
Filename.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.Header))
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.Header))
|
||||
Header.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.InternalName) != true)
|
||||
InternalName.Visibility = Visibility.Collapsed;
|
||||
@@ -151,21 +416,21 @@ namespace MPF.UI.Core.Windows
|
||||
Multisession.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CopyProtection?.LibCrypt == null)
|
||||
LibCrypt.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.CopyProtection?.LibCryptData))
|
||||
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.LibCryptData))
|
||||
LibCryptData.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.PFIHash) != true)
|
||||
PFIHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.PIC))
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.PIC))
|
||||
PIC.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.PVD))
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.PVD))
|
||||
PVD.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.RingNonZeroDataStart) != true)
|
||||
RingNonZeroDataStart.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.CopyProtection?.SecuROMData))
|
||||
if (string.IsNullOrEmpty(submissionInfo.CopyProtection?.SecuROMData))
|
||||
SecuROMData.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSHash) != true)
|
||||
SSHash.Visibility = Visibility.Collapsed;
|
||||
if (string.IsNullOrWhiteSpace(submissionInfo.Extras?.SecuritySectorRanges))
|
||||
if (string.IsNullOrEmpty(submissionInfo.Extras?.SecuritySectorRanges))
|
||||
SecuritySectorRanges.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.SSVersion) != true)
|
||||
SSVersion.Visibility = Visibility.Collapsed;
|
||||
@@ -177,6 +442,7 @@ namespace MPF.UI.Core.Windows
|
||||
XeMID.Visibility = Visibility.Collapsed;
|
||||
if (submissionInfo.CommonDiscInfo?.CommentsSpecialFields?.ContainsKey(SiteCode.XMID) != true)
|
||||
XMID.Visibility = Visibility.Collapsed;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -193,19 +459,37 @@ namespace MPF.UI.Core.Windows
|
||||
{
|
||||
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:
|
||||
@@ -222,106 +506,217 @@ namespace MPF.UI.Core.Windows
|
||||
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.Label = "Mastering Ring";
|
||||
L1MasteringSID.Label = "Mastering SID";
|
||||
L1Toolstamp.Label = "Toolstamp/Mastering Code";
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -335,13 +730,35 @@ namespace MPF.UI.Core.Windows
|
||||
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
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
@@ -52,6 +49,16 @@
|
||||
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}}"
|
||||
@@ -79,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}}"
|
||||
|
||||
@@ -2,12 +2,15 @@ using System;
|
||||
using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using MPF.Core;
|
||||
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
|
||||
{
|
||||
public partial class MainWindow : WindowBase
|
||||
@@ -17,10 +20,68 @@ namespace MPF.UI.Core.Windows
|
||||
/// </summary>
|
||||
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
|
||||
@@ -40,9 +101,17 @@ namespace MPF.UI.Core.Windows
|
||||
|
||||
// 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();
|
||||
@@ -67,29 +136,62 @@ namespace MPF.UI.Core.Windows
|
||||
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>
|
||||
@@ -99,12 +201,12 @@ namespace MPF.UI.Core.Windows
|
||||
{
|
||||
// Get the current path, if possible
|
||||
string currentPath = MainViewModel.OutputPath;
|
||||
if (string.IsNullOrWhiteSpace(currentPath) && !string.IsNullOrWhiteSpace(MainViewModel.Options.DefaultOutputPath))
|
||||
if (string.IsNullOrEmpty(currentPath) && !string.IsNullOrEmpty(MainViewModel.Options.DefaultOutputPath))
|
||||
currentPath = Path.Combine(MainViewModel.Options.DefaultOutputPath, "track.bin");
|
||||
else if (string.IsNullOrWhiteSpace(currentPath))
|
||||
else if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = "track.bin";
|
||||
if (string.IsNullOrWhiteSpace(currentPath))
|
||||
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "track.bin");
|
||||
if (string.IsNullOrEmpty(currentPath))
|
||||
currentPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory!, "track.bin");
|
||||
|
||||
// Get the full path
|
||||
currentPath = Path.GetFullPath(currentPath);
|
||||
@@ -137,7 +239,7 @@ namespace MPF.UI.Core.Windows
|
||||
(bool different, string message, var url) = MainViewModel.CheckForUpdates();
|
||||
|
||||
// If we have a new version, put it in the clipboard
|
||||
if (different)
|
||||
if (different && !string.IsNullOrEmpty(url))
|
||||
Clipboard.SetText(url);
|
||||
|
||||
if (showIfSame || different)
|
||||
@@ -185,7 +287,7 @@ namespace MPF.UI.Core.Windows
|
||||
{
|
||||
var submissionInfo = MainViewModel.CreateDebugSubmissionInfo();
|
||||
var result = ShowDiscInformationWindow(submissionInfo);
|
||||
InfoTool.ProcessSpecialFields(result.Item2);
|
||||
Formatter.ProcessSpecialFields(result.Item2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -217,6 +319,56 @@ namespace MPF.UI.Core.Windows
|
||||
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>
|
||||
@@ -251,7 +403,7 @@ namespace MPF.UI.Core.Windows
|
||||
theme.Apply();
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
@@ -290,6 +442,18 @@ namespace MPF.UI.Core.Windows
|
||||
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>
|
||||
@@ -315,9 +479,17 @@ namespace MPF.UI.Core.Windows
|
||||
/// <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)
|
||||
{
|
||||
|
||||
@@ -5,17 +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"
|
||||
Width="515.132" WindowStyle="None"
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<Window.DataContext>
|
||||
<viewModels:OptionsViewModel/>
|
||||
</Window.DataContext>
|
||||
<Window.Resources>
|
||||
<core:ElementConverter x:Key="ElementConverter" />
|
||||
</Window.Resources>
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
@@ -30,14 +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>
|
||||
<TextBlock.Inlines>
|
||||
<Run FontWeight="Bold" Text="{Binding Title, Mode=OneWay}"/>
|
||||
</TextBlock.Inlines>
|
||||
</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}}"
|
||||
@@ -186,7 +181,8 @@
|
||||
|
||||
<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" />
|
||||
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>
|
||||
@@ -195,7 +191,7 @@
|
||||
<TabItem Header="Dumping" Style="{DynamicResource CustomTabItemStyle}">
|
||||
<StackPanel>
|
||||
<GroupBox Margin="5,5,5,5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Header="Dumping">
|
||||
<UniformGrid Columns="2" Rows="7">
|
||||
<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"
|
||||
@@ -265,11 +261,16 @@
|
||||
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="2">
|
||||
<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"
|
||||
@@ -289,6 +290,11 @@
|
||||
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>
|
||||
|
||||
@@ -311,28 +317,28 @@
|
||||
<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>
|
||||
@@ -469,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>
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Forms;
|
||||
using MPF.Core.Data;
|
||||
using MPF.Core.UI.ViewModels;
|
||||
using WPFCustomMessageBox;
|
||||
|
||||
#pragma warning disable IDE1006 // Naming Styles
|
||||
|
||||
namespace MPF.UI.Core.Windows
|
||||
{
|
||||
/// <summary>
|
||||
@@ -19,18 +22,65 @@ namespace MPF.UI.Core.Windows
|
||||
/// </summary>
|
||||
public OptionsViewModel OptionsViewModel => DataContext as OptionsViewModel ?? new OptionsViewModel(new Options());
|
||||
|
||||
#if NET35
|
||||
|
||||
private System.Windows.Controls.Button? _AaruPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "AaruPathButton");
|
||||
private System.Windows.Controls.Button? _AcceptButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "AcceptButton");
|
||||
private System.Windows.Controls.Button? _CancelButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "CancelButton");
|
||||
private System.Windows.Controls.Button? _DefaultOutputPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "DefaultOutputPathButton");
|
||||
private System.Windows.Controls.Button? _DiscImageCreatorPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "DiscImageCreatorPathButton");
|
||||
private System.Windows.Controls.Button? _RedumperPathButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "RedumperPathButton");
|
||||
private System.Windows.Controls.Button? _RedumpLoginTestButton => ItemHelper.FindChild<System.Windows.Controls.Button>(this, "RedumpLoginTestButton");
|
||||
private PasswordBox? _RedumpPasswordBox => ItemHelper.FindChild<PasswordBox>(this, "RedumpPasswordBox");
|
||||
private System.Windows.Controls.TextBox? _RedumpUsernameTextBox => ItemHelper.FindChild<System.Windows.Controls.TextBox>(this, "RedumpUsernameTextBox");
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
public OptionsWindow(Options options)
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
DumpSpeedCDTextBox.IsReadOnlyCaretVisible = false;
|
||||
DumpSpeedDVDTextBox.IsReadOnlyCaretVisible = false;
|
||||
DumpSpeedHDDVDTextBox.IsReadOnlyCaretVisible = false;
|
||||
DumpSpeedBDTextBox.IsReadOnlyCaretVisible = false;
|
||||
#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 OptionsViewModel(options);
|
||||
|
||||
// Set initial value for binding
|
||||
#if NET35
|
||||
_RedumpPasswordBox!.Password = options.RedumpPassword;
|
||||
#else
|
||||
RedumpPasswordBox.Password = options.RedumpPassword;
|
||||
#endif
|
||||
|
||||
// Add handlers
|
||||
#if NET35
|
||||
_AaruPathButton!.Click += BrowseForPathClick;
|
||||
_DiscImageCreatorPathButton!.Click += BrowseForPathClick;
|
||||
_RedumperPathButton!.Click += BrowseForPathClick;
|
||||
_DefaultOutputPathButton!.Click += BrowseForPathClick;
|
||||
|
||||
_AcceptButton!.Click += OnAcceptClick;
|
||||
_CancelButton!.Click += OnCancelClick;
|
||||
_RedumpPasswordBox!.PasswordChanged += OnPasswordChanged;
|
||||
_RedumpLoginTestButton!.Click += OnRedumpTestClick;
|
||||
#else
|
||||
AaruPathButton.Click += BrowseForPathClick;
|
||||
DiscImageCreatorPathButton.Click += BrowseForPathClick;
|
||||
RedumperPathButton.Click += BrowseForPathClick;
|
||||
@@ -40,6 +90,7 @@ namespace MPF.UI.Core.Windows
|
||||
CancelButton.Click += OnCancelClick;
|
||||
RedumpPasswordBox.PasswordChanged += OnPasswordChanged;
|
||||
RedumpLoginTestButton.Click += OnRedumpTestClick;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -65,7 +116,7 @@ namespace MPF.UI.Core.Windows
|
||||
return;
|
||||
|
||||
// Strips button prefix to obtain the setting name
|
||||
string pathSettingName = button.Name[..button.Name.IndexOf("Button")];
|
||||
string pathSettingName = button.Name.Substring(0, button.Name.IndexOf("Button"));
|
||||
|
||||
// TODO: hack for now, then we'll see
|
||||
bool shouldBrowseForPath = pathSettingName == "DefaultOutputPath";
|
||||
@@ -149,7 +200,11 @@ namespace MPF.UI.Core.Windows
|
||||
/// </summary>
|
||||
private async Task ValidateRedumpCredentials()
|
||||
{
|
||||
(bool? success, string? message) = await MPF.Core.UI.ViewModels.OptionsViewModel.TestRedumpLogin(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password);
|
||||
#if NET35
|
||||
(bool? success, string? message) = await OptionsViewModel.TestRedumpLogin(_RedumpUsernameTextBox!.Text, _RedumpPasswordBox!.Password);
|
||||
#else
|
||||
(bool? success, string? message) = await OptionsViewModel.TestRedumpLogin(RedumpUsernameTextBox.Text, RedumpPasswordBox.Password);
|
||||
#endif
|
||||
|
||||
if (success == true)
|
||||
CustomMessageBox.Show(this, message, "Success", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
@@ -192,14 +247,26 @@ namespace MPF.UI.Core.Windows
|
||||
/// </summary>
|
||||
private void OnPasswordChanged(object sender, EventArgs e)
|
||||
{
|
||||
#if NET35
|
||||
OptionsViewModel.Options.RedumpPassword = _RedumpPasswordBox!.Password;
|
||||
#else
|
||||
OptionsViewModel.Options.RedumpPassword = RedumpPasswordBox.Password;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test Redump credentials for validity
|
||||
/// </summary>
|
||||
#if NET40
|
||||
private void OnRedumpTestClick(object sender, EventArgs e)
|
||||
{
|
||||
var validateTask = ValidateRedumpCredentials();
|
||||
validateTask.Wait();
|
||||
}
|
||||
#else
|
||||
private async void OnRedumpTestClick(object sender, EventArgs e) => await ValidateRedumpCredentials();
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,6 @@
|
||||
WindowStartupLocation="CenterOwner" ResizeMode="CanMinimize" SizeToContent="Height"
|
||||
BorderBrush="DarkGray" BorderThickness="2">
|
||||
|
||||
<WindowChrome.WindowChrome>
|
||||
<WindowChrome CaptionHeight="0" ResizeBorderThickness="0" />
|
||||
</WindowChrome.WindowChrome>
|
||||
|
||||
<Grid>
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Grid Margin="0,10,0,0">
|
||||
@@ -29,7 +25,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>Ring Code Guide</Bold></TextBlock>
|
||||
<Run FontWeight="Bold" BaselineAlignment="Center" Text="Ring Code Guide" />
|
||||
</Label.Content>
|
||||
<Label.ContextMenu>
|
||||
<ContextMenu Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"
|
||||
@@ -85,22 +81,34 @@
|
||||
RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<Label Grid.Row="1">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Red">1. Mastering Ring:</Bold> Sony DADC<tab>A0100368905-0101<tab>13</TextBlock>
|
||||
<TextBlock>
|
||||
<Run FontWeight="Bold" Foreground="Red" Text="1. Mastering Ring:" />
|
||||
<Run Text=" Sony DADC<tab>A0100368905-0101<tab>13" />
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="2">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Green">2. Mastering SID:</Bold> IFPI L553</TextBlock>
|
||||
<TextBlock>
|
||||
<Run FontWeight="Bold" Foreground="Green" Text="2. Mastering SID:" />
|
||||
<Run Text=" IFPI L553" />
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="3">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Purple">3. Toolstamp/Mastering Code:</Bold> A2</TextBlock>
|
||||
<TextBlock>
|
||||
<Run FontWeight="Bold" Foreground="Purple" Text="3. Toolstamp/Mastering Code:" />
|
||||
<Run Text=" A2" />
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="4">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="LightBlue">4. Mould SID:</Bold> IFPI 94V1</TextBlock>
|
||||
<TextBlock>
|
||||
<Run FontWeight="Bold" Foreground="LightBlue" Text="4. Mould SID:" />
|
||||
<Run Text=" IFPI 94V1" />
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
</Grid>
|
||||
@@ -126,27 +134,42 @@
|
||||
RenderOptions.BitmapScalingMode="HighQuality" />
|
||||
<Label Grid.Row="1">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Red">1. Outer Mastering Ring:</Bold> IM01501A-L1<tab>03 +<tab>+</TextBlock>
|
||||
<TextBlock>
|
||||
<Run FontWeight="Bold" Foreground="Red" Text="1. Outer Mastering Ring:" />
|
||||
<Run Text=" IM01501A-L1<tab>03 +<tab>+" />
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="2">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Green">2. Inner Mastering Ring:</Bold> IM01501A-L0<tab>01 +<tab>+</TextBlock>
|
||||
<TextBlock>
|
||||
<Run FontWeight="Bold" Foreground="Green" Text="2. Inner Mastering Ring:" />
|
||||
<Run Text=" IM01501A-L0<tab>01 +<tab>+" />
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="3">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="Purple">3. Outer Mastering SID:</Bold> IFPI LB48</TextBlock>
|
||||
<TextBlock>
|
||||
<Run FontWeight="Bold" Foreground="Purple" Text="3. Outer Mastering SID:" />
|
||||
<Run Text=" IFPI LB48" />
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="4">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold Foreground="LightBlue">4. Inner Mastering SID:</Bold> IFPI LB48</TextBlock>
|
||||
<TextBlock>
|
||||
<Run FontWeight="Bold" Foreground="LightBlue" Text="4. Inner Mastering SID:" />
|
||||
<Run Text=" IFPI LB48" />
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
<Label Grid.Row="5">
|
||||
<Label.Content>
|
||||
<TextBlock><Bold>Note:</Bold> See the 1-Layer guide for more details on additional fields</TextBlock>
|
||||
<TextBlock>
|
||||
<Run FontWeight="Bold" Text="Note:" />
|
||||
<Run Text=" See the 1-Layer guide for more details on additional fields" />
|
||||
</TextBlock>
|
||||
</Label.Content>
|
||||
</Label>
|
||||
</Grid>
|
||||
|
||||
@@ -5,6 +5,20 @@
|
||||
/// </summary>
|
||||
public partial class RingCodeGuideWindow : WindowBase
|
||||
{
|
||||
public RingCodeGuideWindow() => InitializeComponent();
|
||||
public RingCodeGuideWindow()
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
var chrome = new System.Windows.Shell.WindowChrome
|
||||
{
|
||||
CaptionHeight = 0,
|
||||
ResizeBorderThickness = new System.Windows.Thickness(0),
|
||||
};
|
||||
System.Windows.Shell.WindowChrome.SetWindowChrome(this, chrome);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
MPF.sln
20
MPF.sln
@@ -13,11 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
appveyor.yml = appveyor.yml
|
||||
CHANGELIST.md = CHANGELIST.md
|
||||
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
|
||||
.github\ISSUE_TEMPLATE\informational.md = .github\ISSUE_TEMPLATE\informational.md
|
||||
.github\ISSUE_TEMPLATE\issue-report.md = .github\ISSUE_TEMPLATE\issue-report.md
|
||||
publish-nix.sh = publish-nix.sh
|
||||
publish-win.bat = publish-win.bat
|
||||
publish-win.ps1 = publish-win.ps1
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
@@ -27,6 +24,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.UI.Core", "MPF.UI.Core\
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MPF", "MPF", "{4160167D-681D-480B-ABC6-06AC869E5769}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEMPLATE", "{ADC5FDD7-F43F-4F9C-B222-19AA1D64D3D4}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.github\ISSUE_TEMPLATE\feature-request.md = .github\ISSUE_TEMPLATE\feature-request.md
|
||||
.github\ISSUE_TEMPLATE\informational.md = .github\ISSUE_TEMPLATE\informational.md
|
||||
.github\ISSUE_TEMPLATE\issue-report.md = .github\ISSUE_TEMPLATE\issue-report.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{440776E0-E6E2-46C6-8E85-09E64A8FC7D6}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.github\workflows\build_check.yml = .github\workflows\build_check.yml
|
||||
.github\workflows\build_ui.yml = .github\workflows\build_ui.yml
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -63,6 +73,8 @@ Global
|
||||
{8CFDE289-E171-4D49-A40D-5293265C1253} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{70B1265D-FE49-472A-A83D-0B462152D37A} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{EA3768DB-694A-4653-82E4-9FF71B8963F3} = {4160167D-681D-480B-ABC6-06AC869E5769}
|
||||
{ADC5FDD7-F43F-4F9C-B222-19AA1D64D3D4} = {4D1DCF5A-F0B0-4E81-A05B-F1A7D37C9D9D}
|
||||
{440776E0-E6E2-46C6-8E85-09E64A8FC7D6} = {4D1DCF5A-F0B0-4E81-A05B-F1A7D37C9D9D}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {73C62E6A-6584-4D93-83B5-ECB1FBDB469B}
|
||||
|
||||
197
MPF/App.xaml
197
MPF/App.xaml
@@ -1,7 +1,6 @@
|
||||
<Application
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
|
||||
xmlns:windows="clr-namespace:MPF.UI.Core.Windows;assembly=MPF.UI.Core"
|
||||
x:Class="MPF.App">
|
||||
<Application.MainWindow>
|
||||
@@ -205,140 +204,7 @@
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
|
||||
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
|
||||
<Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
|
||||
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
|
||||
<ScrollViewer x:Name="DropDownScrollViewer">
|
||||
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
|
||||
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
|
||||
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
|
||||
</Canvas>
|
||||
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Themes:SystemDropShadowChrome>
|
||||
</Popup>
|
||||
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{DynamicResource ComboBoxToggleButton}"/>
|
||||
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
|
||||
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
|
||||
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
|
||||
</Trigger>
|
||||
<Trigger Property="HasItems" Value="false">
|
||||
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsGrouping" Value="true"/>
|
||||
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
|
||||
</MultiTrigger>
|
||||
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
|
||||
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
|
||||
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
<SolidColorBrush x:Key="TextBox.Static.Background" Color="#FFFFFFFF"/>
|
||||
<Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
|
||||
<Setter Property="OverridesDefaultStyle" Value="true"/>
|
||||
<Setter Property="AllowDrop" Value="true"/>
|
||||
<Setter Property="MinWidth" Value="0"/>
|
||||
<Setter Property="MinHeight" Value="0"/>
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
|
||||
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
|
||||
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type TextBox}">
|
||||
<ScrollViewer x:Name="PART_ContentHost" Background="Transparent" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
<ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type ComboBox}">
|
||||
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
|
||||
<Themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
|
||||
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
|
||||
<ScrollViewer x:Name="DropDownScrollViewer">
|
||||
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
|
||||
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
|
||||
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
|
||||
</Canvas>
|
||||
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Themes:SystemDropShadowChrome>
|
||||
</Popup>
|
||||
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{DynamicResource ComboBoxToggleButton}"/>
|
||||
<Border x:Name="border" Background="{DynamicResource TextBox.Static.Background}" Margin="{TemplateBinding BorderThickness}">
|
||||
<TextBox x:Name="PART_EditableTextBox" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" Style="{DynamicResource ComboBoxEditableTextBox}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsEnabled" Value="false">
|
||||
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsKeyboardFocusWithin" Value="true">
|
||||
<Setter Property="Foreground" Value="Black"/>
|
||||
</Trigger>
|
||||
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
|
||||
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
|
||||
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
|
||||
</Trigger>
|
||||
<Trigger Property="HasItems" Value="false">
|
||||
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property="IsGrouping" Value="true"/>
|
||||
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
|
||||
</MultiTrigger>
|
||||
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
|
||||
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
|
||||
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
<Style x:Key="CustomComboBoxStyle" TargetType="{x:Type ComboBox}">
|
||||
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource ComboBox.Static.Background}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ComboBox.Static.Border}"/>
|
||||
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
|
||||
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
|
||||
<Setter Property="Padding" Value="6,3,5,3"/>
|
||||
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
|
||||
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
|
||||
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
|
||||
<Setter Property="Template" Value="{DynamicResource ComboBoxTemplate}"/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property="IsEditable" Value="true">
|
||||
<Setter Property="IsTabStop" Value="false"/>
|
||||
<Setter Property="Padding" Value="2"/>
|
||||
<Setter Property="Template" Value="{DynamicResource ComboBoxEditableTemplate}"/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
<!-- ContextMenu -->
|
||||
<SolidColorBrush x:Key="ContextMenu.Static.Border" Color="#FF888888"/>
|
||||
@@ -384,7 +250,8 @@
|
||||
<Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" IsOpen="{Binding IsSubmenuOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.MenuPopupAnimationKey}}" Placement="Bottom">
|
||||
<Border x:Name="SubMenuBorder" BorderBrush="{DynamicResource MenuItem.SubMenu.Border}" BorderThickness="1" Background="{DynamicResource MenuItem.SubMenu.Background}" Padding="2">
|
||||
<ScrollViewer x:Name="SubMenuScrollViewer" Style="{DynamicResource {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}}">
|
||||
<Grid RenderOptions.ClearTypeHint="Enabled">
|
||||
<!--<Grid RenderOptions.ClearTypeHint="Enabled">--> <!-- Not supported in .NET Framework 3.5 -->
|
||||
<Grid> <!-- Not supported in .NET Framework 3.5 -->
|
||||
<Canvas HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
|
||||
<Rectangle x:Name="OpaqueRect" Fill="{Binding Background, ElementName=SubMenuBorder}" Height="{Binding ActualHeight, ElementName=SubMenuBorder}" Width="{Binding ActualWidth, ElementName=SubMenuBorder}"/>
|
||||
</Canvas>
|
||||
@@ -426,66 +293,6 @@
|
||||
<SolidColorBrush x:Key="ProgressBar.Progress" Color="#FF06B025"/>
|
||||
<SolidColorBrush x:Key="ProgressBar.Background" Color="#FFE6E6E6"/>
|
||||
<SolidColorBrush x:Key="ProgressBar.Border" Color="#FFBCBCBC"/>
|
||||
<Style x:Key="CustomProgressBarStyle" TargetType="{x:Type ProgressBar}">
|
||||
<Setter Property="Foreground" Value="{DynamicResource ProgressBar.Progress}"/>
|
||||
<Setter Property="Background" Value="{DynamicResource ProgressBar.Background}"/>
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource ProgressBar.Border}"/>
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type ProgressBar}">
|
||||
<Grid x:Name="TemplateRoot">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Determinate"/>
|
||||
<VisualState x:Name="Indeterminate">
|
||||
<Storyboard RepeatBehavior="Forever">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)" Storyboard.TargetName="Animation">
|
||||
<EasingDoubleKeyFrame KeyTime="0" Value="0.25"/>
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:1" Value="0.25"/>
|
||||
<EasingDoubleKeyFrame KeyTime="0:0:2" Value="0.25"/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<PointAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransformOrigin)" Storyboard.TargetName="Animation">
|
||||
<EasingPointKeyFrame KeyTime="0" Value="-0.5,0.5"/>
|
||||
<EasingPointKeyFrame KeyTime="0:0:1" Value="0.5,0.5"/>
|
||||
<EasingPointKeyFrame KeyTime="0:0:2" Value="1.5,0.5"/>
|
||||
</PointAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}"/>
|
||||
<Rectangle x:Name="PART_Track"/>
|
||||
<Grid x:Name="PART_Indicator" ClipToBounds="true" HorizontalAlignment="Left">
|
||||
<Rectangle x:Name="Indicator" Fill="{TemplateBinding Foreground}"/>
|
||||
<Rectangle x:Name="Animation" Fill="{TemplateBinding Foreground}" RenderTransformOrigin="0.5,0.5">
|
||||
<Rectangle.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform/>
|
||||
<SkewTransform/>
|
||||
<RotateTransform/>
|
||||
<TranslateTransform/>
|
||||
</TransformGroup>
|
||||
</Rectangle.RenderTransform>
|
||||
</Rectangle>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="Orientation" Value="Vertical">
|
||||
<Setter Property="LayoutTransform" TargetName="TemplateRoot">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle="-90"/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property="IsIndeterminate" Value="true">
|
||||
<Setter Property="Visibility" TargetName="Indicator" Value="Collapsed"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- ScrollViewer -->
|
||||
<SolidColorBrush x:Key="ScrollViewer.ScrollBar.Background" Color="LightGray"/>
|
||||
|
||||
493
MPF/App.xaml.cs
493
MPF/App.xaml.cs
@@ -1,7 +1,496 @@
|
||||
namespace MPF
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Markup;
|
||||
|
||||
namespace MPF
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : System.Windows.Application { }
|
||||
public partial class App : Application
|
||||
{
|
||||
|
||||
#if NET35_OR_GREATER || NETCOREAPP
|
||||
|
||||
#region ControlTemplates
|
||||
|
||||
/// <summary>
|
||||
/// ComboBoxTemplate ControlTemplate XAML (.NET Framework 4.0 and above)
|
||||
/// </summary>
|
||||
private const string _comboBoxTemplateDefault = @"<ControlTemplate TargetType=""{x:Type ComboBox}"">
|
||||
<Grid x:Name=""templateRoot"" SnapsToDevicePixels=""true"">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width=""*""/>
|
||||
<ColumnDefinition MinWidth=""{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"" Width=""0""/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Popup x:Name=""PART_Popup"" AllowsTransparency=""true"" Grid.ColumnSpan=""2"" IsOpen=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Margin=""1"" PopupAnimation=""{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"" Placement=""Bottom"">
|
||||
<themes:SystemDropShadowChrome x:Name=""shadow"" Color=""Transparent"" MaxHeight=""{TemplateBinding MaxDropDownHeight}"" MinWidth=""{Binding ActualWidth, ElementName=templateRoot}"">
|
||||
<Border x:Name=""dropDownBorder"" BorderBrush=""{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"" BorderThickness=""1"" Background=""{DynamicResource {x:Static SystemColors.WindowBrushKey}}"">
|
||||
<ScrollViewer x:Name=""DropDownScrollViewer"">
|
||||
<Grid x:Name=""grid"" RenderOptions.ClearTypeHint=""Enabled"">
|
||||
<Canvas x:Name=""canvas"" HorizontalAlignment=""Left"" Height=""0"" VerticalAlignment=""Top"" Width=""0"">
|
||||
<Rectangle x:Name=""opaqueRect"" Fill=""{Binding Background, ElementName=dropDownBorder}"" Height=""{Binding ActualHeight, ElementName=dropDownBorder}"" Width=""{Binding ActualWidth, ElementName=dropDownBorder}""/>
|
||||
</Canvas>
|
||||
<ItemsPresenter x:Name=""ItemsPresenter"" KeyboardNavigation.DirectionalNavigation=""Contained"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}""/>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</themes:SystemDropShadowChrome>
|
||||
</Popup>
|
||||
<ToggleButton x:Name=""toggleButton"" BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}"" Grid.ColumnSpan=""2"" IsChecked=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Style=""{DynamicResource ComboBoxToggleButton}""/>
|
||||
<ContentPresenter x:Name=""contentPresenter"" ContentTemplate=""{TemplateBinding SelectionBoxItemTemplate}"" ContentTemplateSelector=""{TemplateBinding ItemTemplateSelector}"" Content=""{TemplateBinding SelectionBoxItem}"" ContentStringFormat=""{TemplateBinding SelectionBoxItemStringFormat}"" HorizontalAlignment=""{TemplateBinding HorizontalContentAlignment}"" IsHitTestVisible=""false"" Margin=""{TemplateBinding Padding}"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}"" VerticalAlignment=""{TemplateBinding VerticalContentAlignment}""/>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property=""HasDropShadow"" SourceName=""PART_Popup"" Value=""true"">
|
||||
<Setter Property=""Margin"" TargetName=""shadow"" Value=""0,0,5,5""/>
|
||||
<Setter Property=""Color"" TargetName=""shadow"" Value=""#71000000""/>
|
||||
</Trigger>
|
||||
<Trigger Property=""HasItems"" Value=""false"">
|
||||
<Setter Property=""Height"" TargetName=""dropDownBorder"" Value=""95""/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property=""IsGrouping"" Value=""true""/>
|
||||
<Condition Property=""VirtualizingPanel.IsVirtualizingWhenGrouping"" Value=""false""/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""false""/>
|
||||
</MultiTrigger>
|
||||
<Trigger Property=""ScrollViewer.CanContentScroll"" SourceName=""DropDownScrollViewer"" Value=""false"">
|
||||
<Setter Property=""Canvas.Top"" TargetName=""opaqueRect"" Value=""{Binding VerticalOffset, ElementName=DropDownScrollViewer}""/>
|
||||
<Setter Property=""Canvas.Left"" TargetName=""opaqueRect"" Value=""{Binding HorizontalOffset, ElementName=DropDownScrollViewer}""/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>";
|
||||
|
||||
/// <summary>
|
||||
/// ComboBoxTemplate ControlTemplate XAML (.NET Framework 3.5)
|
||||
/// </summary>
|
||||
private const string _comboBoxTemplateNet35 = @"<ControlTemplate TargetType=""{x:Type ComboBox}"">
|
||||
<Grid x:Name=""templateRoot"" SnapsToDevicePixels=""true"">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width=""*""/>
|
||||
<ColumnDefinition MinWidth=""{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"" Width=""0""/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Popup x:Name=""PART_Popup"" AllowsTransparency=""true"" Grid.ColumnSpan=""2"" IsOpen=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Margin=""1"" PopupAnimation=""{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"" Placement=""Bottom"">
|
||||
<Border x:Name=""dropDownBorder"" BorderBrush=""{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"" BorderThickness=""1"" Background=""{DynamicResource {x:Static SystemColors.WindowBrushKey}}"">
|
||||
<ScrollViewer x:Name=""DropDownScrollViewer"">
|
||||
<Grid x:Name=""grid"">
|
||||
<Canvas x:Name=""canvas"" HorizontalAlignment=""Left"" Height=""0"" VerticalAlignment=""Top"" Width=""0"">
|
||||
<Rectangle x:Name=""opaqueRect"" Fill=""{Binding Background, ElementName=dropDownBorder}"" Height=""{Binding ActualHeight, ElementName=dropDownBorder}"" Width=""{Binding ActualWidth, ElementName=dropDownBorder}""/>
|
||||
</Canvas>
|
||||
<ItemsPresenter x:Name=""ItemsPresenter"" KeyboardNavigation.DirectionalNavigation=""Contained"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}""/>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Popup>
|
||||
<ToggleButton x:Name=""toggleButton"" BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}"" Grid.ColumnSpan=""2"" IsChecked=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Style=""{DynamicResource ComboBoxToggleButton}""/>
|
||||
<ContentPresenter x:Name=""contentPresenter"" ContentTemplate=""{TemplateBinding SelectionBoxItemTemplate}"" ContentTemplateSelector=""{TemplateBinding ItemTemplateSelector}"" Content=""{TemplateBinding SelectionBoxItem}"" ContentStringFormat=""{TemplateBinding SelectionBoxItemStringFormat}"" HorizontalAlignment=""{TemplateBinding HorizontalContentAlignment}"" IsHitTestVisible=""false"" Margin=""{TemplateBinding Padding}"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}"" VerticalAlignment=""{TemplateBinding VerticalContentAlignment}""/>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property=""HasItems"" Value=""false"">
|
||||
<Setter Property=""Height"" TargetName=""dropDownBorder"" Value=""95""/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property=""IsGrouping"" Value=""true""/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""false""/>
|
||||
</MultiTrigger>
|
||||
<Trigger Property=""ScrollViewer.CanContentScroll"" SourceName=""DropDownScrollViewer"" Value=""false"">
|
||||
<Setter Property=""Canvas.Top"" TargetName=""opaqueRect"" Value=""{Binding VerticalOffset, ElementName=DropDownScrollViewer}""/>
|
||||
<Setter Property=""Canvas.Left"" TargetName=""opaqueRect"" Value=""{Binding HorizontalOffset, ElementName=DropDownScrollViewer}""/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>";
|
||||
|
||||
/// <summary>
|
||||
/// ComboBoxEditableTemplate ControlTemplate XAML (.NET Framework 4.0 and above)
|
||||
/// </summary>
|
||||
private const string _comboBoxEditableTemplateDefault = @"<ControlTemplate TargetType=""{x:Type ComboBox}"">
|
||||
<Grid x:Name=""templateRoot"" SnapsToDevicePixels=""true"">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width=""*""/>
|
||||
<ColumnDefinition MinWidth=""{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"" Width=""0""/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Popup x:Name=""PART_Popup"" AllowsTransparency=""true"" Grid.ColumnSpan=""2"" IsOpen=""{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"" PopupAnimation=""{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"" Placement=""Bottom"">
|
||||
<themes:SystemDropShadowChrome x:Name=""shadow"" Color=""Transparent"" MaxHeight=""{TemplateBinding MaxDropDownHeight}"" MinWidth=""{Binding ActualWidth, ElementName=templateRoot}"">
|
||||
<Border x:Name=""dropDownBorder"" BorderBrush=""{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"" BorderThickness=""1"" Background=""{DynamicResource {x:Static SystemColors.WindowBrushKey}}"">
|
||||
<ScrollViewer x:Name=""DropDownScrollViewer"">
|
||||
<Grid x:Name=""grid"" RenderOptions.ClearTypeHint=""Enabled"">
|
||||
<Canvas x:Name=""canvas"" HorizontalAlignment=""Left"" Height=""0"" VerticalAlignment=""Top"" Width=""0"">
|
||||
<Rectangle x:Name=""opaqueRect"" Fill=""{Binding Background, ElementName=dropDownBorder}"" Height=""{Binding ActualHeight, ElementName=dropDownBorder}"" Width=""{Binding ActualWidth, ElementName=dropDownBorder}""/>
|
||||
</Canvas>
|
||||
<ItemsPresenter x:Name=""ItemsPresenter"" KeyboardNavigation.DirectionalNavigation=""Contained"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}""/>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</themes:SystemDropShadowChrome>
|
||||
</Popup>
|
||||
<ToggleButton x:Name=""toggleButton"" BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}"" Grid.ColumnSpan=""2"" IsChecked=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Style=""{DynamicResource ComboBoxToggleButton}""/>
|
||||
<Border x:Name=""border"" Background=""{DynamicResource TextBox.Static.Background}"" Margin=""{TemplateBinding BorderThickness}"">
|
||||
<TextBox x:Name=""PART_EditableTextBox"" HorizontalContentAlignment=""{TemplateBinding HorizontalContentAlignment}"" IsReadOnly=""{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"" Margin=""{TemplateBinding Padding}"" Style=""{DynamicResource ComboBoxEditableTextBox}"" VerticalContentAlignment=""{TemplateBinding VerticalContentAlignment}""/>
|
||||
</Border>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property=""IsEnabled"" Value=""false"">
|
||||
<Setter Property=""Opacity"" TargetName=""border"" Value=""0.56""/>
|
||||
</Trigger>
|
||||
<Trigger Property=""IsKeyboardFocusWithin"" Value=""true"">
|
||||
<Setter Property=""Foreground"" Value=""Black""/>
|
||||
</Trigger>
|
||||
<Trigger Property=""HasDropShadow"" SourceName=""PART_Popup"" Value=""true"">
|
||||
<Setter Property=""Margin"" TargetName=""shadow"" Value=""0,0,5,5""/>
|
||||
<Setter Property=""Color"" TargetName=""shadow"" Value=""#71000000""/>
|
||||
</Trigger>
|
||||
<Trigger Property=""HasItems"" Value=""false"">
|
||||
<Setter Property=""Height"" TargetName=""dropDownBorder"" Value=""95""/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property=""IsGrouping"" Value=""true""/>
|
||||
<Condition Property=""VirtualizingPanel.IsVirtualizingWhenGrouping"" Value=""false""/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""false""/>
|
||||
</MultiTrigger>
|
||||
<Trigger Property=""ScrollViewer.CanContentScroll"" SourceName=""DropDownScrollViewer"" Value=""false"">
|
||||
<Setter Property=""Canvas.Top"" TargetName=""opaqueRect"" Value=""{Binding VerticalOffset, ElementName=DropDownScrollViewer}""/>
|
||||
<Setter Property=""Canvas.Left"" TargetName=""opaqueRect"" Value=""{Binding HorizontalOffset, ElementName=DropDownScrollViewer}""/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>";
|
||||
|
||||
/// <summary>
|
||||
/// ComboBoxEditableTemplate ControlTemplate XAML (.NET Framework 3.5)
|
||||
/// </summary>
|
||||
private const string _comboBoxEditableTemplateNet35 = @"<ControlTemplate TargetType=""{x:Type ComboBox}"">
|
||||
<Grid x:Name=""templateRoot"" SnapsToDevicePixels=""true"">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width=""*""/>
|
||||
<ColumnDefinition MinWidth=""{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}"" Width=""0""/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Popup x:Name=""PART_Popup"" AllowsTransparency=""true"" Grid.ColumnSpan=""2"" IsOpen=""{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}"" PopupAnimation=""{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}"" Placement=""Bottom"">
|
||||
<Border x:Name=""dropDownBorder"" BorderBrush=""{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}"" BorderThickness=""1"" Background=""{DynamicResource {x:Static SystemColors.WindowBrushKey}}"">
|
||||
<ScrollViewer x:Name=""DropDownScrollViewer"">
|
||||
<Grid x:Name=""grid"">
|
||||
<Canvas x:Name=""canvas"" HorizontalAlignment=""Left"" Height=""0"" VerticalAlignment=""Top"" Width=""0"">
|
||||
<Rectangle x:Name=""opaqueRect"" Fill=""{Binding Background, ElementName=dropDownBorder}"" Height=""{Binding ActualHeight, ElementName=dropDownBorder}"" Width=""{Binding ActualWidth, ElementName=dropDownBorder}""/>
|
||||
</Canvas>
|
||||
<ItemsPresenter x:Name=""ItemsPresenter"" KeyboardNavigation.DirectionalNavigation=""Contained"" SnapsToDevicePixels=""{TemplateBinding SnapsToDevicePixels}""/>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
</Border>
|
||||
</Popup>
|
||||
<ToggleButton x:Name=""toggleButton"" BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}"" Grid.ColumnSpan=""2"" IsChecked=""{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}"" Style=""{DynamicResource ComboBoxToggleButton}""/>
|
||||
<Border x:Name=""border"" Background=""{DynamicResource TextBox.Static.Background}"" Margin=""{TemplateBinding BorderThickness}"">
|
||||
<TextBox x:Name=""PART_EditableTextBox"" HorizontalContentAlignment=""{TemplateBinding HorizontalContentAlignment}"" IsReadOnly=""{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}"" Margin=""{TemplateBinding Padding}"" Style=""{DynamicResource ComboBoxEditableTextBox}"" VerticalContentAlignment=""{TemplateBinding VerticalContentAlignment}""/>
|
||||
</Border>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property=""IsEnabled"" Value=""false"">
|
||||
<Setter Property=""Opacity"" TargetName=""border"" Value=""0.56""/>
|
||||
</Trigger>
|
||||
<Trigger Property=""IsKeyboardFocusWithin"" Value=""true"">
|
||||
<Setter Property=""Foreground"" Value=""Black""/>
|
||||
</Trigger>
|
||||
<Trigger Property=""HasItems"" Value=""false"">
|
||||
<Setter Property=""Height"" TargetName=""dropDownBorder"" Value=""95""/>
|
||||
</Trigger>
|
||||
<MultiTrigger>
|
||||
<MultiTrigger.Conditions>
|
||||
<Condition Property=""IsGrouping"" Value=""true""/>
|
||||
</MultiTrigger.Conditions>
|
||||
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""false""/>
|
||||
</MultiTrigger>
|
||||
<Trigger Property=""ScrollViewer.CanContentScroll"" SourceName=""DropDownScrollViewer"" Value=""false"">
|
||||
<Setter Property=""Canvas.Top"" TargetName=""opaqueRect"" Value=""{Binding VerticalOffset, ElementName=DropDownScrollViewer}""/>
|
||||
<Setter Property=""Canvas.Left"" TargetName=""opaqueRect"" Value=""{Binding HorizontalOffset, ElementName=DropDownScrollViewer}""/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>";
|
||||
|
||||
#endregion
|
||||
|
||||
#region Styles
|
||||
|
||||
/// <summary>
|
||||
/// ComboBoxEditableTextBox Style XAML (.NET Framework 4.0 and above)
|
||||
/// </summary>
|
||||
private const string _comboBoxEditableTextBoxStyleDefault = @"<Style TargetType=""{x:Type TextBox}"">
|
||||
<Setter Property=""OverridesDefaultStyle"" Value=""true""/>
|
||||
<Setter Property=""AllowDrop"" Value=""true""/>
|
||||
<Setter Property=""MinWidth"" Value=""0""/>
|
||||
<Setter Property=""MinHeight"" Value=""0""/>
|
||||
<Setter Property=""FocusVisualStyle"" Value=""{x:Null}""/>
|
||||
<Setter Property=""ScrollViewer.PanningMode"" Value=""VerticalFirst""/>
|
||||
<Setter Property=""Stylus.IsFlicksEnabled"" Value=""False""/>
|
||||
<Setter Property=""Template"">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType=""{x:Type TextBox}"">
|
||||
<ScrollViewer x:Name=""PART_ContentHost"" Background=""Transparent"" Focusable=""false"" HorizontalScrollBarVisibility=""Hidden"" VerticalScrollBarVisibility=""Hidden""/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>";
|
||||
|
||||
/// <summary>
|
||||
/// ComboBoxEditableTextBox Style XAML (.NET Framework 3.5)
|
||||
/// </summary>
|
||||
private const string _comboBoxEditableTextBoxStyleNet35 = @"<Style TargetType=""{x:Type TextBox}"">
|
||||
<Setter Property=""OverridesDefaultStyle"" Value=""true""/>
|
||||
<Setter Property=""AllowDrop"" Value=""true""/>
|
||||
<Setter Property=""MinWidth"" Value=""0""/>
|
||||
<Setter Property=""MinHeight"" Value=""0""/>
|
||||
<Setter Property=""FocusVisualStyle"" Value=""{x:Null}""/>
|
||||
<Setter Property=""Stylus.IsFlicksEnabled"" Value=""False""/>
|
||||
<Setter Property=""Template"">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType=""{x:Type TextBox}"">
|
||||
<ScrollViewer x:Name=""PART_ContentHost"" Background=""Transparent"" Focusable=""false"" HorizontalScrollBarVisibility=""Hidden"" VerticalScrollBarVisibility=""Hidden""/>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>";
|
||||
|
||||
/// <summary>
|
||||
/// CustomComboBoxStyle Style XAML (.NET Framework 4.0 and above)
|
||||
/// </summary>
|
||||
private const string _customComboBoxStyleDefault = @"<Style x:Key=""CustomComboBoxStyle"" TargetType=""{x:Type ComboBox}"">
|
||||
<Setter Property=""FocusVisualStyle"" Value=""{DynamicResource FocusVisual}""/>
|
||||
<Setter Property=""Background"" Value=""{DynamicResource ComboBox.Static.Background}""/>
|
||||
<Setter Property=""BorderBrush"" Value=""{DynamicResource ComboBox.Static.Border}""/>
|
||||
<Setter Property=""Foreground"" Value=""{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}""/>
|
||||
<Setter Property=""BorderThickness"" Value=""1""/>
|
||||
<Setter Property=""ScrollViewer.HorizontalScrollBarVisibility"" Value=""Auto""/>
|
||||
<Setter Property=""ScrollViewer.VerticalScrollBarVisibility"" Value=""Auto""/>
|
||||
<Setter Property=""Padding"" Value=""6,3,5,3""/>
|
||||
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""true""/>
|
||||
<Setter Property=""ScrollViewer.PanningMode"" Value=""VerticalFirst""/>
|
||||
<Setter Property=""Stylus.IsFlicksEnabled"" Value=""False""/>
|
||||
<Setter Property=""Template"" Value=""{DynamicResource ComboBoxTemplate}""/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property=""IsEditable"" Value=""true"">
|
||||
<Setter Property=""IsTabStop"" Value=""false""/>
|
||||
<Setter Property=""Padding"" Value=""2""/>
|
||||
<Setter Property=""Template"" Value=""{DynamicResource ComboBoxEditableTemplate}""/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>";
|
||||
|
||||
/// <summary>
|
||||
/// CustomComboBoxStyle Style XAML (.NET Framework 3.5)
|
||||
/// </summary>
|
||||
private const string _customComboBoxStyleNet35 = @"<Style TargetType=""{x:Type ComboBox}"">
|
||||
<Setter Property=""FocusVisualStyle"" Value=""{DynamicResource FocusVisual}""/>
|
||||
<Setter Property=""Background"" Value=""{DynamicResource ComboBox.Static.Background}""/>
|
||||
<Setter Property=""BorderBrush"" Value=""{DynamicResource ComboBox.Static.Border}""/>
|
||||
<Setter Property=""Foreground"" Value=""{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}""/>
|
||||
<Setter Property=""BorderThickness"" Value=""1""/>
|
||||
<Setter Property=""ScrollViewer.HorizontalScrollBarVisibility"" Value=""Auto""/>
|
||||
<Setter Property=""ScrollViewer.VerticalScrollBarVisibility"" Value=""Auto""/>
|
||||
<Setter Property=""Padding"" Value=""6,3,5,3""/>
|
||||
<Setter Property=""ScrollViewer.CanContentScroll"" Value=""true""/>
|
||||
<Setter Property=""Stylus.IsFlicksEnabled"" Value=""False""/>
|
||||
<Setter Property=""Template"" Value=""{DynamicResource ComboBoxTemplate}""/>
|
||||
<Style.Triggers>
|
||||
<Trigger Property=""IsEditable"" Value=""true"">
|
||||
<Setter Property=""IsTabStop"" Value=""false""/>
|
||||
<Setter Property=""Padding"" Value=""2""/>
|
||||
<Setter Property=""Template"" Value=""{DynamicResource ComboBoxEditableTemplate}""/>
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>";
|
||||
|
||||
/// <summary>
|
||||
/// CustomProgressBarStyle Style XAML (.NET Framework 4.0 and above)
|
||||
/// </summary>
|
||||
private const string _customProgressBarStyleDefault = @"<Style x:Key=""CustomProgressBarStyle"" TargetType=""{x:Type ProgressBar}"">
|
||||
<Setter Property=""Foreground"" Value=""{DynamicResource ProgressBar.Progress}""/>
|
||||
<Setter Property=""Background"" Value=""{DynamicResource ProgressBar.Background}""/>
|
||||
<Setter Property=""BorderBrush"" Value=""{DynamicResource ProgressBar.Border}""/>
|
||||
<Setter Property=""BorderThickness"" Value=""1""/>
|
||||
<Setter Property=""Template"">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType=""{x:Type ProgressBar}"">
|
||||
<Grid x:Name=""TemplateRoot"">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name=""CommonStates"">
|
||||
<VisualState x:Name=""Determinate""/>
|
||||
<VisualState x:Name=""Indeterminate"">
|
||||
<Storyboard RepeatBehavior=""Forever"">
|
||||
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)"" Storyboard.TargetName=""Animation"">
|
||||
<EasingDoubleKeyFrame KeyTime=""0"" Value=""0.25""/>
|
||||
<EasingDoubleKeyFrame KeyTime=""0:0:1"" Value=""0.25""/>
|
||||
<EasingDoubleKeyFrame KeyTime=""0:0:2"" Value=""0.25""/>
|
||||
</DoubleAnimationUsingKeyFrames>
|
||||
<PointAnimationUsingKeyFrames Storyboard.TargetProperty=""(UIElement.RenderTransformOrigin)"" Storyboard.TargetName=""Animation"">
|
||||
<EasingPointKeyFrame KeyTime=""0"" Value=""-0.5,0.5""/>
|
||||
<EasingPointKeyFrame KeyTime=""0:0:1"" Value=""0.5,0.5""/>
|
||||
<EasingPointKeyFrame KeyTime=""0:0:2"" Value=""1.5,0.5""/>
|
||||
</PointAnimationUsingKeyFrames>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
<Border BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}""/>
|
||||
<Rectangle x:Name=""PART_Track""/>
|
||||
<Grid x:Name=""PART_Indicator"" ClipToBounds=""true"" HorizontalAlignment=""Left"">
|
||||
<Rectangle x:Name=""Indicator"" Fill=""{TemplateBinding Foreground}""/>
|
||||
<Rectangle x:Name=""Animation"" Fill=""{TemplateBinding Foreground}"" RenderTransformOrigin=""0.5,0.5"">
|
||||
<Rectangle.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform/>
|
||||
<SkewTransform/>
|
||||
<RotateTransform/>
|
||||
<TranslateTransform/>
|
||||
</TransformGroup>
|
||||
</Rectangle.RenderTransform>
|
||||
</Rectangle>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property=""Orientation"" Value=""Vertical"">
|
||||
<Setter Property=""LayoutTransform"" TargetName=""TemplateRoot"">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle=""-90""/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property=""IsIndeterminate"" Value=""true"">
|
||||
<Setter Property=""Visibility"" TargetName=""Indicator"" Value=""Collapsed""/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>";
|
||||
|
||||
/// <summary>
|
||||
/// CustomProgressBarStyle Style XAML (.NET Framework 3.5)
|
||||
/// </summary>
|
||||
private const string _customProgressBarStyleNet35 = @"<Style TargetType=""{x:Type ProgressBar}"">
|
||||
<Setter Property=""Foreground"" Value=""{DynamicResource ProgressBar.Progress}""/>
|
||||
<Setter Property=""Background"" Value=""{DynamicResource ProgressBar.Background}""/>
|
||||
<Setter Property=""BorderBrush"" Value=""{DynamicResource ProgressBar.Border}""/>
|
||||
<Setter Property=""BorderThickness"" Value=""1""/>
|
||||
<Setter Property=""Template"">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType=""{x:Type ProgressBar}"">
|
||||
<Grid x:Name=""TemplateRoot"">
|
||||
<Border BorderBrush=""{TemplateBinding BorderBrush}"" BorderThickness=""{TemplateBinding BorderThickness}"" Background=""{TemplateBinding Background}""/>
|
||||
<Rectangle x:Name=""PART_Track""/>
|
||||
<Grid x:Name=""PART_Indicator"" ClipToBounds=""true"" HorizontalAlignment=""Left"">
|
||||
<Rectangle x:Name=""Indicator"" Fill=""{TemplateBinding Foreground}""/>
|
||||
<Rectangle x:Name=""Animation"" Fill=""{TemplateBinding Foreground}"" RenderTransformOrigin=""0.5,0.5"">
|
||||
<Rectangle.RenderTransform>
|
||||
<TransformGroup>
|
||||
<ScaleTransform/>
|
||||
<SkewTransform/>
|
||||
<RotateTransform/>
|
||||
<TranslateTransform/>
|
||||
</TransformGroup>
|
||||
</Rectangle.RenderTransform>
|
||||
</Rectangle>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property=""Orientation"" Value=""Vertical"">
|
||||
<Setter Property=""LayoutTransform"" TargetName=""TemplateRoot"">
|
||||
<Setter.Value>
|
||||
<RotateTransform Angle=""-90""/>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Trigger>
|
||||
<Trigger Property=""IsIndeterminate"" Value=""true"">
|
||||
<Setter Property=""Visibility"" TargetName=""Indicator"" Value=""Collapsed""/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>";
|
||||
|
||||
#endregion
|
||||
public App()
|
||||
{
|
||||
#if NET40_OR_GREATER || NETCOREAPP
|
||||
InitializeComponent();
|
||||
#endif
|
||||
|
||||
// Create control templates
|
||||
CreateControlTemplate("ComboBoxTemplate");
|
||||
CreateControlTemplate("ComboBoxEditableTemplate");
|
||||
|
||||
// Create styles
|
||||
CreateStyle("ComboBoxEditableTextBox");
|
||||
CreateStyle("CustomComboBoxStyle");
|
||||
CreateStyle("CustomProgressBarStyle");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an XAML parser context with the required namespaces
|
||||
/// </summary>
|
||||
private ParserContext CreateParserContext()
|
||||
{
|
||||
var context = new ParserContext();
|
||||
|
||||
context.XmlnsDictionary[""] = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
|
||||
context.XmlnsDictionary["x"] = "http://schemas.microsoft.com/winfx/2006/xaml";
|
||||
#if NETFRAMEWORK
|
||||
context.XmlnsDictionary["themes"] = "clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero";
|
||||
#else
|
||||
context.XmlnsDictionary["themes"] = "clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2";
|
||||
#endif
|
||||
context.XamlTypeMapper = new XamlTypeMapper([]);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a named control template and add it to the current set of resources
|
||||
/// </summary>
|
||||
private void CreateControlTemplate(string resourceName)
|
||||
{
|
||||
var parserContext = CreateParserContext();
|
||||
var controlTemplate = resourceName switch
|
||||
{
|
||||
#if NET35
|
||||
"ComboBoxTemplate" => XamlReader.Parse(_comboBoxTemplateNet35, parserContext) as ControlTemplate,
|
||||
"ComboBoxEditableTemplate" => XamlReader.Parse(_comboBoxEditableTemplateNet35, parserContext) as ControlTemplate,
|
||||
#else
|
||||
"ComboBoxTemplate" => XamlReader.Parse(_comboBoxTemplateDefault, parserContext) as ControlTemplate,
|
||||
"ComboBoxEditableTemplate" => XamlReader.Parse(_comboBoxEditableTemplateDefault, parserContext) as ControlTemplate,
|
||||
#endif
|
||||
_ => throw new ArgumentException($"'{resourceName}' is not a recognized control template", nameof(resourceName)),
|
||||
};
|
||||
|
||||
// Add the control template
|
||||
Resources[resourceName] = controlTemplate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a named style and add it to the current set of resources
|
||||
/// </summary>
|
||||
private void CreateStyle(string resourceName)
|
||||
{
|
||||
var parserContext = CreateParserContext();
|
||||
var style = resourceName switch
|
||||
{
|
||||
#if NET35
|
||||
"ComboBoxEditableTextBox" => XamlReader.Parse(_comboBoxEditableTextBoxStyleNet35, parserContext) as Style,
|
||||
"CustomComboBoxStyle" => XamlReader.Parse(_customComboBoxStyleNet35, parserContext) as Style,
|
||||
"CustomProgressBarStyle" => XamlReader.Parse(_customProgressBarStyleNet35, parserContext) as Style,
|
||||
#else
|
||||
"ComboBoxEditableTextBox" => XamlReader.Parse(_comboBoxEditableTextBoxStyleDefault, parserContext) as Style,
|
||||
"CustomComboBoxStyle" => XamlReader.Parse(_customComboBoxStyleDefault, parserContext) as Style,
|
||||
"CustomProgressBarStyle" => XamlReader.Parse(_customProgressBarStyleDefault, parserContext) as Style,
|
||||
#endif
|
||||
_ => throw new ArgumentException($"'{resourceName}' is not a recognized style", nameof(resourceName)),
|
||||
};
|
||||
|
||||
// Add the style
|
||||
Resources[resourceName] = style;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,54 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0-windows;net8.0-windows</TargetFrameworks>
|
||||
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>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>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<ApplicationIcon>Images\Icon.ico</ApplicationIcon>
|
||||
<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>
|
||||
<Nullable>enable</Nullable>
|
||||
<ApplicationIcon>Images\Icon.ico</ApplicationIcon>
|
||||
<Title>MPF</Title>
|
||||
<Description>Frontend for various dumping programs</Description>
|
||||
<VersionPrefix>3.1.3</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Copyright>Copyright (c)2019-2023</Copyright>
|
||||
<VersionPrefix>3.0.0</VersionPrefix>
|
||||
<InternalsVisibleTo>MPF.Test</InternalsVisibleTo>
|
||||
<Description>Frontend 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>
|
||||
|
||||
<!-- Special handling for Aero2 on non-Framework .NET -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net4`))">
|
||||
<ReferenceWpfAeroTheme>true</ReferenceWpfAeroTheme>
|
||||
<ReferenceWpfAero2Theme>false</ReferenceWpfAero2Theme>
|
||||
<ReferenceWpfAeroLiteTheme>false</ReferenceWpfAeroLiteTheme>
|
||||
<ReferenceWpfClassicTheme>false</ReferenceWpfClassicTheme>
|
||||
<ReferenceWpfLunaTheme>false</ReferenceWpfLunaTheme>
|
||||
<ReferenceWpfRoyaleTheme>false</ReferenceWpfRoyaleTheme>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`)) OR $(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<ReferenceWpfAeroTheme>false</ReferenceWpfAeroTheme>
|
||||
<ReferenceWpfAero2Theme>true</ReferenceWpfAero2Theme>
|
||||
<ReferenceWpfAeroLiteTheme>false</ReferenceWpfAeroLiteTheme>
|
||||
<ReferenceWpfClassicTheme>false</ReferenceWpfClassicTheme>
|
||||
<ReferenceWpfLunaTheme>false</ReferenceWpfLunaTheme>
|
||||
<ReferenceWpfRoyaleTheme>false</ReferenceWpfRoyaleTheme>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="MPF.Test" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Icon.ico" />
|
||||
</ItemGroup>
|
||||
@@ -25,15 +58,59 @@
|
||||
<ProjectReference Include="..\MPF.UI.Core\MPF.UI.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net3`))">
|
||||
<Reference Include="PresentationBuildTasks" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationBuildTasks.dll" />
|
||||
<Reference Include="PresentationCore" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationCore.dll" />
|
||||
<Reference Include="PresentationFramework" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\PresentationFramework.dll" />
|
||||
<Reference Include="WindowsBase" HintPath="$(ProgramFiles)\Reference Assemblies\Microsoft\Framework\v3.0\WindowsBase.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<Reference Include="PresentationFramework.Aero" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.0.0" GeneratePathProperty="true">
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.1" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.2.0" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Special handling for Aero2 on non-Framework .NET -->
|
||||
<Target Name="SelectWpfThemeAssembly" AfterTargets="ResolveAssemblyReferences" Condition="'$(ReferenceWpfAeroTheme)' == 'true' OR '$(ReferenceWpfAero2Theme)' == 'true' OR '$(ReferenceWpfAeroLiteTheme)' == 'true' OR '$(ReferenceWpfClassicTheme)' == 'true' OR '$(ReferenceWpfLunaTheme)' == 'true' OR '$(ReferenceWpfRoyaleTheme)' == 'true'">
|
||||
<ItemGroup>
|
||||
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Aero'">
|
||||
<Aliases Condition="'$(WpfAeroThemeAliases)'!=''">$(WpfAeroThemeAliases)</Aliases>
|
||||
</_WpfThemeAssemblies>
|
||||
|
||||
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Aero2'">
|
||||
<Aliases Condition="'$(WpfAero2ThemeAliases)'!=''">$(WpfAero2ThemeAliases)</Aliases>
|
||||
</_WpfThemeAssemblies>
|
||||
|
||||
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.AeroLite'">
|
||||
<Aliases Condition="'$(WpfAeroLiteThemeAliases)'!=''">$(WpfAeroLiteThemeAliases)</Aliases>
|
||||
</_WpfThemeAssemblies>
|
||||
|
||||
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Classic'">
|
||||
<Aliases Condition="'$(WpfClassicThemeAliases)'!=''">$(WpfClassicThemeAliases)</Aliases>
|
||||
</_WpfThemeAssemblies>
|
||||
|
||||
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Luna'">
|
||||
<Aliases Condition="'$(WpfLunaThemeAliases)'!=''">$(WpfLunaThemeAliases)</Aliases>
|
||||
</_WpfThemeAssemblies>
|
||||
|
||||
<_WpfThemeAssemblies Include="@(ReferencePath)" Condition="'%(ReferencePath.NuGetPackageId)'=='Microsoft.WindowsDesktop.App.Ref' AND '%(ReferencePath.FileName)'=='PresentationFramework.Royale'">
|
||||
<Aliases Condition="'$(WpfRoyaleThemeAliases)'!=''">$(WpfRoyaleThemeAliases)</Aliases>
|
||||
</_WpfThemeAssemblies>
|
||||
|
||||
<ReferencePath Remove="@(_WpfThemeAssemblies)" />
|
||||
<ReferencePath Include="@(_WpfThemeAssemblies)" Condition="('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Aero' AND'$(ReferenceWpfAeroTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Aero2' AND '$(ReferenceWpfAero2Theme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.AeroLite' AND '$(ReferenceWpfAeroLiteTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Classic' AND '$(ReferenceWpfClassicTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Luna' AND '$(ReferenceWpfLunaTheme)'=='true') OR ('%(_WpfThemeAssemblies.FileName)'=='PresentationFramework.Royale' AND '$(ReferenceWpfRoyaleTheme)'=='true')" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
|
||||
</Project>
|
||||
18
README.md
18
README.md
@@ -2,7 +2,9 @@
|
||||
|
||||
Redumper/Aaru/DiscImageCreator UI in C#
|
||||
|
||||
[](https://ci.appveyor.com/project/mnadareski/MPF/build/artifacts)
|
||||
[](https://ci.appveyor.com/project/mnadareski/MPF)
|
||||
[](https://github.com/SabreTools/MPF/actions/workflows/build_ui.yml)
|
||||
[](https://github.com/SabreTools/MPF/actions/workflows/build_check.yml)
|
||||
|
||||
This is a community project, so if you have some time and knowledge to give, we'll be glad to add you as a contributor to this project. If you have any suggestions, issues, bugs, or crashes, please look at the [Issues](https://github.com/SabreTools/MPF/issues) page first to see if it has been reported before and try out the latest AppVeyor WIP build below to see if it has already been addressed. If it hasn't, please open an issue that's as descriptive as you can be. Help me make this a better program for everyone :)
|
||||
|
||||
@@ -10,7 +12,7 @@ This is a community project, so if you have some time and knowledge to give, we'
|
||||
|
||||
For the most recent stable build, download the latest release here: [Releases Page](https://github.com/SabreTools/MPF/releases)
|
||||
|
||||
For the latest AppVeyor WIP build here: [AppVeyor](https://ci.appveyor.com/project/mnadareski/MPF/build/artifacts)
|
||||
For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/MPF/releases/tag/rolling)
|
||||
|
||||
## Media Preservation Frontend UI (MPF)
|
||||
|
||||
@@ -33,8 +35,6 @@ MPF.Check is a commandline-only program that allows users to generate submission
|
||||
|
||||
Both MPF UI and MPF.Check have the same system requirements for running, with the exception that MPF UI is Windows-only.
|
||||
|
||||
- [Supported OS versions for .NET 6](https://github.com/dotnet/core/blob/main/release-notes/6.0/supported-os.md)
|
||||
- Requires [.NET 6.0 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/6.0) if built without bundled runtime
|
||||
- [Supported OS versions for .NET 8](https://github.com/dotnet/core/blob/main/release-notes/8.0/supported-os.md)
|
||||
- Requires [.NET 8.0 Runtime](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) if built without bundled runtime
|
||||
|
||||
@@ -42,27 +42,27 @@ Ensure that your operating system and runtimes are as up-to-date as possible, si
|
||||
|
||||
## Build Instructions
|
||||
|
||||
To build for .NET 6.0 or .NET 8.0, ensure that the [.NET 8.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) (or later) is installed and included in your `PATH`. Then, run the following commands from command prompt, Powershell, Terminal, or shell:
|
||||
To build for .NET 8.0, ensure that the [.NET 8.0 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) (or later) is installed and included in your `PATH`. Then, run the following commands from command prompt, Powershell, Terminal, or shell:
|
||||
|
||||
**MPF UI (Windows only):**
|
||||
|
||||
```bash
|
||||
dotnet build MPF/MPF.csproj --framework [net6.0-windows|net8.0-windows] --runtime win-x64
|
||||
dotnet build MPF/MPF.csproj --framework net8.0-windows --runtime [win-x86|win-x64]
|
||||
```
|
||||
|
||||
**MPF.Check (Windows, OSX, Linux):**
|
||||
|
||||
```bash
|
||||
dotnet build MPF.Check/MPF.Check.csproj --framework [net6.0|net8.0] --runtime [win-x64|linux-x64|osx-x64]
|
||||
dotnet build MPF.Check/MPF.Check.csproj --framework net8.0 --runtime [win-x86|win-x64|linux-x64|osx-x64]
|
||||
```
|
||||
|
||||
Choose one of `win-x64`, `linux-x64`, or `osx-x64` depending on the machine you are targeting.
|
||||
|
||||
### Build Scripts
|
||||
|
||||
Windows users may run `publish-win.bat` and Linux users may run `publish-nix.sh` to perform a full release build.
|
||||
Windows users may run `publish-win.ps1` and Linux users may run `publish-nix.sh` to perform a full release build.
|
||||
|
||||
- `publish-win.bat` will build and package all variants of MPF UI and MPF.Check
|
||||
- `publish-win.ps1` will build and package all variants of MPF UI and MPF.Check
|
||||
- The script requires [7-zip commandline](https://www.7-zip.org/download.html) and [Git for Windows](https://git-scm.com/downloads) to be installed and in `PATH`
|
||||
- `publish-nix.sh` will _only_ build and package all variants of MPF.Check
|
||||
- The script requires `zip` and `git` to be installed and in `PATH`
|
||||
|
||||
69
appveyor.yml
69
appveyor.yml
@@ -1,5 +1,5 @@
|
||||
# version format
|
||||
version: 3.0.0-{build}
|
||||
version: 3.1.3-{build}
|
||||
|
||||
# pull request template
|
||||
pull_requests:
|
||||
@@ -10,58 +10,7 @@ image: Visual Studio 2022
|
||||
|
||||
# build step
|
||||
build_script:
|
||||
- dotnet restore
|
||||
|
||||
# .NET 6.0 Debug
|
||||
- dotnet publish MPF\MPF.csproj -f net6.0-windows -r win-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r win-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r linux-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r osx-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
|
||||
|
||||
# .NET 6.0 Release
|
||||
- dotnet publish MPF\MPF.csproj -f net6.0-windows -r win-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r win-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r linux-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r osx-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
|
||||
# .NET 8.0 Debug
|
||||
- dotnet publish MPF\MPF.csproj -f net8.0-windows -r win-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r win-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r linux-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r osx-x64 -c Debug --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true
|
||||
|
||||
# .NET 8.0 Release
|
||||
- dotnet publish MPF\MPF.csproj -f net8.0-windows -r win-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r win-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r linux-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
- dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r osx-x64 -c Release --self-contained true --version-suffix %APPVEYOR_REPO_COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
|
||||
# post-build step
|
||||
after_build:
|
||||
|
||||
# Aaru
|
||||
- ps: appveyor DownloadFile https://github.com/aaru-dps/Aaru/releases/download/v5.3.2/aaru-5.3.2_windows_x64.zip
|
||||
- 7z x aaru-5.3.2_windows_x64.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Aaru *
|
||||
|
||||
# DiscImageCreator
|
||||
- ps: appveyor DownloadFile https://github.com/saramibreak/DiscImageCreator/files/11660558/DiscImageCreator_20230606.zip
|
||||
- 7z e DiscImageCreator_20230606.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Creator Release_ANSI\*
|
||||
|
||||
# Redumper
|
||||
- ps: appveyor DownloadFile https://github.com/superg/redumper/releases/download/build_247/redumper-2023.11.01_build247-win64.zip
|
||||
- 7z e redumper-2023.11.01_build247-win64.zip -oMPF\bin\Debug\net8.0-windows\win-x64\publish\Programs\Redumper redumper-2023.11.01_build247-win64\bin\*
|
||||
|
||||
# Create MPF Debug archives
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF\bin\Debug\net8.0-windows\win-x64\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF_%APPVEYOR_BUILD_NUMBER%_net8.0_win-x64_debug.zip *
|
||||
|
||||
# Create MPF.Check Debug archives
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\win-x64\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_win-x64_debug.zip *
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\linux-x64\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_linux-x64_debug.zip *
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\osx-x64\publish\
|
||||
- 7z a -tzip %APPVEYOR_BUILD_FOLDER%\MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_osx-x64_debug.zip *
|
||||
- dotnet build
|
||||
|
||||
# success/failure tracking
|
||||
on_success:
|
||||
@@ -69,16 +18,4 @@ on_success:
|
||||
- ps: ./send.ps1 success $env:WEBHOOK_URL
|
||||
on_failure:
|
||||
- ps: Invoke-RestMethod https://raw.githubusercontent.com/DiscordHooks/appveyor-discord-webhook/master/send.ps1 -o send.ps1
|
||||
- ps: ./send.ps1 failure $env:WEBHOOK_URL
|
||||
|
||||
# artifact linking
|
||||
artifacts:
|
||||
- path: MPF_%APPVEYOR_BUILD_NUMBER%_net8.0_win-x64_debug.zip
|
||||
name: MPF (.NET 8.0, Debug, Windows x64)
|
||||
|
||||
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_win-x64_debug.zip
|
||||
name: MPF.Check (.NET 8.0, Debug, Windows x64)
|
||||
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_linux-x64_debug.zip
|
||||
name: MPF.Check (.NET 8.0, Debug, Linux x64)
|
||||
- path: MPF.Check_%APPVEYOR_BUILD_NUMBER%_net8.0_osx-x64_debug.zip
|
||||
name: MPF.Check (.NET 8.0, Debug, OSX x64)
|
||||
- ps: ./send.ps1 failure $env:WEBHOOK_URL
|
||||
210
publish-nix.sh
210
publish-nix.sh
@@ -10,83 +10,161 @@
|
||||
# If any of these are not satisfied, the operation may fail
|
||||
# in an unpredictable way and result in an incomplete output.
|
||||
|
||||
# TODO: Re-enable MPF building after figuring out how to build Windows desktop applications on Linux
|
||||
# This may require an additional package to be installed?
|
||||
# Optional parameters
|
||||
USE_ALL=false
|
||||
INCLUDE_PROGRAMS=false
|
||||
NO_BUILD=false
|
||||
NO_ARCHIVE=false
|
||||
while getopts "upba" OPTION; do
|
||||
case $OPTION in
|
||||
u)
|
||||
USE_ALL=true
|
||||
;;
|
||||
p)
|
||||
INCLUDE_PROGRAMS=true
|
||||
;;
|
||||
b)
|
||||
NO_BUILD=true
|
||||
;;
|
||||
a)
|
||||
NO_ARCHIVE=true
|
||||
;;
|
||||
*)
|
||||
echo "Invalid option provided"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Set the current directory as a variable
|
||||
BUILD_FOLDER=$PWD
|
||||
|
||||
# Set the current commit hash
|
||||
COMMIT=`git log --pretty=%H -1`
|
||||
COMMIT=$(git log --pretty=%H -1)
|
||||
|
||||
# Restore Nuget packages for all builds
|
||||
echo "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
# Create the build matrix arrays
|
||||
UI_FRAMEWORKS=("net8.0-windows")
|
||||
UI_RUNTIMES=("win-x86" "win-x64")
|
||||
CHECK_FRAMEWORKS=("net8.0")
|
||||
CHECK_RUNTIMES=("win-x86" "win-x64" "linux-x64" "osx-x64")
|
||||
|
||||
# .NET 6.0 Debug
|
||||
echo "Building .NET 6.0 debug"
|
||||
#dotnet publish MPF/MPF.csproj -f net6.0-windows -r win-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r win-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r linux-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r osx-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
# Use expanded lists, if requested
|
||||
if [ $USE_ALL = true ]; then
|
||||
UI_FRAMEWORKS=("net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0-windows" "net6.0-windows" "net7.0-windows" "net8.0-windows")
|
||||
UI_RUNTIMES=("win-x86" "win-x64")
|
||||
CHECK_FRAMEWORKS=("net20" "net35" "net40" "net452" "net462" "net472" "net48" "netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0")
|
||||
CHECK_RUNTIMES=("win-x86" "win-x64" "win-arm64" "linux-x64" "linux-arm64" "osx-x64")
|
||||
fi
|
||||
|
||||
# .NET 6.0 Release
|
||||
echo "Building .NET 6.0 release"
|
||||
#dotnet publish MPF/MPF.csproj -f net6.0-windows -r win-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r win-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r linux-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net6.0 -r osx-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
# Create the filter arrays
|
||||
SINGLE_FILE_CAPABLE=("net5.0" "net5.0-windows" "net6.0" "net6.0-windows" "net7.0" "net7.0-windows" "net8.0" "net8.0-windows")
|
||||
VALID_CROSS_PLATFORM_FRAMEWORKS=("netcoreapp3.1" "net5.0" "net6.0" "net7.0" "net8.0")
|
||||
VALID_CROSS_PLATFORM_RUNTIMES=("win-arm64" "linux-x64" "linux-arm64" "osx-x64")
|
||||
|
||||
# .NET 8.0 Debug
|
||||
echo "Building .NET 8.0 debug"
|
||||
#dotnet publish MPF/MPF.csproj -f net8.0-windows -r win-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r win-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r linux-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r osx-x64 -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
# Only build if requested
|
||||
if [ $NO_BUILD = false ]; then
|
||||
# Restore Nuget packages for all builds
|
||||
echo "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# .NET 8.0 Release
|
||||
echo "Building .NET 8.0 release"
|
||||
#dotnet publish MPF/MPF.csproj -f net8.0-windows -r win-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r win-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r linux-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f net8.0 -r osx-x64 -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:PublishTrimmed=true -p:DebugType=None -p:DebugSymbols=false
|
||||
# Build UI
|
||||
for FRAMEWORK in "${UI_FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${UI_RUNTIMES[@]}"; do
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create MPF Debug archives
|
||||
#cd $BUILD_FOLDER/MPF/bin/Debug/net6.0-windows/win-x64/publish/
|
||||
#zip -r $BUILD_FOLDER/MPF_net6.0_win-x64_debug.zip .
|
||||
#cd $BUILD_FOLDER/MPF/bin/Debug/net8.0-windows/win-x64/publish/
|
||||
#zip -r $BUILD_FOLDER/MPF_net8.0_win-x64_debug.zip .
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
dotnet publish MPF/MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
fi
|
||||
dotnet publish MPF/MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
else
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
dotnet publish MPF/MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
fi
|
||||
dotnet publish MPF/MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Create MPF Release archives
|
||||
#cd $BUILD_FOLDER/MPF/bin/Release/net6.0-windows/win-x64/publish/
|
||||
#zip -r $BUILD_FOLDER/MPF_net6.0_win-x64_release.zip .
|
||||
#cd $BUILD_FOLDER/MPF/bin/Release/net8.0-windows/win-x64/publish/
|
||||
#zip -r $BUILD_FOLDER/MPF_net8.0_win-x64_release.zip .
|
||||
# Build Check
|
||||
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Create MPF.Check Debug archives
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net6.0/win-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_win-x64_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net6.0/linux-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_linux-x64_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net6.0/osx-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_osx-x64_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net8.0/win-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_win-x64_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net8.0/linux-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_linux-x64_debug.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/net8.0/osx-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_osx-x64_debug.zip .
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
fi
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
else
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
fi
|
||||
dotnet publish MPF.Check/MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
fi
|
||||
done
|
||||
done
|
||||
fi
|
||||
|
||||
# Create MPF.Check Release archives
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net6.0/win-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_win-x64_release.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net6.0/linux-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_linux-x64_release.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net6.0/osx-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net6.0_osx-x64_release.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net8.0/win-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_win-x64_release.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net8.0/linux-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_linux-x64_release.zip .
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/net8.0/osx-x64/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_net8.0_osx-x64_release.zip .
|
||||
# Only create archives if requested
|
||||
if [ $NO_ARCHIVE = false ]; then
|
||||
# Create UI archives
|
||||
for FRAMEWORK in "${UI_FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${UI_RUNTIMES[@]}"; do
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
cd $BUILD_FOLDER/MPF/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
if [ $INCLUDE_PROGRAMS = true ]; then
|
||||
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_debug.zip .
|
||||
else
|
||||
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_debug.zip . -x 'Programs/\*'
|
||||
fi
|
||||
fi
|
||||
cd $BUILD_FOLDER/MPF/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
if [ $INCLUDE_PROGRAMS = true ]; then
|
||||
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_release.zip .
|
||||
else
|
||||
zip -r $BUILD_FOLDER/MPF_${FRAMEWORK}_${RUNTIME}_release.zip . -x 'Programs/\*'
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Create Check archives
|
||||
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_${FRAMEWORK}_${RUNTIME}_debug.zip .
|
||||
fi
|
||||
cd $BUILD_FOLDER/MPF.Check/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.Check_${FRAMEWORK}_${RUNTIME}_release.zip .
|
||||
done
|
||||
done
|
||||
|
||||
# Reset the directory
|
||||
cd $BUILD_FOLDER
|
||||
fi
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
@echo OFF
|
||||
|
||||
REM This batch file assumes the following:
|
||||
REM - .NET 8.0 (or newer) SDK is installed and in PATH
|
||||
REM - 7-zip commandline (7z.exe) is installed and in PATH
|
||||
REM - Git for Windows is installed and in PATH
|
||||
REM - The relevant commandline programs are already downloaded
|
||||
REM and put into their respective folders
|
||||
REM
|
||||
REM If any of these are not satisfied, the operation may fail
|
||||
REM in an unpredictable way and result in an incomplete output.
|
||||
|
||||
REM Set the current directory as a variable
|
||||
set BUILD_FOLDER=%~dp0
|
||||
|
||||
REM Set the current commit hash
|
||||
for /f %%i in ('git log --pretty^=%%H -1') do set COMMIT=%%i
|
||||
|
||||
REM Restore Nuget packages for all builds
|
||||
echo Restoring Nuget packages
|
||||
dotnet restore
|
||||
|
||||
REM .NET 6.0 Debug
|
||||
echo Building .NET 6.0 debug
|
||||
dotnet publish MPF\MPF.csproj -f net6.0-windows -r win-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r win-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r linux-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r osx-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
|
||||
REM .NET 6.0 Release
|
||||
echo Building .NET 6.0 release
|
||||
dotnet publish MPF\MPF.csproj -f net6.0-windows -r win-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r win-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r linux-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net6.0 -r osx-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
|
||||
REM .NET 8.0 Debug
|
||||
echo Building .NET 8.0 debug
|
||||
dotnet publish MPF\MPF.csproj -f net8.0-windows -r win-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r win-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r linux-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r osx-x64 -c Debug --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true
|
||||
|
||||
REM .NET 8.0 Release
|
||||
echo Building .NET 8.0 release
|
||||
dotnet publish MPF\MPF.csproj -f net8.0-windows -r win-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r win-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r linux-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f net8.0 -r osx-x64 -c Release --self-contained true --version-suffix %COMMIT% -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
|
||||
REM Create MPF Debug archives
|
||||
cd %BUILD_FOLDER%\MPF\bin\Debug\net6.0-windows\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF_net6.0_win-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF\bin\Debug\net8.0-windows\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF_net8.0_win-x64_debug.zip *
|
||||
|
||||
REM Create MPF Release archives
|
||||
cd %BUILD_FOLDER%\MPF\bin\Release\net6.0-windows\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF_net6.0_win-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF\bin\Release\net8.0-windows\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF_net8.0_win-x64_release.zip *
|
||||
|
||||
REM Create MPF.Check Debug archives
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_win-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\linux-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_linux-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net6.0\osx-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_osx-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_win-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\linux-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_linux-x64_debug.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Debug\net8.0\osx-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_osx-x64_debug.zip *
|
||||
|
||||
REM Create MPF.Check Release archives
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net6.0\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_win-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net6.0\linux-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_linux-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net6.0\osx-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net6.0_osx-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net8.0\win-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_win-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net8.0\linux-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_linux-x64_release.zip *
|
||||
cd %BUILD_FOLDER%\MPF.Check\bin\Release\net8.0\osx-x64\publish\
|
||||
7z a -tzip %BUILD_FOLDER%\MPF.Check_net8.0_osx-x64_release.zip *
|
||||
160
publish-win.ps1
Normal file
160
publish-win.ps1
Normal file
@@ -0,0 +1,160 @@
|
||||
# This batch file assumes the following:
|
||||
# - .NET 8.0 (or newer) SDK is installed and in PATH
|
||||
# - 7-zip commandline (7z.exe) is installed and in PATH
|
||||
# - Git for Windows is installed and in PATH
|
||||
# - The relevant commandline programs are already downloaded
|
||||
# and put into their respective folders
|
||||
#
|
||||
# If any of these are not satisfied, the operation may fail
|
||||
# in an unpredictable way and result in an incomplete output.
|
||||
|
||||
# Optional parameters
|
||||
param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Alias("UseAll")]
|
||||
[switch]$USE_ALL,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Alias("IncludePrograms")]
|
||||
[switch]$INCLUDE_PROGRAMS,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Alias("NoBuild")]
|
||||
[switch]$NO_BUILD,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[Alias("NoArchive")]
|
||||
[switch]$NO_ARCHIVE
|
||||
)
|
||||
|
||||
# Set the current directory as a variable
|
||||
$BUILD_FOLDER = $PSScriptRoot
|
||||
|
||||
# Set the current commit hash
|
||||
$COMMIT = git log --pretty=format:"%H" -1
|
||||
|
||||
# Create the build matrix arrays
|
||||
$UI_FRAMEWORKS = @('net8.0-windows')
|
||||
$UI_RUNTIMES = @('win-x86', 'win-x64')
|
||||
$CHECK_FRAMEWORKS = @('net8.0')
|
||||
$CHECK_RUNTIMES = @('win-x86', 'win-x64', 'linux-x64', 'osx-x64')
|
||||
|
||||
# Use expanded lists, if requested
|
||||
if ($USE_ALL.IsPresent) {
|
||||
$UI_FRAMEWORKS = @('net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0-windows', 'net6.0-windows', 'net7.0-windows', 'net8.0-windows')
|
||||
$UI_RUNTIMES = @('win-x86', 'win-x64')
|
||||
$CHECK_FRAMEWORKS = @('net20', 'net35', 'net40', 'net452', 'net462', 'net472', 'net48', 'netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
|
||||
$CHECK_RUNTIMES = @('win-x86', 'win-x64', 'win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64')
|
||||
}
|
||||
|
||||
# Create the filter arrays
|
||||
$SINGLE_FILE_CAPABLE = @('net5.0', 'net5.0-windows', 'net6.0', 'net6.0-windows', 'net7.0', 'net7.0-windows', 'net8.0', 'net8.0-windows')
|
||||
$VALID_CROSS_PLATFORM_FRAMEWORKS = @('netcoreapp3.1', 'net5.0', 'net6.0', 'net7.0', 'net8.0')
|
||||
$VALID_CROSS_PLATFORM_RUNTIMES = @('win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64')
|
||||
|
||||
# Only build if requested
|
||||
if (!$NO_BUILD.IsPresent) {
|
||||
# Restore Nuget packages for all builds
|
||||
Write-Host "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Build UI
|
||||
foreach ($FRAMEWORK in $UI_FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $UI_RUNTIMES) {
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
continue
|
||||
}
|
||||
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK) {
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
}
|
||||
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
}
|
||||
else {
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
}
|
||||
dotnet publish MPF\MPF.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Build Check
|
||||
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $CHECK_RUNTIMES) {
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
continue
|
||||
}
|
||||
|
||||
# Only .NET 5 and above can publish to a single file
|
||||
if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK) {
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
}
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false
|
||||
}
|
||||
else {
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
}
|
||||
dotnet publish MPF.Check\MPF.Check.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Only create archives if requested
|
||||
if (!$NO_ARCHIVE.IsPresent) {
|
||||
# Create UI archives
|
||||
foreach ($FRAMEWORK in $UI_FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $UI_RUNTIMES) {
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
Set-Location -Path $BUILD_FOLDER\MPF\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
if ($INCLUDE_PROGRAMS.IsPresent) {
|
||||
7z a -tzip $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_debug.zip *
|
||||
}
|
||||
else {
|
||||
7z a -tzip -x!Programs\* $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_debug.zip *
|
||||
}
|
||||
}
|
||||
|
||||
Set-Location -Path $BUILD_FOLDER\MPF\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
if ($INCLUDE_PROGRAMS.IsPresent) {
|
||||
7z a -tzip $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_release.zip *
|
||||
}
|
||||
else {
|
||||
7z a -tzip -x!Programs\* $BUILD_FOLDER\MPF_${FRAMEWORK}_${RUNTIME}_release.zip *
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Create Check archives
|
||||
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $CHECK_RUNTIMES) {
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
continue
|
||||
}
|
||||
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\MPF.Check_${FRAMEWORK}_${RUNTIME}_debug.zip *
|
||||
}
|
||||
Set-Location -Path $BUILD_FOLDER\MPF.Check\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\MPF.Check_${FRAMEWORK}_${RUNTIME}_release.zip *
|
||||
}
|
||||
}
|
||||
|
||||
# Reset the directory
|
||||
Set-Location -Path $PSScriptRoot
|
||||
}
|
||||
Reference in New Issue
Block a user