mirror of
https://github.com/SabreTools/MPF.git
synced 2026-02-04 13:45:29 +00:00
Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05738b7c11 | ||
|
|
f963db67b1 | ||
|
|
de64631c00 | ||
|
|
c8adef78c2 | ||
|
|
7b116e7a04 | ||
|
|
fb7b6ff1be | ||
|
|
7fb8e44c31 | ||
|
|
239ad4c4bc | ||
|
|
9834d0ea3e | ||
|
|
a35c13bd10 | ||
|
|
5e1777a7c7 | ||
|
|
66570300df | ||
|
|
4ac1fb201e | ||
|
|
cba8daa010 | ||
|
|
ba24a4b21a | ||
|
|
91c6fdac82 | ||
|
|
416656c457 | ||
|
|
fdd75818c4 | ||
|
|
ac302626c2 | ||
|
|
428f3cc547 | ||
|
|
66fc36fe3c | ||
|
|
9dddf1c9b6 | ||
|
|
5dbb955d26 | ||
|
|
2f2958bdea | ||
|
|
c91f6ebbce | ||
|
|
22fdd036eb | ||
|
|
3f12c6acb9 | ||
|
|
1dbae18da6 | ||
|
|
6370e2dd6a | ||
|
|
0c8879bc66 | ||
|
|
6be34414fe | ||
|
|
f15fc989c8 | ||
|
|
0fc53cb534 | ||
|
|
dc0909808a | ||
|
|
00401d1282 | ||
|
|
b9d0d5d8f6 | ||
|
|
22a6b77d27 | ||
|
|
bc4fe17fab | ||
|
|
4b4027f285 | ||
|
|
d28257b2b7 | ||
|
|
669ef47f32 | ||
|
|
be224800bc | ||
|
|
8dbb589d42 | ||
|
|
7b2fd5bf35 | ||
|
|
95fa651074 | ||
|
|
a0a155eb9b | ||
|
|
72339b18df | ||
|
|
95c9c7706d | ||
|
|
135bb43cdf | ||
|
|
cfc75ca84d | ||
|
|
33c35b63d7 | ||
|
|
851a43d46f | ||
|
|
a88bef481d | ||
|
|
781fec2b57 | ||
|
|
ee96367a45 | ||
|
|
9f9bfc0888 | ||
|
|
c6cc697320 | ||
|
|
5e3f7f740b | ||
|
|
e17a8f4708 | ||
|
|
ff4771a74a | ||
|
|
426717102d | ||
|
|
126bae33a4 | ||
|
|
11b8dd44bb | ||
|
|
cbbb8aaa8c | ||
|
|
9ee7cd7fd7 | ||
|
|
324c1fcee3 | ||
|
|
06776a6093 | ||
|
|
43a079bb28 | ||
|
|
d45345d338 | ||
|
|
1ff0340cae | ||
|
|
278c86f9f4 | ||
|
|
00089b799c | ||
|
|
0f98f03999 | ||
|
|
22f6f39a91 | ||
|
|
e9a9011dbd | ||
|
|
40bbb3d1c8 | ||
|
|
a48dad817b | ||
|
|
9f76dcc5fd | ||
|
|
4356561a8a | ||
|
|
347c522d62 | ||
|
|
998ecec261 | ||
|
|
ab8e775df0 | ||
|
|
246e6b8bfd | ||
|
|
c7f69de18f | ||
|
|
d4a98d7712 | ||
|
|
2983266e8a | ||
|
|
1baef4440a | ||
|
|
7cd25dae1c | ||
|
|
38f9b7234b | ||
|
|
2d7ea1bed9 | ||
|
|
806a69c280 | ||
|
|
a92159b8cb | ||
|
|
9451629461 | ||
|
|
51a1f0cc8e | ||
|
|
2830641b8a | ||
|
|
7c87a22dcc | ||
|
|
dd5b5d4c7d | ||
|
|
bc5e73d371 | ||
|
|
f47a55b723 | ||
|
|
c4d014e480 | ||
|
|
69b1d2f7ad | ||
|
|
7c295ca2f4 | ||
|
|
5af3aad68a | ||
|
|
f150483e84 | ||
|
|
92ef962f42 | ||
|
|
859e53f843 | ||
|
|
e70d70ca22 | ||
|
|
c2cf8147d3 | ||
|
|
cf32f38c0e | ||
|
|
1f92ff08d6 | ||
|
|
6aaf076434 | ||
|
|
6865b23aa7 | ||
|
|
e1c13982bd | ||
|
|
9cddcc2eae | ||
|
|
33932fad47 | ||
|
|
470e5c69fe | ||
|
|
3aae2990a3 | ||
|
|
503933e67f | ||
|
|
1a99fd9e71 | ||
|
|
affc175bda | ||
|
|
fe4c88d3ad | ||
|
|
9c830c9755 | ||
|
|
fc300465f8 |
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
2
.github/ISSUE_TEMPLATE/feature-request.md
vendored
@@ -10,7 +10,7 @@ assignees: mnadareski
|
||||
**Before You Submit**
|
||||
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
|
||||
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/SabreTools/BinaryObjectScanner/issues) instead.
|
||||
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
|
||||
|
||||
If none of those apply, then continue...
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/informational.md
vendored
2
.github/ISSUE_TEMPLATE/informational.md
vendored
@@ -10,7 +10,7 @@ assignees: mnadareski
|
||||
**Before You Submit**
|
||||
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the feature already exists.
|
||||
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/SabreTools/BinaryObjectScanner/issues) instead.
|
||||
- Check [previous issues](https://github.com/SabreTools/MPF/issues) to see if any of those are related to what you're about to ask for.
|
||||
|
||||
If none of those apply, then continue...
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/issue-report.md
vendored
2
.github/ISSUE_TEMPLATE/issue-report.md
vendored
@@ -10,7 +10,7 @@ assignees: mnadareski
|
||||
**Before You Submit**
|
||||
|
||||
- Remember to try the [latest WIP build](https://ci.appveyor.com/project/mnadareski/mpf/build/artifacts) to see if the issue has already been addressed.
|
||||
- Is it copy protection related? If so, report the issue [here](hhttps://github.com/SabreTools/BinaryObjectScanner/issues) instead.
|
||||
- Is it copy protection related? If so, report the issue [here](https://github.com/SabreTools/BinaryObjectScanner/issues) instead.
|
||||
- Check multiple discs to help narrow down the issue
|
||||
- Check the Options to see if changing any of those affects your issue.
|
||||
|
||||
|
||||
2
.github/workflows/build_check.yml
vendored
2
.github/workflows/build_check.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
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]
|
||||
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, osx-arm64]
|
||||
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
|
||||
|
||||
steps:
|
||||
|
||||
50
.github/workflows/build_cli.yml
vendored
Normal file
50
.github/workflows/build_cli.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
name: MPF CLI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
project: [MPF.CLI]
|
||||
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, osx-arm64]
|
||||
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
|
||||
61
.github/workflows/build_nupkg.yml
vendored
Normal file
61
.github/workflows/build_nupkg.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
name: Nuget Pack
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup .NET
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: 8.0.x
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Pack
|
||||
run: dotnet pack
|
||||
|
||||
- name: Upload Execution Contexts package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'Execution Contexts Package'
|
||||
path: 'MPF.ExecutionContexts/bin/Release/*.nupkg'
|
||||
|
||||
- name: Upload Execution Contexts to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: 'MPF.ExecutionContexts/bin/Release/*.nupkg'
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
|
||||
- name: Upload Processors package
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'Processors Package'
|
||||
path: 'MPF.Processors/bin/Release/*.nupkg'
|
||||
|
||||
- name: Upload Execution Contexts to rolling
|
||||
uses: ncipollo/release-action@v1.14.0
|
||||
with:
|
||||
allowUpdates: True
|
||||
artifacts: 'MPF.Processors/bin/Release/*.nupkg'
|
||||
body: 'Last built commit: ${{ github.sha }}'
|
||||
name: 'Rolling Release'
|
||||
prerelease: True
|
||||
replacesArtifacts: True
|
||||
tag: "rolling"
|
||||
updateOnlyUnreleased: True
|
||||
0
.gitmodules
vendored
0
.gitmodules
vendored
120
CHANGELIST.md
120
CHANGELIST.md
@@ -1,3 +1,123 @@
|
||||
### 3.2.2 (2024-09-xx)
|
||||
|
||||
- Clean up some Check options, add IRD option
|
||||
- Add Check flags for protection scan extras
|
||||
- Add comments around default options object
|
||||
- Fix usings ordering in ItemHelper
|
||||
- Add physical drive extensions to new tool
|
||||
- Fix build for older .NET
|
||||
- Move two extensions to a better location
|
||||
- Fix XGD3 SS ranges
|
||||
- Fix config location in OptionsLoader
|
||||
- Fix some CLI issues
|
||||
- Add more verbose requirement to CLI help
|
||||
- Quote input paths if needed
|
||||
- Allow separate mounted path for Linux
|
||||
- Fix cleaning XGD3 SS
|
||||
- Prepare Redumper for XGD support
|
||||
- Hash DMI and PFI files for XGD in Redumper
|
||||
- Support GD-ROM info for Redumper
|
||||
- Futureproof GD-ROM LD in Redumper
|
||||
- Make GD-ROM LD code nicer to read
|
||||
- Rename 2 XGD helper methods
|
||||
- Add bus encryption enabled method
|
||||
- Move BEE method to better location
|
||||
- Use new BEE method in code
|
||||
- Include serial for UMD
|
||||
- Remove GD-ROM version fallback method
|
||||
- Preemptively update Redumper Saturn support
|
||||
- Move MSXC parsing to PhysicalTool
|
||||
- Fix minor inconsistencies
|
||||
- Create some PlayStation helper methods
|
||||
- Remove redundant drive calls
|
||||
- Start preparing for better output file checks
|
||||
- Create currently-unused helper class
|
||||
- Make helper class more robust
|
||||
- Add unused GetOutputFiles method
|
||||
- Hook up GetOutputFiles in debug way
|
||||
- Replace GetLogFilePaths with common code
|
||||
- Move GetLogFilePaths to better location
|
||||
- Replace GenerateArtifacts with common code
|
||||
- Define new ArtifactKey field
|
||||
- Add artifact keys for all relevant files
|
||||
- Make GenerateArtifacts return a dictionary
|
||||
- Split new output file methods
|
||||
- Fix up some file path methods
|
||||
- Fix broken build
|
||||
- Rearrange some BaseProcessor methods
|
||||
- Fix new AddToArchive methods
|
||||
- Fix recursive issue in AddToArchive
|
||||
- Add unused passable func to OutputFile
|
||||
- Pass in new func for OutputFile
|
||||
- Use new func in Redumper
|
||||
- Add runtime error for improperly created artifacts
|
||||
- Minor tweaks to existing code
|
||||
- Add new, unused CheckAllOutputFilesExist variant
|
||||
- Rename new method to CheckRequiredFiles
|
||||
- Use simplified CheckAllOutputFilesExist
|
||||
- Create and use RegexOutputFile
|
||||
- Add archive override for RegexOutputFile
|
||||
- Less confusing implmentation of DatfileExists
|
||||
- Forgot the other locations
|
||||
- Add future XGD output files
|
||||
- Add and use CustomOutputFile
|
||||
- Fix access permissions of output file classes
|
||||
- Handle XGD required files
|
||||
- Forgot to assume directories don't exist
|
||||
- Fix typo in publisher identifiers
|
||||
- Ensure manufacturer files starting from 0 are zipped in redumper DVD (TurnedToast)
|
||||
- Add _drive.txt file to GetOutputFiles for UmdImageCreator (TurnedToast)
|
||||
|
||||
### 3.2.1 (2024-08-05)
|
||||
|
||||
- Add nuget packing for processors and contexts
|
||||
- Address build warnings for packages
|
||||
- Add Linux ARM64 as target by default
|
||||
- Fix nuget package naming
|
||||
- Forgot to upload packages to release
|
||||
- Add `osx-arm64` to libraries
|
||||
- Better support build matricies
|
||||
- Show script settings
|
||||
- Add flag values to script settings
|
||||
- Enable last runtime by default
|
||||
- Update README with new build matricies
|
||||
- Remove empty gitmodules
|
||||
- Purple
|
||||
- Separate themes into own namespace and files
|
||||
- Seal all theme classes
|
||||
- Add preliminary MPF.CLI
|
||||
- Add CLI build status to README
|
||||
- Add CLI information to README
|
||||
- Save default config values for CLI
|
||||
- Allow custom parameters for CLI
|
||||
- Load options before anything else
|
||||
- Dispose of stream when creating config
|
||||
- Try to make config safer for CLI
|
||||
- Blindly assume the path exists
|
||||
- Add CLI status output on runtime
|
||||
- Ensure tracks are assigned in Aaru
|
||||
- Custom theme colors
|
||||
- Use speed for CLI from configuration
|
||||
- Fix minimum number of args checks
|
||||
- Move GetDefaultSpeedForMediaType to common location
|
||||
- Move some Check-specific methods
|
||||
- Add some custom CLI parameters
|
||||
- Try out custom options classes
|
||||
- Simplify custom parameters warning
|
||||
- Fix CLI help text alignment
|
||||
- Bring Check and CLI in parity with param processing
|
||||
- Remove now-unncessary names
|
||||
- Don't set MediaType if parameters ambiguous
|
||||
- Fix parameters after extension change
|
||||
- Fix logic for deducing region from PlayStation ISN (JohnVeness)
|
||||
- Fix broken test logic
|
||||
- Remove RedumpLib tests
|
||||
- Change to generic wording in report (JohnVeness)
|
||||
- Add include artifacts flag for check, sanitize options
|
||||
- Remove old --protect-file mentions (JohnVeness)
|
||||
- Update RedumpLib to 1.4.1
|
||||
- Enable loading seed JSON (Deterous)
|
||||
|
||||
### 3.2.0 (2024-06-20)
|
||||
|
||||
- Create currently-unused processors
|
||||
|
||||
25
MPF.CLI/ConsoleLogger.cs
Normal file
25
MPF.CLI/ConsoleLogger.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Frontend;
|
||||
|
||||
namespace MPF.CLI
|
||||
{
|
||||
public static class ConsoleLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Simple process counter to write to console
|
||||
/// </summary>
|
||||
public static void ProgressUpdated(object? sender, ResultEventArgs value)
|
||||
{
|
||||
Console.WriteLine(value.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple process counter to write to console
|
||||
/// </summary>
|
||||
public static void ProgressUpdated(object? sender, ProtectionProgress value)
|
||||
{
|
||||
Console.WriteLine($"{value.Percentage * 100:N2}%: {value.Filename} - {value.Protection}");
|
||||
}
|
||||
}
|
||||
}
|
||||
61
MPF.CLI/MPF.CLI.csproj
Normal file
61
MPF.CLI/MPF.CLI.csproj
Normal file
@@ -0,0 +1,61 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.2.2</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Title>MPF CLI</Title>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>CLI 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>
|
||||
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- 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>
|
||||
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.13" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
339
MPF.CLI/Program.cs
Normal file
339
MPF.CLI/Program.cs
Normal file
@@ -0,0 +1,339 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
#if NET40
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Frontend;
|
||||
using MPF.Frontend.Tools;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
namespace MPF.CLI
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Load options from the config file
|
||||
var options = OptionsLoader.LoadFromConfig();
|
||||
if (options.FirstRun)
|
||||
{
|
||||
// Application paths
|
||||
options.AaruPath = "FILL ME IN";
|
||||
options.DiscImageCreatorPath = "FILL ME IN";
|
||||
options.RedumperPath = "FILL ME IN";
|
||||
options.InternalProgram = InternalProgram.NONE;
|
||||
|
||||
// Reset first run
|
||||
options.FirstRun = false;
|
||||
OptionsLoader.SaveToConfig(options, saveDefault: true);
|
||||
}
|
||||
|
||||
// Try processing the standalone arguments
|
||||
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
|
||||
if (standaloneProcessed != false)
|
||||
{
|
||||
if (standaloneProcessed == null)
|
||||
DisplayHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
// Try processing the common arguments
|
||||
(bool success, MediaType mediaType, RedumpSystem? knownSystem, var error) = OptionsLoader.ProcessCommonArguments(args);
|
||||
if (!success)
|
||||
{
|
||||
DisplayHelp(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate the supplied credentials
|
||||
(bool? _, string? message) = RedumpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).GetAwaiter().GetResult();
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
Console.WriteLine(message);
|
||||
|
||||
// Process any custom parameters
|
||||
(CommandOptions opts, int startIndex) = LoadFromArguments(args, options, startIndex: 2);
|
||||
|
||||
// Validate the internal program
|
||||
switch (options.InternalProgram)
|
||||
{
|
||||
case InternalProgram.Aaru:
|
||||
if (!File.Exists(options.AaruPath))
|
||||
{
|
||||
DisplayHelp("A path needs to be supplied for Aaru, exiting...");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case InternalProgram.DiscImageCreator:
|
||||
if (!File.Exists(options.DiscImageCreatorPath))
|
||||
{
|
||||
DisplayHelp("A path needs to be supplied for DIC, exiting...");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case InternalProgram.Redumper:
|
||||
if (!File.Exists(options.RedumperPath))
|
||||
{
|
||||
DisplayHelp("A path needs to be supplied for Redumper, exiting...");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
DisplayHelp($"{options.InternalProgram} is not a supported dumping program, exiting...");
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure we have the values we need
|
||||
if (opts.CustomParams == null && (opts.DevicePath == null || opts.FilePath == null))
|
||||
{
|
||||
DisplayHelp("Both a device path and file path need to be supplied, exiting...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the speed from the options
|
||||
int speed = opts.DriveSpeed ?? FrontendTool.GetDefaultSpeedForMediaType(mediaType, options);
|
||||
|
||||
// Populate an environment
|
||||
var drive = Drive.Create(null, opts.DevicePath ?? string.Empty);
|
||||
var env = new DumpEnvironment(options,
|
||||
opts.FilePath,
|
||||
drive,
|
||||
knownSystem,
|
||||
mediaType,
|
||||
options.InternalProgram,
|
||||
parameters: null);
|
||||
|
||||
// Process the parameters
|
||||
string? paramStr = opts.CustomParams ?? env.GetFullParameters(speed);
|
||||
if (string.IsNullOrEmpty(paramStr))
|
||||
{
|
||||
DisplayHelp("No valid environment could be created, exiting...");
|
||||
return;
|
||||
}
|
||||
env.SetExecutionContext(paramStr);
|
||||
|
||||
// Make new Progress objects
|
||||
var resultProgress = new Progress<ResultEventArgs>();
|
||||
resultProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
var protectionProgress = new Progress<ProtectionProgress>();
|
||||
protectionProgress.ProgressChanged += ConsoleLogger.ProgressUpdated;
|
||||
|
||||
// Invoke the dumping program
|
||||
Console.WriteLine($"Invoking {options.InternalProgram} using '{paramStr}'");
|
||||
var dumpResult = env.Run(resultProgress).GetAwaiter().GetResult();
|
||||
Console.WriteLine(dumpResult.Message);
|
||||
if (!dumpResult)
|
||||
return;
|
||||
|
||||
// If it was not a dumping command
|
||||
if (!env.IsDumpingCommand())
|
||||
{
|
||||
Console.WriteLine("Execution not recognized as dumping command, skipping processing...");
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have a mounted path, replace the environment
|
||||
if (opts.MountedPath != null && Directory.Exists(opts.MountedPath))
|
||||
{
|
||||
drive = Drive.Create(null, opts.MountedPath);
|
||||
env = new DumpEnvironment(options,
|
||||
opts.FilePath,
|
||||
drive,
|
||||
knownSystem,
|
||||
mediaType,
|
||||
internalProgram: null,
|
||||
parameters: null);
|
||||
}
|
||||
|
||||
// Finally, attempt to do the output dance
|
||||
#if NET40
|
||||
var verifyTask = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress);
|
||||
verifyTask.Wait();
|
||||
var verifyResult = verifyTask.Result;
|
||||
#else
|
||||
var verifyResult = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
#endif
|
||||
Console.WriteLine(verifyResult.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Display help for MPF.CLI
|
||||
/// </summary>
|
||||
/// <param name="error">Error string to prefix the help text with</param>
|
||||
private static void DisplayHelp(string? error = null)
|
||||
{
|
||||
if (error != null)
|
||||
Console.WriteLine(error);
|
||||
|
||||
Console.WriteLine("Usage:");
|
||||
Console.WriteLine("MPF.CLI <mediatype> <system> [options]");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Standalone Options:");
|
||||
Console.WriteLine("-h, -? Show this help text");
|
||||
Console.WriteLine("-lc, --listcodes List supported comment/content site codes");
|
||||
Console.WriteLine("-lm, --listmedia List supported media types");
|
||||
Console.WriteLine("-ls, --listsystems List supported system types");
|
||||
Console.WriteLine("-lp, --listprograms List supported dumping program outputs");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("CLI Options:");
|
||||
Console.WriteLine("-u, --use <program> Override default dumping program");
|
||||
Console.WriteLine("-d, --device <devicepath> Physical drive path (Required if no custom parameters set)");
|
||||
Console.WriteLine("-m, --mounted <dirpath> Mounted filesystem path for additional checks");
|
||||
Console.WriteLine("-f, --file \"<filepath>\" Output file path (Required if no custom parameters set)");
|
||||
Console.WriteLine("-s, --speed <speed> Override default dumping speed");
|
||||
Console.WriteLine("-c, --custom \"<params>\" Custom parameters to use");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Custom dumping parameters, if used, will fully replace the default parameters.");
|
||||
Console.WriteLine("All dumping parameters need to be supplied if doing this.");
|
||||
Console.WriteLine("Otherwise, both a drive path and output file path are required.");
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Mounted filesystem path is only recommended on OSes that require block");
|
||||
Console.WriteLine("device dumping, usually Linux and macOS.");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from application arguments
|
||||
/// </summary>
|
||||
private static (CommandOptions, int) LoadFromArguments(string[] args, Frontend.Options options, int startIndex = 0)
|
||||
{
|
||||
// Create return values
|
||||
var opts = new CommandOptions();
|
||||
|
||||
// If we have no arguments, just return
|
||||
if (args == null || args.Length == 0)
|
||||
return (opts, 0);
|
||||
|
||||
// If we have an invalid start index, just return
|
||||
if (startIndex < 0 || startIndex >= args.Length)
|
||||
return (opts, startIndex);
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
{
|
||||
// Use specific program
|
||||
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
string internalProgram = args[startIndex].Split('=')[1];
|
||||
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
string internalProgram = args[startIndex + 1];
|
||||
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Use a device path
|
||||
else if (args[startIndex].StartsWith("-d=") || args[startIndex].StartsWith("--device="))
|
||||
{
|
||||
opts.DevicePath = args[startIndex].Split('=')[1].Trim('"');
|
||||
}
|
||||
else if (args[startIndex] == "-d" || args[startIndex] == "--device")
|
||||
{
|
||||
opts.DevicePath = args[startIndex + 1].Trim('"');
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Use a mounted path for physical checks
|
||||
else if (args[startIndex].StartsWith("-m=") || args[startIndex].StartsWith("--mounted="))
|
||||
{
|
||||
opts.MountedPath = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-m" || args[startIndex] == "--mounted")
|
||||
{
|
||||
opts.MountedPath = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Use a file path
|
||||
else if (args[startIndex].StartsWith("-f=") || args[startIndex].StartsWith("--file="))
|
||||
{
|
||||
opts.FilePath = args[startIndex].Split('=')[1].Trim('"');
|
||||
}
|
||||
else if (args[startIndex] == "-f" || args[startIndex] == "--file")
|
||||
{
|
||||
opts.FilePath = args[startIndex + 1].Trim('"');
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Set an override speed
|
||||
else if (args[startIndex].StartsWith("-s=") || args[startIndex].StartsWith("--speed="))
|
||||
{
|
||||
if (!int.TryParse(args[startIndex].Split('=')[1].Trim('"'), out int speed))
|
||||
speed = -1;
|
||||
|
||||
opts.DriveSpeed = speed;
|
||||
}
|
||||
else if (args[startIndex] == "-s" || args[startIndex] == "--speed")
|
||||
{
|
||||
if (!int.TryParse(args[startIndex + 1].Trim('"'), out int speed))
|
||||
speed = -1;
|
||||
|
||||
opts.DriveSpeed = speed;
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Use a custom parameters
|
||||
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--custom="))
|
||||
{
|
||||
opts.CustomParams = args[startIndex].Split('=')[1].Trim('"');
|
||||
}
|
||||
else if (args[startIndex] == "-c" || args[startIndex] == "--custom")
|
||||
{
|
||||
opts.CustomParams = args[startIndex + 1].Trim('"');
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (opts, startIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents commandline options
|
||||
/// </summary>
|
||||
private class CommandOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Path to the device to dump
|
||||
/// </summary>
|
||||
/// <remarks>Required if custom parameters are not set</remarks>
|
||||
public string? DevicePath { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Path to the mounted filesystem to check
|
||||
/// </summary>
|
||||
/// <remarks>Should only be used when the device path is not readable</remarks>
|
||||
public string? MountedPath { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Path to the output file
|
||||
/// </summary>
|
||||
/// <remarks>Required if custom parameters are not set</remarks>
|
||||
public string? FilePath { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Override drive speed
|
||||
/// </summary>
|
||||
public int? DriveSpeed { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Custom parameters for dumping
|
||||
/// </summary>
|
||||
public string? CustomParams { get; set; } = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,45 +1,65 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- 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.2.0</VersionPrefix>
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<OutputType>Exe</OutputType>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.2.2</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Title>MPF Check</Title>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Validator for various dumping programs</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
<!-- Package Properties -->
|
||||
<Title>MPF Check</Title>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Validator for various dumping programs</Description>
|
||||
<Copyright>Copyright (c) Matt Nadareski 2019-2024</Copyright>
|
||||
<PackageProjectUrl>https://github.com/SabreTools/</PackageProjectUrl>
|
||||
<RepositoryUrl>https://github.com/SabreTools/MPF</RepositoryUrl>
|
||||
<RepositoryType>git</RepositoryType>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.13" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="MinAsyncBridge" Version="0.12.4" />
|
||||
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
|
||||
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.13" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,8 +1,13 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
#if NET40
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
using BinaryObjectScanner;
|
||||
using MPF.Frontend;
|
||||
using MPF.Frontend.Tools;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using SabreTools.RedumpLib.Web;
|
||||
|
||||
@@ -12,6 +17,34 @@ namespace MPF.Check
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
// Create a default options object
|
||||
var options = new Frontend.Options()
|
||||
{
|
||||
// Internal Program
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
|
||||
// Extra Dumping Options
|
||||
ScanForProtection = false,
|
||||
AddPlaceholders = true,
|
||||
PullAllInformation = false,
|
||||
AddFilenameSuffix = false,
|
||||
OutputSubmissionJSON = false,
|
||||
IncludeArtifacts = false,
|
||||
CompressLogFiles = false,
|
||||
DeleteUnnecessaryFiles = false,
|
||||
CreateIRDAfterDumping = false,
|
||||
|
||||
// Protection Scanning Options
|
||||
ScanArchivesForProtection = true,
|
||||
ScanPackersForProtection = false,
|
||||
IncludeDebugProtectionInformation = false,
|
||||
HideDriveLetters = false,
|
||||
|
||||
// Redump Login Information
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
};
|
||||
|
||||
// Try processing the standalone arguments
|
||||
bool? standaloneProcessed = OptionsLoader.ProcessStandaloneArguments(args);
|
||||
if (standaloneProcessed != false)
|
||||
@@ -30,7 +63,7 @@ namespace MPF.Check
|
||||
}
|
||||
|
||||
// Loop through and process options
|
||||
(var options, var seedInfo, var path, int startIndex) = OptionsLoader.LoadFromArguments(args, startIndex: 2);
|
||||
(CommandOptions opts, int startIndex) = LoadFromArguments(args, options, startIndex: 2);
|
||||
if (options.InternalProgram == InternalProgram.NONE)
|
||||
{
|
||||
DisplayHelp("A program name needs to be provided");
|
||||
@@ -44,11 +77,7 @@ 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();
|
||||
#endif
|
||||
(bool? _, string? message) = RedumpClient.ValidateCredentials(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty).GetAwaiter().GetResult();
|
||||
if (!string.IsNullOrEmpty(message))
|
||||
Console.WriteLine(message);
|
||||
|
||||
@@ -67,19 +96,13 @@ namespace MPF.Check
|
||||
|
||||
// Now populate an environment
|
||||
Drive? drive = null;
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
drive = Drive.Create(null, path!);
|
||||
if (!string.IsNullOrEmpty(opts.DevicePath))
|
||||
drive = Drive.Create(null, opts.DevicePath!);
|
||||
|
||||
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
|
||||
var result = env.VerifyAndSaveDumpOutput(resultProgress, protectionProgress, seedInfo: opts.Seed).GetAwaiter().GetResult();
|
||||
Console.WriteLine(result.Message);
|
||||
}
|
||||
}
|
||||
@@ -94,7 +117,7 @@ namespace MPF.Check
|
||||
Console.WriteLine(error);
|
||||
|
||||
Console.WriteLine("Usage:");
|
||||
Console.WriteLine("MPF.Check.exe <mediatype> <system> [options] </path/to/output.cue/iso> ...");
|
||||
Console.WriteLine("MPF.Check <mediatype> <system> [options] </path/to/output.cue/iso> ...");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Standalone Options:");
|
||||
Console.WriteLine("-h, -? Show this help text");
|
||||
@@ -105,12 +128,212 @@ namespace MPF.Check
|
||||
Console.WriteLine();
|
||||
|
||||
Console.WriteLine("Check Options:");
|
||||
var supportedArguments = OptionsLoader.PrintSupportedArguments();
|
||||
foreach (string argument in supportedArguments)
|
||||
{
|
||||
Console.WriteLine(argument);
|
||||
}
|
||||
Console.WriteLine("-u, --use <program> Dumping program output type [REQUIRED]");
|
||||
Console.WriteLine(" --load-seed <path> Load a seed submission JSON for user information");
|
||||
Console.WriteLine(" --no-placeholders Disable placeholder values in submission info");
|
||||
Console.WriteLine(" --create-ird Create IRD from output files (PS3 only)");
|
||||
Console.WriteLine("-c, --credentials <user> <pw> Redump username and password");
|
||||
Console.WriteLine(" --pull-all Pull all information from Redump (requires --credentials)");
|
||||
Console.WriteLine("-p, --path <drivepath> Physical drive path for additional checks");
|
||||
Console.WriteLine("-s, --scan Enable copy protection scan (requires --path)");
|
||||
Console.WriteLine(" --disable-archives Disable scanning archives (requires --scan)");
|
||||
Console.WriteLine(" --enable-packers Enable scanning for packers (requires --scan)");
|
||||
Console.WriteLine(" --enable-debug Enable debug protection information (requires --scan)");
|
||||
Console.WriteLine(" --hide-drive-letters Hide drive letters from scan output (requires --scan)");
|
||||
Console.WriteLine("-x, --suffix Enable adding filename suffix");
|
||||
Console.WriteLine("-j, --json Enable submission JSON output");
|
||||
Console.WriteLine(" --include-artifacts Include artifacts in JSON (requires --json)");
|
||||
Console.WriteLine("-z, --zip Enable log file compression");
|
||||
Console.WriteLine("-d, --delete Enable unnecessary file deletion");
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from application arguments
|
||||
/// </summary>
|
||||
private static (CommandOptions, int) LoadFromArguments(string[] args, Frontend.Options options, int startIndex = 0)
|
||||
{
|
||||
// Create return values
|
||||
var opts = new CommandOptions();
|
||||
|
||||
// These values require multiple parts to be active
|
||||
bool scan = false,
|
||||
enableArchives = true,
|
||||
enablePackers = false,
|
||||
enableDebug = false,
|
||||
hideDriveLetters = false;
|
||||
|
||||
// If we have no arguments, just return
|
||||
if (args == null || args.Length == 0)
|
||||
return (opts, 0);
|
||||
|
||||
// If we have an invalid start index, just return
|
||||
if (startIndex < 0 || startIndex >= args.Length)
|
||||
return (opts, startIndex);
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
{
|
||||
// Use specific program
|
||||
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
string internalProgram = args[startIndex].Split('=')[1];
|
||||
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
string internalProgram = args[startIndex + 1];
|
||||
options.InternalProgram = Frontend.Options.ToInternalProgram(internalProgram);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Include seed info file
|
||||
else if (args[startIndex].StartsWith("--load-seed="))
|
||||
{
|
||||
string seedInfo = args[startIndex].Split('=')[1];
|
||||
opts.Seed = Builder.CreateFromFile(seedInfo);
|
||||
}
|
||||
else if (args[startIndex] == "--load-seed")
|
||||
{
|
||||
string seedInfo = args[startIndex + 1];
|
||||
opts.Seed = Builder.CreateFromFile(seedInfo);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Disable placeholder values in submission info
|
||||
else if (args[startIndex].Equals("--no-placeholders"))
|
||||
{
|
||||
options.AddPlaceholders = false;
|
||||
}
|
||||
|
||||
// Create IRD from output files (PS3 only)
|
||||
else if (args[startIndex].Equals("--create-ird"))
|
||||
{
|
||||
options.CreateIRDAfterDumping = true;
|
||||
}
|
||||
|
||||
// Redump login
|
||||
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
{
|
||||
string[] credentials = args[startIndex].Split('=')[1].Split(';');
|
||||
options.RedumpUsername = credentials[0];
|
||||
options.RedumpPassword = credentials[1];
|
||||
}
|
||||
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
|
||||
{
|
||||
options.RedumpUsername = args[startIndex + 1];
|
||||
options.RedumpPassword = args[startIndex + 2];
|
||||
startIndex += 2;
|
||||
}
|
||||
|
||||
// Pull all information (requires Redump login)
|
||||
else if (args[startIndex].Equals("--pull-all"))
|
||||
{
|
||||
options.PullAllInformation = true;
|
||||
}
|
||||
|
||||
// Use a device path for physical checks
|
||||
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
|
||||
{
|
||||
opts.DevicePath = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
|
||||
{
|
||||
opts.DevicePath = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Scan for protection (requires device path)
|
||||
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
|
||||
{
|
||||
scan = true;
|
||||
}
|
||||
|
||||
// Disable scanning archives (requires --scan)
|
||||
else if (args[startIndex].Equals("--disable-archives"))
|
||||
{
|
||||
enableArchives = false;
|
||||
}
|
||||
|
||||
// Enable scanning for packers (requires --scan)
|
||||
else if (args[startIndex].Equals("--enable-packers"))
|
||||
{
|
||||
enablePackers = true;
|
||||
}
|
||||
|
||||
// Enable debug protection information (requires --scan)
|
||||
else if (args[startIndex].Equals("--enable-debug"))
|
||||
{
|
||||
enableDebug = true;
|
||||
}
|
||||
|
||||
// Hide drive letters from scan output (requires --scan)
|
||||
else if (args[startIndex].Equals("--hide-drive-letters"))
|
||||
{
|
||||
hideDriveLetters = true;
|
||||
}
|
||||
|
||||
// Add filename suffix
|
||||
else if (args[startIndex].Equals("-x") || args[startIndex].Equals("--suffix"))
|
||||
{
|
||||
options.AddFilenameSuffix = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
|
||||
{
|
||||
options.OutputSubmissionJSON = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("--include-artifacts"))
|
||||
{
|
||||
options.IncludeArtifacts = true;
|
||||
}
|
||||
|
||||
// Compress log and extraneous files
|
||||
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
|
||||
{
|
||||
options.CompressLogFiles = true;
|
||||
}
|
||||
|
||||
// Delete unnecessary files files
|
||||
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
|
||||
{
|
||||
options.DeleteUnnecessaryFiles = true;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now deal with the complex options
|
||||
options.ScanForProtection = scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
options.ScanArchivesForProtection = enableArchives && scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
options.ScanPackersForProtection = enablePackers && scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
options.IncludeDebugProtectionInformation = enableDebug && scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(opts.DevicePath);
|
||||
|
||||
return (opts, startIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents commandline options
|
||||
/// </summary>
|
||||
private class CommandOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Seed submission info from an input file
|
||||
/// </summary>
|
||||
public SubmissionInfo? Seed { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Path to the device to scan
|
||||
/// </summary>
|
||||
public string? DevicePath { get; set; } = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ namespace MPF.ExecutionContexts.Aaru
|
||||
#region Generic Dumping Information
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? InputPath => InputValue;
|
||||
public override string? InputPath => InputValue?.Trim('"');
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? OutputPath => OutputValue;
|
||||
public override string? OutputPath => OutputValue?.Trim('"');
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int? Speed
|
||||
@@ -1082,7 +1082,11 @@ namespace MPF.ExecutionContexts.Aaru
|
||||
if (string.IsNullOrEmpty(InputValue))
|
||||
return null;
|
||||
|
||||
parameters.Add(InputValue!.TrimEnd('\\'));
|
||||
if (InputValue.Contains(' '))
|
||||
parameters.Add($"\"{InputValue!.TrimEnd('\\')}\"");
|
||||
else
|
||||
parameters.Add(InputValue!.TrimEnd('\\'));
|
||||
|
||||
break;
|
||||
|
||||
// Two input values
|
||||
|
||||
@@ -315,14 +315,6 @@ namespace MPF.ExecutionContexts
|
||||
return supported.Contains(flag);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a string is a valid drive letter
|
||||
/// </summary>
|
||||
/// <param name="parameter">String value to check</param>
|
||||
/// <returns>True if it's a valid drive letter, false otherwise</W>
|
||||
protected static bool IsValidDriveLetter(string parameter)
|
||||
=> Regex.IsMatch(parameter, @"^[A-Z]:?\\?$");
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a string is a valid bool
|
||||
/// </summary>
|
||||
|
||||
@@ -15,10 +15,10 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
#region Generic Dumping Information
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? InputPath => DrivePath;
|
||||
public override string? InputPath => DrivePath?.Trim('"');
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? OutputPath => Filename;
|
||||
public override string? OutputPath => Filename?.Trim('"');
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <inheritdoc/>
|
||||
@@ -419,9 +419,16 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
|| BaseCommand == CommandStrings.XGD3Swap)
|
||||
{
|
||||
if (DrivePath != null)
|
||||
parameters.Add(DrivePath);
|
||||
{
|
||||
if (DrivePath.Contains(' '))
|
||||
parameters.Add($"\"{DrivePath}\"");
|
||||
else
|
||||
parameters.Add(DrivePath);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Filename
|
||||
@@ -1074,10 +1081,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count < 6)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1106,10 +1111,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count < 4)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1128,10 +1131,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count != 2)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -1139,10 +1140,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count < 4)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1161,10 +1160,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count < 6)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1193,10 +1190,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count < 4)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1215,10 +1210,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count != 3)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1231,21 +1224,16 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count != 2)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
break;
|
||||
|
||||
case CommandStrings.Eject:
|
||||
if (parts.Count != 2)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -1253,10 +1241,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count != 3)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1269,10 +1255,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count < 4)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1318,10 +1302,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count != 2)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -1329,10 +1311,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count < 4)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1351,10 +1331,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count != 2)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -1362,10 +1340,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count != 2)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
break;
|
||||
|
||||
@@ -1384,10 +1360,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count < 4)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1423,10 +1397,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count < 4)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
@@ -1447,10 +1419,8 @@ namespace MPF.ExecutionContexts.DiscImageCreator
|
||||
if (parts.Count < 4)
|
||||
return false;
|
||||
|
||||
if (!IsValidDriveLetter(parts[1]))
|
||||
return false;
|
||||
else
|
||||
DrivePath = parts[1];
|
||||
// Blindly assume the path exists
|
||||
DrivePath = parts[1];
|
||||
|
||||
if (IsFlagSupported(parts[2]))
|
||||
return false;
|
||||
|
||||
@@ -1,45 +1,59 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- 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.2.0</VersionPrefix>
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.2.2</VersionPrefix>
|
||||
<WarningsNotAsErrors>NU5104</WarningsNotAsErrors>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Common code for all MPF execution contexts</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>
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Common code for all MPF execution contexts</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>
|
||||
<InternalsVisibleTo Include="MPF.Test" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="MinAsyncBridge" Version="0.12.4" />
|
||||
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
|
||||
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="IndexRange" Version="1.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
|
||||
</ItemGroup>
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="MinAsyncBridge" Version="0.12.4" />
|
||||
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
|
||||
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net4`)) AND !$(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="IndexRange" Version="1.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="!$(TargetFramework.StartsWith(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -15,10 +15,13 @@ namespace MPF.ExecutionContexts.Redumper
|
||||
#region Generic Dumping Information
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? InputPath => DriveValue;
|
||||
public override string? InputPath => DriveValue?.Trim('"');
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string? OutputPath => Path.Combine(ImagePathValue?.Trim('"') ?? string.Empty, ImageNameValue?.Trim('"') ?? string.Empty) + GetDefaultExtension(this.Type);
|
||||
public override string? OutputPath => Path.Combine(
|
||||
ImagePathValue?.Trim('"') ?? string.Empty,
|
||||
ImageNameValue?.Trim('"') ?? string.Empty)
|
||||
+ GetDefaultExtension(this.Type);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int? Speed => SpeedValue;
|
||||
@@ -264,7 +267,12 @@ namespace MPF.ExecutionContexts.Redumper
|
||||
if (this[FlagStrings.Drive] == true)
|
||||
{
|
||||
if (DriveValue != null)
|
||||
parameters.Add($"{FlagStrings.Drive}={DriveValue}");
|
||||
{
|
||||
if (DriveValue.Contains(' '))
|
||||
parameters.Add($"{FlagStrings.Drive}=\"{DriveValue}\"");
|
||||
else
|
||||
parameters.Add($"{FlagStrings.Drive}={DriveValue}");
|
||||
}
|
||||
}
|
||||
|
||||
// Speed
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
#if NET462_OR_GREATER || NETCOREAPP
|
||||
using Microsoft.Management.Infrastructure;
|
||||
using Microsoft.Management.Infrastructure.Generic;
|
||||
#endif
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SabreTools.IO;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Frontend
|
||||
@@ -235,548 +229,6 @@ namespace MPF.Frontend
|
||||
|
||||
#endregion
|
||||
|
||||
#region Information Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Get the EXE name from a PlayStation disc, if possible
|
||||
/// </summary>
|
||||
/// <returns>Executable name on success, null otherwise</returns>
|
||||
public string? GetPlayStationExecutableName()
|
||||
{
|
||||
// If there's no drive path, we can't get exe name
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't get exe name
|
||||
if (!Directory.Exists(Name))
|
||||
return null;
|
||||
|
||||
// Get the two paths that we will need to check
|
||||
string psxExePath = Path.Combine(Name, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(Name, "SYSTEM.CNF");
|
||||
|
||||
// Read the CNF file as an INI file
|
||||
var systemCnf = new IniFile(systemCnfPath);
|
||||
string? bootValue = string.Empty;
|
||||
|
||||
// PlayStation uses "BOOT" as the key
|
||||
if (systemCnf.ContainsKey("BOOT"))
|
||||
bootValue = systemCnf["BOOT"];
|
||||
|
||||
// PlayStation 2 uses "BOOT2" as the key
|
||||
if (systemCnf.ContainsKey("BOOT2"))
|
||||
bootValue = systemCnf["BOOT2"];
|
||||
|
||||
// If we had any boot value, parse it and get the executable name
|
||||
if (!string.IsNullOrEmpty(bootValue))
|
||||
{
|
||||
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)", RegexOptions.Compiled);
|
||||
if (match.Groups.Count > 1)
|
||||
{
|
||||
string? serial = match.Groups[1].Value;
|
||||
|
||||
// Some games may have the EXE in a subfolder
|
||||
serial = Path.GetFileName(serial);
|
||||
|
||||
return serial;
|
||||
}
|
||||
}
|
||||
|
||||
// If the SYSTEM.CNF value can't be found, try PSX.EXE
|
||||
if (File.Exists(psxExePath))
|
||||
return "PSX.EXE";
|
||||
|
||||
// If neither can be found, we return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the EXE date from a PlayStation disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="serial">Internal disc serial, if possible</param>
|
||||
/// <param name="region">Output region, if possible</param>
|
||||
/// <param name="date">Output EXE date in "yyyy-mm-dd" format if possible, null on error</param>
|
||||
/// <returns>True if information could be determined, false otherwise</returns>
|
||||
public bool GetPlayStationExecutableInfo(out string? serial, out Region? region, out string? date)
|
||||
{
|
||||
serial = null; region = null; date = null;
|
||||
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
return false;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(Name))
|
||||
return false;
|
||||
|
||||
// Get the executable name
|
||||
string? exeName = GetPlayStationExecutableName();
|
||||
|
||||
// If no executable found, we can't do this part
|
||||
if (exeName == null)
|
||||
return false;
|
||||
|
||||
// EXE name may have a trailing `;` after
|
||||
// EXE name should always be in all caps
|
||||
exeName = exeName
|
||||
.Split(';')[0]
|
||||
.ToUpperInvariant();
|
||||
|
||||
// Serial is most of the EXE name normalized
|
||||
serial = exeName
|
||||
.Replace('_', '-')
|
||||
.Replace(".", string.Empty);
|
||||
|
||||
// Get the region, if possible
|
||||
region = GetPlayStationRegion(exeName);
|
||||
|
||||
// Now that we have the EXE name, try to get the fileinfo for it
|
||||
string exePath = Path.Combine(Name, exeName);
|
||||
if (!File.Exists(exePath))
|
||||
return false;
|
||||
|
||||
// Fix the Y2K timestamp issue
|
||||
var fi = new FileInfo(exePath);
|
||||
var dt = new DateTime(fi.LastWriteTimeUtc.Year >= 1900 && fi.LastWriteTimeUtc.Year < 1920 ? 2000 + fi.LastWriteTimeUtc.Year % 100 : fi.LastWriteTimeUtc.Year,
|
||||
fi.LastWriteTimeUtc.Month, fi.LastWriteTimeUtc.Day);
|
||||
date = dt.ToString("yyyy-MM-dd");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the region based on the PlayStation serial code
|
||||
/// </summary>
|
||||
/// <param name="serial">PlayStation serial code</param>
|
||||
/// <returns>Region mapped from name, if possible</returns>
|
||||
public static Region? GetPlayStationRegion(string? serial)
|
||||
{
|
||||
// If we have a fully invalid serial
|
||||
if (string.IsNullOrEmpty(serial))
|
||||
return null;
|
||||
|
||||
// Standardized "S" serials
|
||||
if (serial!.StartsWith("S"))
|
||||
{
|
||||
// string publisher = serial[0] + serial[1];
|
||||
// char secondRegion = serial[3];
|
||||
switch (serial[2])
|
||||
{
|
||||
case 'A': return Region.Asia;
|
||||
case 'C': return Region.China;
|
||||
case 'E': return Region.Europe;
|
||||
case 'K': return Region.SouthKorea;
|
||||
case 'U': return Region.UnitedStatesOfAmerica;
|
||||
case 'P':
|
||||
// Region of S_P_ serials may be Japan, Asia, or SouthKorea
|
||||
return serial[3] switch
|
||||
{
|
||||
// Check first two digits of S_PS serial
|
||||
'S' => (Region?)(serial.Substring(5, 2) switch
|
||||
{
|
||||
"46" => Region.SouthKorea,
|
||||
"51" => Region.Asia,
|
||||
"56" => Region.SouthKorea,
|
||||
"55" => Region.Asia,
|
||||
_ => Region.Japan,
|
||||
}),
|
||||
|
||||
// Check first three digits of S_PM serial
|
||||
'M' => (Region?)(serial.Substring(5, 3) switch
|
||||
{
|
||||
"645" => Region.SouthKorea,
|
||||
"675" => Region.SouthKorea,
|
||||
"885" => Region.SouthKorea,
|
||||
_ => Region.Japan, // Remaining S_PM serials may be Japan or Asia
|
||||
}),
|
||||
_ => (Region?)Region.Japan,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Japan-only special serial
|
||||
else if (serial.StartsWith("PAPX"))
|
||||
return Region.Japan;
|
||||
|
||||
// Region appears entirely random
|
||||
else if (serial.StartsWith("PABX"))
|
||||
return null;
|
||||
|
||||
// Region appears entirely random
|
||||
else if (serial.StartsWith("PBPX"))
|
||||
return null;
|
||||
|
||||
// Japan-only special serial
|
||||
else if (serial.StartsWith("PCBX"))
|
||||
return Region.Japan;
|
||||
|
||||
// Japan-only special serial
|
||||
else if (serial.StartsWith("PCXC"))
|
||||
return Region.Japan;
|
||||
|
||||
// Single disc known, Japan
|
||||
else if (serial.StartsWith("PDBX"))
|
||||
return Region.Japan;
|
||||
|
||||
// Single disc known, Europe
|
||||
else if (serial.StartsWith("PEBX"))
|
||||
return Region.Europe;
|
||||
|
||||
// Single disc known, USA
|
||||
else if (serial.StartsWith("PUBX"))
|
||||
return Region.UnitedStatesOfAmerica;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 2 disc, if possible
|
||||
/// </summary>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
public string? GetPlayStation2Version()
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(Name))
|
||||
return null;
|
||||
|
||||
// Get the SYSTEM.CNF path to check
|
||||
string systemCnfPath = Path.Combine(Name, "SYSTEM.CNF");
|
||||
|
||||
// Try to parse the SYSTEM.CNF file
|
||||
var systemCnf = new IniFile(systemCnfPath);
|
||||
if (systemCnf.ContainsKey("VER"))
|
||||
return systemCnf["VER"];
|
||||
|
||||
// If "VER" can't be found, we can't do much
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
public string? GetPlayStation3Serial()
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(Name))
|
||||
return null;
|
||||
|
||||
// Attempt to use PS3_DISC.SFB
|
||||
string sfbPath = Path.Combine(Name, "PS3_DISC.SFB");
|
||||
if (File.Exists(sfbPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(sfbPath));
|
||||
br.BaseStream.Seek(0x220, SeekOrigin.Begin);
|
||||
return new string(br.ReadChars(0x10)).TrimEnd('\0');
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to use PARAM.SFO
|
||||
#if NET20 || NET35
|
||||
string sfoPath = Path.Combine(Path.Combine(Name, "PS3_GAME"), "PARAM.SFO");
|
||||
#else
|
||||
string sfoPath = Path.Combine(Name, "PS3_GAME", "PARAM.SFO");
|
||||
#endif
|
||||
if (File.Exists(sfoPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(sfoPath));
|
||||
br.BaseStream.Seek(-0x18, SeekOrigin.End);
|
||||
return new string(br.ReadChars(9)).TrimEnd('\0').Insert(4, "-");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
public string? GetPlayStation3Version()
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(Name))
|
||||
return null;
|
||||
|
||||
// Attempt to use PS3_DISC.SFB
|
||||
string sfbPath = Path.Combine(Name, "PS3_DISC.SFB");
|
||||
if (File.Exists(sfbPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(sfbPath));
|
||||
br.BaseStream.Seek(0x230, SeekOrigin.Begin);
|
||||
var discVersion = new string(br.ReadChars(0x10)).TrimEnd('\0');
|
||||
if (!string.IsNullOrEmpty(discVersion))
|
||||
return discVersion;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to use PARAM.SFO
|
||||
#if NET20 || NET35
|
||||
string sfoPath = Path.Combine(Path.Combine(Name, "PS3_GAME"), "PARAM.SFO");
|
||||
#else
|
||||
string sfoPath = Path.Combine(Name, "PS3_GAME", "PARAM.SFO");
|
||||
#endif
|
||||
if (File.Exists(sfoPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(sfoPath));
|
||||
br.BaseStream.Seek(-0x08, SeekOrigin.End);
|
||||
return new string(br.ReadChars(5)).TrimEnd('\0');
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the firmware version from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <returns>Firmware version if possible, null on error</returns>
|
||||
public string? GetPlayStation3FirmwareVersion()
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(Name))
|
||||
return null;
|
||||
|
||||
// Attempt to read from /PS3_UPDATE/PS3UPDAT.PUP
|
||||
#if NET20 || NET35
|
||||
string pupPath = Path.Combine(Path.Combine(Name, "PS3_UPDATE"), "PS3UPDAT.PUP");
|
||||
#else
|
||||
string pupPath = Path.Combine(Name, "PS3_UPDATE", "PS3UPDAT.PUP");
|
||||
#endif
|
||||
if (!File.Exists(pupPath))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(pupPath));
|
||||
br.BaseStream.Seek(0x3E, SeekOrigin.Begin);
|
||||
byte[] buf = new byte[2];
|
||||
br.Read(buf, 0, 2);
|
||||
Array.Reverse(buf);
|
||||
short location = BitConverter.ToInt16(buf, 0);
|
||||
br.BaseStream.Seek(location, SeekOrigin.Begin);
|
||||
return new string(br.ReadChars(4));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 4 disc, if possible
|
||||
/// </summary>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
public string? GetPlayStation4Serial()
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(Name))
|
||||
return null;
|
||||
|
||||
// If we can't find param.sfo, we don't have a PlayStation 4 disc
|
||||
#if NET20 || NET35
|
||||
string paramSfoPath = Path.Combine(Path.Combine(Name, "bd"), "param.sfo");
|
||||
#else
|
||||
string paramSfoPath = Path.Combine(Name, "bd", "param.sfo");
|
||||
#endif
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.sfo to find the serial at the end of the file
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(paramSfoPath));
|
||||
br.BaseStream.Seek(-0x14, SeekOrigin.End);
|
||||
return new string(br.ReadChars(9)).Insert(4, "-");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 4 disc, if possible
|
||||
/// </summary>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
public string? GetPlayStation4Version()
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(Name))
|
||||
return null;
|
||||
|
||||
// If we can't find param.sfo, we don't have a PlayStation 4 disc
|
||||
#if NET20 || NET35
|
||||
string paramSfoPath = Path.Combine(Path.Combine(Name, "bd"), "param.sfo");
|
||||
#else
|
||||
string paramSfoPath = Path.Combine(Name, "bd", "param.sfo");
|
||||
#endif
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.sfo to find the version at the end of the file
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(paramSfoPath));
|
||||
br.BaseStream.Seek(-0x08, SeekOrigin.End);
|
||||
return new string(br.ReadChars(5));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 5 disc, if possible
|
||||
/// </summary>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
public string? GetPlayStation5Serial()
|
||||
{
|
||||
// Attempt to get the param.json file
|
||||
var json = GetPlayStation5ParamsJsonFromDrive();
|
||||
if (json == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return json["disc"]?[0]?["masterDataId"]?.Value<string>()?.Insert(4, "-");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// Get the version from a PlayStation 5 disc, if possible
|
||||
/// </summary>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
public string? GetPlayStation5Version()
|
||||
{
|
||||
// Attempt to get the param.json file
|
||||
var json = GetPlayStation5ParamsJsonFromDrive();
|
||||
if (json == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return json["masterVersion"]?.Value<string>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the params.json file from a drive path, if possible
|
||||
/// </summary>
|
||||
/// <returns>JObject representing the JSON on success, null on error</returns>
|
||||
private JObject? GetPlayStation5ParamsJsonFromDrive()
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(Name))
|
||||
return null;
|
||||
|
||||
// If we can't find param.json, we don't have a PlayStation 5 disc
|
||||
#if NET20 || NET35
|
||||
string paramJsonPath = Path.Combine(Path.Combine(Name, "bd"), "param.json");
|
||||
#else
|
||||
string paramJsonPath = Path.Combine(Name, "bd", "param.json");
|
||||
#endif
|
||||
return GetPlayStation5ParamsJsonFromFile(paramJsonPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the params.json file from a filename, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to check</param>
|
||||
/// <returns>JObject representing the JSON on success, null on error</returns>
|
||||
private static JObject? GetPlayStation5ParamsJsonFromFile(string? filename)
|
||||
{
|
||||
// If the file doesn't exist
|
||||
if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.json to find the version in the unencrypted JSON
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(filename));
|
||||
br.BaseStream.Seek(0x800, SeekOrigin.Begin);
|
||||
byte[] jsonBytes = br.ReadBytes((int)(br.BaseStream.Length - 0x800));
|
||||
return JsonConvert.DeserializeObject(Encoding.ASCII.GetString(jsonBytes)) as JObject;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,6 +4,9 @@ using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
#if NET40
|
||||
using System.Threading;
|
||||
#endif
|
||||
using System.Threading.Tasks;
|
||||
using BinaryObjectScanner;
|
||||
using MPF.ExecutionContexts;
|
||||
@@ -35,7 +38,7 @@ namespace MPF.Frontend
|
||||
/// <summary>
|
||||
/// Drive object representing the current drive
|
||||
/// </summary>
|
||||
private readonly Drive? _drive;
|
||||
private Drive? _drive;
|
||||
|
||||
/// <summary>
|
||||
/// ExecutionContext object representing how to invoke the internal program
|
||||
@@ -119,7 +122,7 @@ namespace MPF.Frontend
|
||||
/// <param name="internalProgram"></param>
|
||||
/// <param name="parameters"></param>
|
||||
public DumpEnvironment(Frontend.Options options,
|
||||
string outputPath,
|
||||
string? outputPath,
|
||||
Drive? drive,
|
||||
RedumpSystem? system,
|
||||
MediaType? type,
|
||||
@@ -155,21 +158,21 @@ namespace MPF.Frontend
|
||||
if (programFound == null && _internalProgram != InternalProgram.Aaru)
|
||||
{
|
||||
var processor = new Processors.Aaru(_system, _type);
|
||||
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename);
|
||||
if (foundOtherFiles)
|
||||
programFound = InternalProgram.Aaru;
|
||||
}
|
||||
if (programFound == null && _internalProgram != InternalProgram.DiscImageCreator)
|
||||
{
|
||||
var processor = new Processors.DiscImageCreator(_system, _type);
|
||||
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename);
|
||||
if (foundOtherFiles)
|
||||
programFound = InternalProgram.DiscImageCreator;
|
||||
}
|
||||
if (programFound == null && _internalProgram != InternalProgram.Redumper)
|
||||
{
|
||||
var processor = new Processors.Redumper(_system, _type);
|
||||
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
(bool foundOtherFiles, _) = processor.FoundAllFiles(outputDirectory, outputFilename);
|
||||
if (foundOtherFiles)
|
||||
programFound = InternalProgram.Redumper;
|
||||
}
|
||||
@@ -194,11 +197,15 @@ namespace MPF.Frontend
|
||||
_ => null,
|
||||
};
|
||||
|
||||
// Set system and type
|
||||
// Set system, type, and drive
|
||||
if (_executionContext != null)
|
||||
{
|
||||
_executionContext.System = _system;
|
||||
_executionContext.Type = _type;
|
||||
|
||||
// Set some parameters, if not already set
|
||||
OutputPath ??= _executionContext.OutputPath!;
|
||||
_drive ??= Drive.Create(InternalDriveType.Optical, _executionContext.InputPath!);
|
||||
}
|
||||
|
||||
return _executionContext != null;
|
||||
@@ -287,13 +294,13 @@ namespace MPF.Frontend
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BaseProcessor.FoundAllFiles(string?, string, bool)"/>
|
||||
public bool FoundAllFiles(string? outputDirectory, string outputFilename, bool preCheck)
|
||||
/// <inheritdoc cref="BaseProcessor.FoundAllFiles(string?, string)"/>
|
||||
public bool FoundAllFiles(string? outputDirectory, string outputFilename)
|
||||
{
|
||||
if (_processor == null)
|
||||
return false;
|
||||
|
||||
return _processor.FoundAllFiles(outputDirectory, outputFilename, preCheck).Item1;
|
||||
return _processor.FoundAllFiles(outputDirectory, outputFilename).Item1;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BaseExecutionContext.GetDefaultExtension(MediaType?)"/>
|
||||
@@ -378,11 +385,7 @@ namespace MPF.Frontend
|
||||
/// Execute the initial invocation of the dumping programs
|
||||
/// </summary>
|
||||
/// <param name="progress">Optional result progress callback</param>
|
||||
#if NET40
|
||||
public ResultEventArgs Run(IProgress<ResultEventArgs>? progress = null)
|
||||
#else
|
||||
public async Task<ResultEventArgs> Run(IProgress<ResultEventArgs>? progress = null)
|
||||
#endif
|
||||
{
|
||||
// If we don't have parameters
|
||||
if (_executionContext == null)
|
||||
@@ -401,8 +404,7 @@ namespace MPF.Frontend
|
||||
Directory.CreateDirectory(directoryName);
|
||||
|
||||
#if NET40
|
||||
var executeTask = Task.Factory.StartNew(() => _executionContext.ExecuteInternalProgram());
|
||||
executeTask.Wait();
|
||||
await Task.Factory.StartNew(() => { _executionContext.ExecuteInternalProgram(); return true; });
|
||||
#else
|
||||
await Task.Run(_executionContext.ExecuteInternalProgram);
|
||||
#endif
|
||||
@@ -435,7 +437,7 @@ namespace MPF.Frontend
|
||||
var outputFilename = Path.GetFileName(OutputPath);
|
||||
|
||||
// Check to make sure that the output had all the correct files
|
||||
(bool foundFiles, List<string> missingFiles) = _processor.FoundAllFiles(outputDirectory, outputFilename, false);
|
||||
(bool foundFiles, List<string> missingFiles) = _processor.FoundAllFiles(outputDirectory, outputFilename);
|
||||
if (!foundFiles)
|
||||
{
|
||||
resultProgress?.Report(ResultEventArgs.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
|
||||
@@ -494,7 +496,7 @@ namespace MPF.Frontend
|
||||
var filenameSuffix = _options.AddFilenameSuffix ? Path.GetFileNameWithoutExtension(outputFilename) : null;
|
||||
|
||||
// Write the text output
|
||||
resultProgress?.Report(ResultEventArgs.Success("Writing information to !submissionInfo.txt..."));
|
||||
resultProgress?.Report(ResultEventArgs.Success("Writing submission information file..."));
|
||||
(bool txtSuccess, string txtResult) = WriteOutputData(outputDirectory, filenameSuffix, formattedValues);
|
||||
if (txtSuccess)
|
||||
resultProgress?.Report(ResultEventArgs.Success(txtResult));
|
||||
@@ -506,7 +508,7 @@ namespace MPF.Frontend
|
||||
{
|
||||
if (_options.ScanForProtection)
|
||||
{
|
||||
resultProgress?.Report(ResultEventArgs.Success("Writing protection to !protectionInfo.txt..."));
|
||||
resultProgress?.Report(ResultEventArgs.Success("Writing protection information file..."));
|
||||
bool scanSuccess = WriteProtectionData(outputDirectory, filenameSuffix, submissionInfo, _options.HideDriveLetters);
|
||||
if (scanSuccess)
|
||||
resultProgress?.Report(ResultEventArgs.Success("Writing complete!"));
|
||||
@@ -518,7 +520,7 @@ namespace MPF.Frontend
|
||||
// Write the JSON output, if required
|
||||
if (_options.OutputSubmissionJSON)
|
||||
{
|
||||
resultProgress?.Report(ResultEventArgs.Success($"Writing information to !submissionInfo.json{(_options.IncludeArtifacts ? ".gz" : string.Empty)}..."));
|
||||
resultProgress?.Report(ResultEventArgs.Success($"Writing submission information JSON file{(_options.IncludeArtifacts ? " with artifacts" : string.Empty)}..."));
|
||||
bool jsonSuccess = WriteOutputData(outputDirectory, filenameSuffix, submissionInfo, _options.IncludeArtifacts);
|
||||
if (jsonSuccess)
|
||||
resultProgress?.Report(ResultEventArgs.Success("Writing complete!"));
|
||||
|
||||
@@ -183,5 +183,39 @@ namespace MPF.Frontend
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functionality Support
|
||||
|
||||
/// <summary>
|
||||
/// Get if a system requires an anti-modchip scan
|
||||
/// </summary>
|
||||
public static bool SupportsAntiModchipScans(this RedumpSystem? system)
|
||||
{
|
||||
return system switch
|
||||
{
|
||||
RedumpSystem.SonyPlayStation => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get if a system requires a copy protection scan
|
||||
/// </summary>
|
||||
public static bool SupportsCopyProtectionScans(this RedumpSystem? system)
|
||||
{
|
||||
return system switch
|
||||
{
|
||||
RedumpSystem.AppleMacintosh => true,
|
||||
RedumpSystem.EnhancedCD => true,
|
||||
RedumpSystem.IBMPCcompatible => true,
|
||||
RedumpSystem.PalmOS => true,
|
||||
RedumpSystem.PocketPC => true,
|
||||
RedumpSystem.RainbowDisc => true,
|
||||
RedumpSystem.SonyElectronicBook => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,73 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- 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.2.0</VersionPrefix>
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.2.2</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Common code for all MPF frontend 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>
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<Description>Common code for all MPF frontend 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>
|
||||
<InternalsVisibleTo Include="MPF.Test" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.ExecutionContexts\MPF.ExecutionContexts.csproj" />
|
||||
<ProjectReference Include="..\MPF.Processors\MPF.Processors.csproj" />
|
||||
</ItemGroup>
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- 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(`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.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.ExecutionContexts\MPF.ExecutionContexts.csproj" />
|
||||
<ProjectReference Include="..\MPF.Processors\MPF.Processors.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.13" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="LibIRD" Version="0.9.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
|
||||
</ItemGroup>
|
||||
<!-- Support for old .NET versions -->
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net40`))">
|
||||
<PackageReference Include="MinAsyncBridge" Version="0.12.4" />
|
||||
<PackageReference Include="MinTasksExtensionsBridge" Version="0.3.4" />
|
||||
<PackageReference Include="MinThreadingBridge" Version="0.11.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith(`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.Net.Http" Version="4.3.4" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BinaryObjectScanner" PrivateAssets="build; analyzers" ExcludeAssets="contentFiles" Version="3.1.13" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="LibIRD" Version="0.9.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -83,6 +83,33 @@ namespace MPF.Frontend
|
||||
set { Settings["EnableDarkMode"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enable purple mode for UI elements
|
||||
/// </summary>
|
||||
public bool EnablePurpMode
|
||||
{
|
||||
get { return GetBooleanSetting(Settings, "EnablePurpMode", false); }
|
||||
set { Settings["EnablePurpMode"] = value.ToString(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom color setting
|
||||
/// </summary>
|
||||
public string? CustomBackgroundColor
|
||||
{
|
||||
get { return GetStringSetting(Settings, "CustomBackgroundColor", null); }
|
||||
set { Settings["CustomBackgroundColor"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom color setting
|
||||
/// </summary>
|
||||
public string? CustomTextColor
|
||||
{
|
||||
get { return GetStringSetting(Settings, "CustomTextColor", null); }
|
||||
set { Settings["CustomTextColor"] = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for updates on startup
|
||||
/// </summary>
|
||||
|
||||
@@ -11,6 +11,33 @@ namespace MPF.Frontend.Tools
|
||||
{
|
||||
#region Information Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Get the default speed for a given media type from the supplied options
|
||||
/// </summary>
|
||||
public static int GetDefaultSpeedForMediaType(MediaType? mediaType, Options options)
|
||||
{
|
||||
return mediaType switch
|
||||
{
|
||||
// CD dump speed
|
||||
MediaType.CDROM => options.PreferredDumpSpeedCD,
|
||||
MediaType.GDROM => options.PreferredDumpSpeedCD,
|
||||
|
||||
// DVD dump speed
|
||||
MediaType.DVD => options.PreferredDumpSpeedDVD,
|
||||
MediaType.NintendoGameCubeGameDisc => options.PreferredDumpSpeedDVD,
|
||||
MediaType.NintendoWiiOpticalDisc => options.PreferredDumpSpeedDVD,
|
||||
|
||||
// HD-DVD dump speed
|
||||
MediaType.HDDVD => options.PreferredDumpSpeedHDDVD,
|
||||
|
||||
// BD dump speed
|
||||
MediaType.BluRay => options.PreferredDumpSpeedBD,
|
||||
|
||||
// Default
|
||||
_ => options.PreferredDumpSpeedCD,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current system from the drive volume label
|
||||
/// </summary>
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
namespace MPF.Frontend.Tools
|
||||
{
|
||||
public static class OptionsLoader
|
||||
{
|
||||
private const string ConfigurationPath = "config.json";
|
||||
/// <summary>
|
||||
/// Full path to the configuration file used by the program
|
||||
/// </summary>
|
||||
#if NET20 || NET35 || NET40 || NET452
|
||||
private static string ConfigurationPath => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "config.json");
|
||||
#else
|
||||
private static string ConfigurationPath => Path.Combine(AppContext.BaseDirectory, "config.json");
|
||||
#endif
|
||||
|
||||
#region Arguments
|
||||
|
||||
@@ -91,171 +98,6 @@ namespace MPF.Frontend.Tools
|
||||
return (true, mediaType, knownSystem, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Load the current set of options from application arguments
|
||||
/// </summary>
|
||||
public static (Options, SubmissionInfo?, string?, int) LoadFromArguments(string[] args, int startIndex = 0)
|
||||
{
|
||||
// Create the output values with defaults
|
||||
var options = new Options()
|
||||
{
|
||||
RedumpUsername = null,
|
||||
RedumpPassword = null,
|
||||
InternalProgram = InternalProgram.NONE,
|
||||
AddFilenameSuffix = false,
|
||||
OutputSubmissionJSON = false,
|
||||
CompressLogFiles = false,
|
||||
DeleteUnnecessaryFiles = false,
|
||||
};
|
||||
|
||||
// Create the submission info to return, if necessary
|
||||
SubmissionInfo? info = null;
|
||||
string? parsedPath = null;
|
||||
|
||||
// These values require multiple parts to be active
|
||||
bool scan = false, hideDriveLetters = false;
|
||||
|
||||
// If we have no arguments, just return
|
||||
if (args == null || args.Length == 0)
|
||||
return (options, null, null, 0);
|
||||
|
||||
// If we have an invalid start index, just return
|
||||
if (startIndex < 0 || startIndex >= args.Length)
|
||||
return (options, null, null, startIndex);
|
||||
|
||||
// Loop through the arguments and parse out values
|
||||
for (; startIndex < args.Length; startIndex++)
|
||||
{
|
||||
// Use specific program
|
||||
if (args[startIndex].StartsWith("-u=") || args[startIndex].StartsWith("--use="))
|
||||
{
|
||||
string internalProgram = args[startIndex].Split('=')[1];
|
||||
options.InternalProgram = Options.ToInternalProgram(internalProgram);
|
||||
}
|
||||
else if (args[startIndex] == "-u" || args[startIndex] == "--use")
|
||||
{
|
||||
string internalProgram = args[startIndex + 1];
|
||||
options.InternalProgram = Options.ToInternalProgram(internalProgram);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Redump login
|
||||
else if (args[startIndex].StartsWith("-c=") || args[startIndex].StartsWith("--credentials="))
|
||||
{
|
||||
string[] credentials = args[startIndex].Split('=')[1].Split(';');
|
||||
options.RedumpUsername = credentials[0];
|
||||
options.RedumpPassword = credentials[1];
|
||||
}
|
||||
else if (args[startIndex] == "-c" || args[startIndex] == "--credentials")
|
||||
{
|
||||
options.RedumpUsername = args[startIndex + 1];
|
||||
options.RedumpPassword = args[startIndex + 2];
|
||||
startIndex += 2;
|
||||
}
|
||||
|
||||
// Pull all information (requires Redump login)
|
||||
else if (args[startIndex].Equals("-a") || args[startIndex].Equals("--pull-all"))
|
||||
{
|
||||
options.PullAllInformation = true;
|
||||
}
|
||||
|
||||
// Use a device path for physical checks
|
||||
else if (args[startIndex].StartsWith("-p=") || args[startIndex].StartsWith("--path="))
|
||||
{
|
||||
parsedPath = args[startIndex].Split('=')[1];
|
||||
}
|
||||
else if (args[startIndex] == "-p" || args[startIndex] == "--path")
|
||||
{
|
||||
parsedPath = args[startIndex + 1];
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Scan for protection (requires device path)
|
||||
else if (args[startIndex].Equals("-s") || args[startIndex].Equals("--scan"))
|
||||
{
|
||||
scan = true;
|
||||
}
|
||||
|
||||
// Hide drive letters from scan output (requires --protect-file)
|
||||
else if (args[startIndex].Equals("-g") || args[startIndex].Equals("--hide-drive-letters"))
|
||||
{
|
||||
hideDriveLetters = true;
|
||||
}
|
||||
|
||||
// Include seed info file
|
||||
else if (args[startIndex].StartsWith("-l=") || args[startIndex].StartsWith("--load-seed="))
|
||||
{
|
||||
string seedInfo = args[startIndex].Split('=')[1];
|
||||
info = Builder.CreateFromFile(seedInfo);
|
||||
}
|
||||
else if (args[startIndex] == "-l" || args[startIndex] == "--load-seed")
|
||||
{
|
||||
string seedInfo = args[startIndex + 1];
|
||||
info = Builder.CreateFromFile(seedInfo);
|
||||
startIndex++;
|
||||
}
|
||||
|
||||
// Add filename suffix
|
||||
else if (args[startIndex].Equals("-x") || args[startIndex].Equals("--suffix"))
|
||||
{
|
||||
options.AddFilenameSuffix = true;
|
||||
}
|
||||
|
||||
// Output submission JSON
|
||||
else if (args[startIndex].Equals("-j") || args[startIndex].Equals("--json"))
|
||||
{
|
||||
options.OutputSubmissionJSON = true;
|
||||
}
|
||||
|
||||
// Compress log and extraneous files
|
||||
else if (args[startIndex].Equals("-z") || args[startIndex].Equals("--zip"))
|
||||
{
|
||||
options.CompressLogFiles = true;
|
||||
}
|
||||
|
||||
// Delete unnecessary files files
|
||||
else if (args[startIndex].Equals("-d") || args[startIndex].Equals("--delete"))
|
||||
{
|
||||
options.DeleteUnnecessaryFiles = true;
|
||||
}
|
||||
|
||||
// Default, we fall out
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Now deal with the complex options
|
||||
options.ScanForProtection = scan && !string.IsNullOrEmpty(parsedPath);
|
||||
options.HideDriveLetters = hideDriveLetters && scan && !string.IsNullOrEmpty(parsedPath);
|
||||
|
||||
return (options, info, parsedPath, startIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return a list of supported arguments and descriptions
|
||||
/// </summary>
|
||||
public static List<string> PrintSupportedArguments()
|
||||
{
|
||||
var supportedArguments = new List<string>
|
||||
{
|
||||
"-u, --use <program> Dumping program output type [REQUIRED]",
|
||||
"-c, --credentials <user> <pw> Redump username and password",
|
||||
"-a, --pull-all Pull all information from Redump (requires --credentials)",
|
||||
"-p, --path <drivepath> Physical drive path for additional checks",
|
||||
"-s, --scan Enable copy protection scan (requires --path)",
|
||||
"-g, --hide-drive-letters Hide drive letters from scan output (requires --protect-file)",
|
||||
"-l, --load-seed <path> Load a seed submission JSON for user information",
|
||||
"-x, --suffix Enable adding filename suffix",
|
||||
"-j, --json Enable submission JSON output",
|
||||
"-z, --zip Enable log file compression",
|
||||
"-d, --delete Enable unnecessary file deletion",
|
||||
};
|
||||
|
||||
return supportedArguments;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List all programs with their short usable names
|
||||
/// </summary>
|
||||
@@ -428,7 +270,7 @@ namespace MPF.Frontend.Tools
|
||||
{
|
||||
if (!File.Exists(ConfigurationPath))
|
||||
{
|
||||
_ = File.Create(ConfigurationPath);
|
||||
File.Create(ConfigurationPath).Dispose();
|
||||
return new Options();
|
||||
}
|
||||
|
||||
@@ -436,14 +278,38 @@ namespace MPF.Frontend.Tools
|
||||
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?>;
|
||||
reader.Dispose();
|
||||
return new Options(settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Save the current set of options to the application configuration
|
||||
/// </summary>
|
||||
public static void SaveToConfig(Options options)
|
||||
public static void SaveToConfig(Options options, bool saveDefault = false)
|
||||
{
|
||||
// If default values should be saved as well
|
||||
if (saveDefault)
|
||||
{
|
||||
PropertyInfo[] properties = typeof(Options).GetProperties();
|
||||
foreach (var property in properties)
|
||||
{
|
||||
// Skip dictionary properties
|
||||
if (property.Name == "Item")
|
||||
continue;
|
||||
|
||||
// Skip non-option properties
|
||||
if (property.Name == "Settings" || property.Name == "HasRedumpLogin")
|
||||
continue;
|
||||
|
||||
var val = property.GetValue(options, null);
|
||||
property.SetValue(options, val, null);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle a very strange edge case
|
||||
if (!File.Exists(ConfigurationPath))
|
||||
File.Create(ConfigurationPath).Dispose();
|
||||
|
||||
var serializer = JsonSerializer.Create();
|
||||
var sw = new StreamWriter(ConfigurationPath) { AutoFlush = true };
|
||||
var writer = new JsonTextWriter(sw) { Formatting = Formatting.Indented };
|
||||
|
||||
591
MPF.Frontend/Tools/PhysicalTool.cs
Normal file
591
MPF.Frontend/Tools/PhysicalTool.cs
Normal file
@@ -0,0 +1,591 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SabreTools.IO;
|
||||
|
||||
namespace MPF.Frontend.Tools
|
||||
{
|
||||
public static class PhysicalTool
|
||||
{
|
||||
#region Generic
|
||||
|
||||
/// <summary>s
|
||||
/// Get the last modified date for a file from a physical disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <param name="filePath">Relative file path</param>
|
||||
/// <returns>Output last modified date in "yyyy-mm-dd" format if possible, null on error</returns>
|
||||
public static string? GetFileDate(Drive? drive, string? filePath, bool fixTwoDigitYear = false)
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return null;
|
||||
|
||||
// If the executable name is invalid, we can't do this part
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
return null;
|
||||
|
||||
// Now that we have the EXE name, try to get the fileinfo for it
|
||||
string exePath = Path.Combine(drive.Name, filePath);
|
||||
if (!File.Exists(exePath))
|
||||
return null;
|
||||
|
||||
// Get the last modified time
|
||||
var fi = new FileInfo(exePath);
|
||||
var lastModified = fi.LastWriteTimeUtc;
|
||||
int year = lastModified.Year;
|
||||
int month = lastModified.Month;
|
||||
int day = lastModified.Day;
|
||||
|
||||
// Fix the Y2K timestamp issue, if required
|
||||
if (fixTwoDigitYear)
|
||||
year = year >= 1900 && year < 1920 ? 2000 + year % 100 : year;
|
||||
|
||||
// Format and return the string
|
||||
var dt = new DateTime(year, month, day);
|
||||
return dt.ToString("yyyy-MM-dd");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region BD-Video
|
||||
|
||||
/// <summary>
|
||||
/// Get if the Bus Encryption Enabled (BEE) flag is set in a path
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Bus encryption enabled status if possible, false otherwise</returns>
|
||||
public static bool GetBusEncryptionEnabled(Drive? drive)
|
||||
{
|
||||
// If there's no drive path, we can't get BEE flag
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return false;
|
||||
|
||||
// If the folder no longer exists, we can't get exe name
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return false;
|
||||
|
||||
// Get the two possible file paths
|
||||
#if NET20 || NET35
|
||||
string content000 = Path.Combine(Path.Combine(drive.Name, "AACS"), "Content000.cer");
|
||||
string content001 = Path.Combine(Path.Combine(drive.Name, "AACS"), "Content001.cer");
|
||||
#else
|
||||
string content000 = Path.Combine(drive.Name, "AACS", "Content000.cer");
|
||||
string content001 = Path.Combine(drive.Name, "AACS", "Content001.cer");
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
// Check the required files
|
||||
if (File.Exists(content000) && new FileInfo(content000).Length > 1)
|
||||
{
|
||||
using var fs = File.OpenRead(content000);
|
||||
_ = fs.ReadByte(); // Skip the first byte
|
||||
return fs.ReadByte() > 127;
|
||||
}
|
||||
else if (File.Exists(content001) && new FileInfo(content001).Length > 1)
|
||||
{
|
||||
using var fs = File.OpenRead(content001);
|
||||
_ = fs.ReadByte(); // Skip the first byte
|
||||
return fs.ReadByte() > 127;
|
||||
}
|
||||
|
||||
// False if neither file fits the criteria
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error is right now
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region PlayStation
|
||||
|
||||
/// <summary>
|
||||
/// Get the EXE name from a PlayStation disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Executable name on success, null otherwise</returns>
|
||||
public static string? GetPlayStationExecutableName(Drive? drive)
|
||||
{
|
||||
// If there's no drive path, we can't get exe name
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't get exe name
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return null;
|
||||
|
||||
// Get the two paths that we will need to check
|
||||
string psxExePath = Path.Combine(drive.Name, "PSX.EXE");
|
||||
string systemCnfPath = Path.Combine(drive.Name, "SYSTEM.CNF");
|
||||
|
||||
// Read the CNF file as an INI file
|
||||
var systemCnf = new IniFile(systemCnfPath);
|
||||
string? bootValue = string.Empty;
|
||||
|
||||
// PlayStation uses "BOOT" as the key
|
||||
if (systemCnf.ContainsKey("BOOT"))
|
||||
bootValue = systemCnf["BOOT"];
|
||||
|
||||
// PlayStation 2 uses "BOOT2" as the key
|
||||
if (systemCnf.ContainsKey("BOOT2"))
|
||||
bootValue = systemCnf["BOOT2"];
|
||||
|
||||
// If we had any boot value, parse it and get the executable name
|
||||
if (!string.IsNullOrEmpty(bootValue))
|
||||
{
|
||||
var match = Regex.Match(bootValue, @"cdrom.?:\\?(.*)", RegexOptions.Compiled);
|
||||
if (match.Groups.Count > 1)
|
||||
{
|
||||
string? serial = match.Groups[1].Value;
|
||||
|
||||
// Some games may have the EXE in a subfolder
|
||||
serial = Path.GetFileName(serial);
|
||||
|
||||
return serial;
|
||||
}
|
||||
}
|
||||
|
||||
// If the SYSTEM.CNF value can't be found, try PSX.EXE
|
||||
if (File.Exists(psxExePath))
|
||||
return "PSX.EXE";
|
||||
|
||||
// If neither can be found, we return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the serial from a PlayStation disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Serial on success, null otherwise</returns>
|
||||
public static string? GetPlayStationSerial(Drive? drive)
|
||||
{
|
||||
// Try to get the executable name
|
||||
string? exeName = GetPlayStationExecutableName(drive);
|
||||
if (string.IsNullOrEmpty(exeName))
|
||||
return null;
|
||||
|
||||
// Handle generic PSX.EXE
|
||||
if (exeName == "PSX.EXE")
|
||||
return null;
|
||||
|
||||
// EXE name may have a trailing `;` after
|
||||
// EXE name should always be in all caps
|
||||
exeName = exeName!
|
||||
.Split(';')[0]
|
||||
.ToUpperInvariant();
|
||||
|
||||
// Serial is most of the EXE name normalized
|
||||
return exeName
|
||||
.Replace('_', '-')
|
||||
.Replace(".", string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 2 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
public static string? GetPlayStation2Version(Drive? drive)
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return null;
|
||||
|
||||
// Get the SYSTEM.CNF path to check
|
||||
string systemCnfPath = Path.Combine(drive.Name, "SYSTEM.CNF");
|
||||
|
||||
// Try to parse the SYSTEM.CNF file
|
||||
var systemCnf = new IniFile(systemCnfPath);
|
||||
if (systemCnf.ContainsKey("VER"))
|
||||
return systemCnf["VER"];
|
||||
|
||||
// If "VER" can't be found, we can't do much
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
public static string? GetPlayStation3Serial(Drive? drive)
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return null;
|
||||
|
||||
// Attempt to use PS3_DISC.SFB
|
||||
string sfbPath = Path.Combine(drive.Name, "PS3_DISC.SFB");
|
||||
if (File.Exists(sfbPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(sfbPath));
|
||||
br.BaseStream.Seek(0x220, SeekOrigin.Begin);
|
||||
return new string(br.ReadChars(0x10)).TrimEnd('\0');
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to use PARAM.SFO
|
||||
#if NET20 || NET35
|
||||
string sfoPath = Path.Combine(Path.Combine(drive.Name, "PS3_GAME"), "PARAM.SFO");
|
||||
#else
|
||||
string sfoPath = Path.Combine(drive.Name, "PS3_GAME", "PARAM.SFO");
|
||||
#endif
|
||||
if (File.Exists(sfoPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(sfoPath));
|
||||
br.BaseStream.Seek(-0x18, SeekOrigin.End);
|
||||
return new string(br.ReadChars(9)).TrimEnd('\0').Insert(4, "-");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
public static string? GetPlayStation3Version(Drive? drive)
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return null;
|
||||
|
||||
// Attempt to use PS3_DISC.SFB
|
||||
string sfbPath = Path.Combine(drive.Name, "PS3_DISC.SFB");
|
||||
if (File.Exists(sfbPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(sfbPath));
|
||||
br.BaseStream.Seek(0x230, SeekOrigin.Begin);
|
||||
var discVersion = new string(br.ReadChars(0x10)).TrimEnd('\0');
|
||||
if (!string.IsNullOrEmpty(discVersion))
|
||||
return discVersion;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to use PARAM.SFO
|
||||
#if NET20 || NET35
|
||||
string sfoPath = Path.Combine(Path.Combine(drive.Name, "PS3_GAME"), "PARAM.SFO");
|
||||
#else
|
||||
string sfoPath = Path.Combine(drive.Name, "PS3_GAME", "PARAM.SFO");
|
||||
#endif
|
||||
if (File.Exists(sfoPath))
|
||||
{
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(sfoPath));
|
||||
br.BaseStream.Seek(-0x08, SeekOrigin.End);
|
||||
return new string(br.ReadChars(5)).TrimEnd('\0');
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the firmware version from a PlayStation 3 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Firmware version if possible, null on error</returns>
|
||||
public static string? GetPlayStation3FirmwareVersion(Drive? drive)
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return null;
|
||||
|
||||
// Attempt to read from /PS3_UPDATE/PS3UPDAT.PUP
|
||||
#if NET20 || NET35
|
||||
string pupPath = Path.Combine(Path.Combine(drive.Name, "PS3_UPDATE"), "PS3UPDAT.PUP");
|
||||
#else
|
||||
string pupPath = Path.Combine(drive.Name, "PS3_UPDATE", "PS3UPDAT.PUP");
|
||||
#endif
|
||||
if (!File.Exists(pupPath))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(pupPath));
|
||||
br.BaseStream.Seek(0x3E, SeekOrigin.Begin);
|
||||
byte[] buf = new byte[2];
|
||||
br.Read(buf, 0, 2);
|
||||
Array.Reverse(buf);
|
||||
short location = BitConverter.ToInt16(buf, 0);
|
||||
br.BaseStream.Seek(location, SeekOrigin.Begin);
|
||||
return new string(br.ReadChars(4));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 4 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
public static string? GetPlayStation4Serial(Drive? drive)
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return null;
|
||||
|
||||
// If we can't find param.sfo, we don't have a PlayStation 4 disc
|
||||
#if NET20 || NET35
|
||||
string paramSfoPath = Path.Combine(Path.Combine(drive.Name, "bd"), "param.sfo");
|
||||
#else
|
||||
string paramSfoPath = Path.Combine(drive.Name, "bd", "param.sfo");
|
||||
#endif
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.sfo to find the serial at the end of the file
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(paramSfoPath));
|
||||
br.BaseStream.Seek(-0x14, SeekOrigin.End);
|
||||
return new string(br.ReadChars(9)).Insert(4, "-");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version from a PlayStation 4 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
public static string? GetPlayStation4Version(Drive? drive)
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return null;
|
||||
|
||||
// If we can't find param.sfo, we don't have a PlayStation 4 disc
|
||||
#if NET20 || NET35
|
||||
string paramSfoPath = Path.Combine(Path.Combine(drive.Name, "bd"), "param.sfo");
|
||||
#else
|
||||
string paramSfoPath = Path.Combine(drive.Name, "bd", "param.sfo");
|
||||
#endif
|
||||
if (!File.Exists(paramSfoPath))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.sfo to find the version at the end of the file
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(paramSfoPath));
|
||||
br.BaseStream.Seek(-0x08, SeekOrigin.End);
|
||||
return new string(br.ReadChars(5));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the internal serial from a PlayStation 5 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Internal disc serial if possible, null on error</returns>
|
||||
public static string? GetPlayStation5Serial(Drive? drive)
|
||||
{
|
||||
// Attempt to get the param.json file
|
||||
var json = GetPlayStation5ParamsJsonFromDrive(drive);
|
||||
if (json == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return json["disc"]?[0]?["masterDataId"]?.Value<string>()?.Insert(4, "-");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// Get the version from a PlayStation 5 disc, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Game version if possible, null on error</returns>
|
||||
public static string? GetPlayStation5Version(Drive? drive)
|
||||
{
|
||||
// Attempt to get the param.json file
|
||||
var json = GetPlayStation5ParamsJsonFromDrive(drive);
|
||||
if (json == null)
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
return json["masterVersion"]?.Value<string>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the params.json file from a drive path, if possible
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>JObject representing the JSON on success, null on error</returns>
|
||||
private static JObject? GetPlayStation5ParamsJsonFromDrive(Drive? drive)
|
||||
{
|
||||
// If there's no drive path, we can't do this part
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't do this part
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return null;
|
||||
|
||||
// If we can't find param.json, we don't have a PlayStation 5 disc
|
||||
#if NET20 || NET35
|
||||
string paramJsonPath = Path.Combine(Path.Combine(drive.Name, "bd"), "param.json");
|
||||
#else
|
||||
string paramJsonPath = Path.Combine(drive.Name, "bd", "param.json");
|
||||
#endif
|
||||
return GetPlayStation5ParamsJsonFromFile(paramJsonPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the params.json file from a filename, if possible
|
||||
/// </summary>
|
||||
/// <param name="filename">Filename to check</param>
|
||||
/// <returns>JObject representing the JSON on success, null on error</returns>
|
||||
private static JObject? GetPlayStation5ParamsJsonFromFile(string? filename)
|
||||
{
|
||||
// If the file doesn't exist
|
||||
if (string.IsNullOrEmpty(filename) || !File.Exists(filename))
|
||||
return null;
|
||||
|
||||
// Let's try reading param.json to find the version in the unencrypted JSON
|
||||
try
|
||||
{
|
||||
using var br = new BinaryReader(File.OpenRead(filename));
|
||||
br.BaseStream.Seek(0x800, SeekOrigin.Begin);
|
||||
byte[] jsonBytes = br.ReadBytes((int)(br.BaseStream.Length - 0x800));
|
||||
return JsonConvert.DeserializeObject(Encoding.ASCII.GetString(jsonBytes)) as JObject;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error was
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Xbox
|
||||
|
||||
/// <summary>
|
||||
/// Get all filenames for Xbox One and Xbox Series X
|
||||
/// </summary>
|
||||
/// <param name="drive">Drive to extract information from</param>
|
||||
/// <returns>Filenames if possible, null on error</returns>
|
||||
public static string? GetXboxFilenames(Drive? drive)
|
||||
{
|
||||
// If there's no drive path, we can't get BEE flag
|
||||
if (string.IsNullOrEmpty(drive?.Name))
|
||||
return null;
|
||||
|
||||
// If the folder no longer exists, we can't get exe name
|
||||
if (!Directory.Exists(drive!.Name))
|
||||
return null;
|
||||
|
||||
// Get the MSXC directory path
|
||||
string msxc = Path.Combine(drive.Name, "MSXC");
|
||||
if (!Directory.Exists(msxc))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
var files = Directory.GetFiles(msxc, "*", SearchOption.TopDirectoryOnly);
|
||||
var filenames = files.Select(Path.GetFileName).ToArray();
|
||||
return string.Join("\n", filenames);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the error is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ namespace MPF.Frontend.Tools
|
||||
string outputFilename = Path.GetFileName(outputPath);
|
||||
|
||||
// Check that all of the relevant files are there
|
||||
(bool foundFiles, List<string> missingFiles) = processor.FoundAllFiles(outputDirectory, outputFilename, false);
|
||||
(bool foundFiles, List<string> missingFiles) = processor.FoundAllFiles(outputDirectory, outputFilename);
|
||||
if (!foundFiles)
|
||||
{
|
||||
resultProgress?.Report(ResultEventArgs.Failure($"There were files missing from the output:\n{string.Join("\n", [.. missingFiles])}"));
|
||||
@@ -81,7 +81,7 @@ namespace MPF.Frontend.Tools
|
||||
// Get specific tool output handling
|
||||
processor?.GenerateSubmissionInfo(info, combinedBase, options.EnableRedumpCompatibility);
|
||||
if (options.IncludeArtifacts)
|
||||
processor?.GenerateArtifacts(info, combinedBase);
|
||||
info.Artifacts = processor?.GenerateArtifacts(combinedBase);
|
||||
|
||||
// Get a list of matching IDs for each line in the DAT
|
||||
if (!string.IsNullOrEmpty(info.TracksAndWriteOffsets!.ClrMameProData) && options.HasRedumpLogin)
|
||||
@@ -107,7 +107,7 @@ namespace MPF.Frontend.Tools
|
||||
ProcessSystem(info, system, drive, options.AddPlaceholders, processor is DiscImageCreator, combinedBase);
|
||||
|
||||
// Run anti-modchip check, if necessary
|
||||
if (drive != null && SupportsAntiModchipScans(system) && info.CopyProtection!.AntiModchip == YesNo.NULL)
|
||||
if (drive != null && system.SupportsAntiModchipScans() && info.CopyProtection!.AntiModchip == YesNo.NULL)
|
||||
{
|
||||
resultProgress?.Report(ResultEventArgs.Success("Checking for anti-modchip strings... this might take a while!"));
|
||||
info.CopyProtection.AntiModchip = await ProtectionTool.GetPlayStationAntiModchipDetected(drive?.Name) ? YesNo.Yes : YesNo.No;
|
||||
@@ -115,7 +115,7 @@ namespace MPF.Frontend.Tools
|
||||
}
|
||||
|
||||
// Run copy protection, if possible or necessary
|
||||
if (SupportsCopyProtectionScans(system))
|
||||
if (system.SupportsCopyProtectionScans())
|
||||
{
|
||||
resultProgress?.Report(ResultEventArgs.Success("Running copy protection scan... this might take a while!"));
|
||||
var (protectionString, fullProtections) = await ProtectionTool.GetCopyProtection(drive, options, protectionProgress);
|
||||
@@ -147,11 +147,7 @@ namespace MPF.Frontend.Tools
|
||||
/// <param name="options">Options object representing user-defined options</param>
|
||||
/// <param name="info">Existing SubmissionInfo object to fill</param>
|
||||
/// <param name="resultProgress">Optional result progress callback</param>
|
||||
#if NET40
|
||||
public static bool FillFromRedump(Frontend.Options options, SubmissionInfo info, IProgress<ResultEventArgs>? resultProgress = null)
|
||||
#else
|
||||
public async static Task<bool> FillFromRedump(Frontend.Options options, SubmissionInfo info, IProgress<ResultEventArgs>? resultProgress = null)
|
||||
#endif
|
||||
{
|
||||
// If no username is provided
|
||||
if (string.IsNullOrEmpty(options.RedumpUsername) || string.IsNullOrEmpty(options.RedumpPassword))
|
||||
@@ -163,13 +159,8 @@ namespace MPF.Frontend.Tools
|
||||
info.PartiallyMatchedIDs = [];
|
||||
|
||||
// Login to Redump
|
||||
#if NETFRAMEWORK
|
||||
using var wc = new RedumpWebClient();
|
||||
bool? loggedIn = wc.Login(options.RedumpUsername!, options.RedumpPassword!);
|
||||
#else
|
||||
using var wc = new RedumpHttpClient();
|
||||
bool? loggedIn = await wc.Login(options.RedumpUsername, options.RedumpPassword);
|
||||
#endif
|
||||
var wc = new RedumpClient();
|
||||
bool? loggedIn = await wc.Login(options.RedumpUsername ?? string.Empty, options.RedumpPassword ?? string.Empty);
|
||||
if (loggedIn == null)
|
||||
{
|
||||
resultProgress?.Report(ResultEventArgs.Failure("There was an unknown error connecting to Redump"));
|
||||
@@ -183,7 +174,7 @@ namespace MPF.Frontend.Tools
|
||||
|
||||
// Setup the checks
|
||||
bool allFound = true;
|
||||
List<int[]> foundIdSets = [];
|
||||
List<int[]> foundIdSets = [];
|
||||
|
||||
// Loop through all of the hashdata to find matching IDs
|
||||
resultProgress?.Report(ResultEventArgs.Success("Finding disc matches on Redump..."));
|
||||
@@ -221,13 +212,7 @@ namespace MPF.Frontend.Tools
|
||||
continue;
|
||||
}
|
||||
|
||||
#if NET40
|
||||
var validateTask = Validator.ValidateSingleTrack(wc, info, sha1);
|
||||
validateTask.Wait();
|
||||
(bool singleFound, var foundIds, string? result) = validateTask.Result;
|
||||
#else
|
||||
(bool singleFound, var foundIds, string? result) = await Validator.ValidateSingleTrack(wc, info, sha1);
|
||||
#endif
|
||||
if (singleFound)
|
||||
resultProgress?.Report(ResultEventArgs.Success(result));
|
||||
else
|
||||
@@ -253,7 +238,7 @@ namespace MPF.Frontend.Tools
|
||||
fullyMatchedIDs = [.. set];
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Try to intersect with all known IDs
|
||||
fullyMatchedIDs = fullyMatchedIDs.Intersect(set).ToList();
|
||||
if (!fullyMatchedIDs.Any())
|
||||
@@ -558,7 +543,7 @@ namespace MPF.Frontend.Tools
|
||||
info.CommonDiscInfo.Layer0MouldSID = addPlaceholders ? RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer1MouldSID = addPlaceholders ? RequiredIfExistsValue : string.Empty;
|
||||
info.CommonDiscInfo.Layer0AdditionalMould = addPlaceholders ? RequiredIfExistsValue : string.Empty;
|
||||
info.Extras!.BCA ??= (addPlaceholders ? RequiredValue : string.Empty);
|
||||
info.Extras!.BCA ??= addPlaceholders ? RequiredValue : string.Empty;
|
||||
break;
|
||||
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
@@ -589,7 +574,7 @@ namespace MPF.Frontend.Tools
|
||||
}
|
||||
|
||||
info.Extras!.DiscKey = addPlaceholders ? RequiredValue : string.Empty;
|
||||
info.Extras.BCA = info.Extras.BCA ?? (addPlaceholders ? RequiredValue : string.Empty);
|
||||
info.Extras.BCA ??= addPlaceholders ? RequiredValue : string.Empty;
|
||||
|
||||
break;
|
||||
|
||||
@@ -639,6 +624,17 @@ namespace MPF.Frontend.Tools
|
||||
break;
|
||||
|
||||
case RedumpSystem.BDVideo:
|
||||
info.CommonDiscInfo!.Category ??= DiscCategory.Video;
|
||||
bool bee = PhysicalTool.GetBusEncryptionEnabled(drive);
|
||||
if (bee && string.IsNullOrEmpty(info.CopyProtection!.Protection))
|
||||
info.CopyProtection.Protection = "Bus encryption enabled flag set";
|
||||
else if (bee)
|
||||
info.CopyProtection!.Protection += "\nBus encryption enabled flag set";
|
||||
else
|
||||
info.CopyProtection!.Protection ??= addPlaceholders ? RequiredIfExistsValue : string.Empty;
|
||||
|
||||
break;
|
||||
|
||||
case RedumpSystem.DVDVideo:
|
||||
case RedumpSystem.HDDVDVideo:
|
||||
info.CommonDiscInfo!.Category ??= DiscCategory.Video;
|
||||
@@ -694,25 +690,19 @@ namespace MPF.Frontend.Tools
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiPython2:
|
||||
string? kp2Exe = PhysicalTool.GetPlayStationExecutableName(drive);
|
||||
|
||||
// TODO: Remove this hack when DIC supports build date output
|
||||
if (isDiscImageCreator)
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", drive?.GetPlayStationExecutableName());
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", kp2Exe);
|
||||
|
||||
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? kp2Exe) || string.IsNullOrEmpty(kp2Exe))
|
||||
info.CommonDiscInfo.Region = Drive.GetPlayStationRegion(kp2Exe);
|
||||
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStationSerial);
|
||||
info.CommonDiscInfo!.EXEDateBuildDate ??= PhysicalTool.GetFileDate(drive, kp2Exe, fixTwoDigitYear: true);
|
||||
|
||||
if (drive?.GetPlayStationExecutableInfo(out var kp2Serial, out Region? kp2Region, out var kp2Date) == true)
|
||||
{
|
||||
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? value) || string.IsNullOrEmpty(value))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = kp2Serial ?? string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Region ??= kp2Region;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= kp2Date;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
|
||||
info.VersionAndEditions!.Version = drive?.GetPlayStation2Version() ?? string.Empty;
|
||||
if (CommentFieldExists(info, SiteCode.InternalSerialName, out kp2Exe))
|
||||
info.CommonDiscInfo!.Region = ProcessingTool.GetPlayStationRegion(kp2Exe);
|
||||
|
||||
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation2Version);
|
||||
break;
|
||||
|
||||
case RedumpSystem.KonamiSystemGV:
|
||||
@@ -732,29 +722,11 @@ namespace MPF.Frontend.Tools
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXboxOne:
|
||||
if (drive?.Name != null)
|
||||
{
|
||||
string xboxOneMsxcPath = Path.Combine(drive.Name, "MSXC");
|
||||
if (drive != null && Directory.Exists(xboxOneMsxcPath))
|
||||
{
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = string.Join("\n",
|
||||
Directory.GetFiles(xboxOneMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = PhysicalTool.GetXboxFilenames(drive) ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXboxSeriesXS:
|
||||
if (drive?.Name != null)
|
||||
{
|
||||
string xboxSeriesXMsxcPath = Path.Combine(drive.Name, "MSXC");
|
||||
if (drive != null && Directory.Exists(xboxSeriesXMsxcPath))
|
||||
{
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = string.Join("\n",
|
||||
Directory.GetFiles(xboxSeriesXMsxcPath, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.Filename] = PhysicalTool.GetXboxFilenames(drive) ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.NamcoSegaNintendoTriforce:
|
||||
@@ -808,83 +780,54 @@ namespace MPF.Frontend.Tools
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
string? ps1Exe = PhysicalTool.GetPlayStationExecutableName(drive);
|
||||
|
||||
// TODO: Remove this hack when DIC supports build date output
|
||||
if (isDiscImageCreator)
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", drive?.GetPlayStationExecutableName(), psx: true);
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", ps1Exe, psx: true);
|
||||
|
||||
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? psxExe) || string.IsNullOrEmpty(psxExe))
|
||||
info.CommonDiscInfo.Region = Drive.GetPlayStationRegion(psxExe);
|
||||
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStationSerial);
|
||||
info.CommonDiscInfo!.EXEDateBuildDate ??= PhysicalTool.GetFileDate(drive, ps1Exe, fixTwoDigitYear: true);
|
||||
|
||||
if (drive?.GetPlayStationExecutableInfo(out var psxSerial, out Region? psxRegion, out var psxDate) == true)
|
||||
{
|
||||
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? value) || string.IsNullOrEmpty(value))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = psxSerial ?? string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Region ??= psxRegion;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= psxDate;
|
||||
}
|
||||
if (CommentFieldExists(info, SiteCode.InternalSerialName, out ps1Exe))
|
||||
info.CommonDiscInfo!.Region = ProcessingTool.GetPlayStationRegion(ps1Exe);
|
||||
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
info.CommonDiscInfo!.LanguageSelection ??= [];
|
||||
string? ps2Exe = PhysicalTool.GetPlayStationExecutableName(drive);
|
||||
|
||||
// TODO: Remove this hack when DIC supports build date output
|
||||
if (isDiscImageCreator)
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", drive?.GetPlayStationExecutableName());
|
||||
info.CommonDiscInfo!.EXEDateBuildDate = DiscImageCreator.GetPlayStationEXEDate($"{basePath}_volDesc.txt", ps2Exe);
|
||||
|
||||
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps2Exe) || string.IsNullOrEmpty(ps2Exe))
|
||||
info.CommonDiscInfo.Region = Drive.GetPlayStationRegion(ps2Exe);
|
||||
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStationSerial);
|
||||
info.CommonDiscInfo!.EXEDateBuildDate ??= PhysicalTool.GetFileDate(drive, ps2Exe, fixTwoDigitYear: true);
|
||||
|
||||
if (drive?.GetPlayStationExecutableInfo(out var ps2Serial, out Region? ps2Region, out var ps2Date) == true)
|
||||
{
|
||||
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? value) || string.IsNullOrEmpty(value))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = ps2Serial ?? string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Region ??= ps2Region;
|
||||
info.CommonDiscInfo.EXEDateBuildDate ??= ps2Date;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
|
||||
info.VersionAndEditions!.Version = drive?.GetPlayStation2Version() ?? string.Empty;
|
||||
if (CommentFieldExists(info, SiteCode.InternalSerialName, out ps2Exe))
|
||||
info.CommonDiscInfo.Region = ProcessingTool.GetPlayStationRegion(ps2Exe);
|
||||
|
||||
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation2Version);
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation3:
|
||||
info.Extras!.DiscKey ??= addPlaceholders ? RequiredValue : string.Empty;
|
||||
info.Extras.DiscID ??= addPlaceholders ? RequiredValue : string.Empty;
|
||||
|
||||
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps3Serial) || string.IsNullOrEmpty(ps3Serial))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = drive?.GetPlayStation3Serial() ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
|
||||
info.VersionAndEditions!.Version = drive?.GetPlayStation3Version() ?? string.Empty;
|
||||
|
||||
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.Patches, out string? ps3Firmware) || string.IsNullOrEmpty(ps3Firmware))
|
||||
{
|
||||
string? firmwareVersion = drive?.GetPlayStation3FirmwareVersion();
|
||||
if (firmwareVersion != null)
|
||||
info.CommonDiscInfo!.ContentsSpecialFields![SiteCode.Patches] = $"PS3 Firmware {firmwareVersion}";
|
||||
}
|
||||
|
||||
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStation3Serial);
|
||||
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation3Version);
|
||||
SetCommentFieldIfNotExists(info, SiteCode.Patches, drive, FormatPlayStation3FirmwareVersion);
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation4:
|
||||
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps4Serial) || string.IsNullOrEmpty(ps4Serial))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = drive?.GetPlayStation4Serial() ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
|
||||
info.VersionAndEditions!.Version = drive?.GetPlayStation4Version() ?? string.Empty;
|
||||
|
||||
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStation4Serial);
|
||||
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation4Version);
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation5:
|
||||
if (!info.CommonDiscInfo!.CommentsSpecialFields!.TryGetValue(SiteCode.InternalSerialName, out string? ps5Serial) || string.IsNullOrEmpty(ps5Serial))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = drive?.GetPlayStation5Serial() ?? string.Empty;
|
||||
|
||||
if (string.IsNullOrEmpty(info.VersionAndEditions!.Version))
|
||||
info.VersionAndEditions!.Version = drive?.GetPlayStation5Version() ?? string.Empty;
|
||||
|
||||
SetCommentFieldIfNotExists(info, SiteCode.InternalSerialName, drive, PhysicalTool.GetPlayStation5Serial);
|
||||
SetVersionIfNotExists(info, drive, PhysicalTool.GetPlayStation5Version);
|
||||
break;
|
||||
|
||||
case RedumpSystem.TomyKissSite:
|
||||
@@ -901,33 +844,60 @@ namespace MPF.Frontend.Tools
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to determine if a system requires an anti-modchip scan
|
||||
/// Get a preformatted string for the PS3 firmware version, if possible
|
||||
/// </summary>
|
||||
private static bool SupportsAntiModchipScans(RedumpSystem? system)
|
||||
private static string? FormatPlayStation3FirmwareVersion(Drive? drive)
|
||||
{
|
||||
return system switch
|
||||
{
|
||||
RedumpSystem.SonyPlayStation => true,
|
||||
_ => false,
|
||||
};
|
||||
string? firmwareVersion = PhysicalTool.GetPlayStation3FirmwareVersion(drive);
|
||||
if (string.IsNullOrEmpty(firmwareVersion))
|
||||
return string.Empty;
|
||||
|
||||
return $"PS3 Firmware {firmwareVersion}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to determine if a system requires a copy protection scan
|
||||
/// Determine if a comment field exists based on key
|
||||
/// </summary>
|
||||
private static bool SupportsCopyProtectionScans(RedumpSystem? system)
|
||||
private static bool CommentFieldExists(SubmissionInfo info, SiteCode key, out string? value)
|
||||
{
|
||||
return system switch
|
||||
{
|
||||
RedumpSystem.AppleMacintosh => true,
|
||||
RedumpSystem.EnhancedCD => true,
|
||||
RedumpSystem.IBMPCcompatible => true,
|
||||
RedumpSystem.PalmOS => true,
|
||||
RedumpSystem.PocketPC => true,
|
||||
RedumpSystem.RainbowDisc => true,
|
||||
RedumpSystem.SonyElectronicBook => true,
|
||||
_ => false,
|
||||
};
|
||||
// Ensure the comments fields exist
|
||||
if (info.CommonDiscInfo!.CommentsSpecialFields == null)
|
||||
info.CommonDiscInfo.CommentsSpecialFields = [];
|
||||
|
||||
// Check if the field exists
|
||||
if (!info.CommonDiscInfo.CommentsSpecialFields.TryGetValue(key, out value))
|
||||
return false;
|
||||
if (string.IsNullOrEmpty(value))
|
||||
return false;
|
||||
|
||||
// The value is valid
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set a comment field if it doesn't already have a value
|
||||
/// </summary>
|
||||
private static void SetCommentFieldIfNotExists(SubmissionInfo info, SiteCode key, Drive? drive, Func<Drive?, string?> valueFunc)
|
||||
{
|
||||
// If the field has a valid value, skip
|
||||
if (CommentFieldExists(info, key, out _))
|
||||
return;
|
||||
|
||||
// Set the value
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![key] = valueFunc(drive) ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the version if it doesn't already have a value
|
||||
/// </summary>
|
||||
private static void SetVersionIfNotExists(SubmissionInfo info, Drive? drive, Func<Drive?, string?> valueFunc)
|
||||
{
|
||||
// If the version already exists, skip
|
||||
if (!string.IsNullOrEmpty(info.VersionAndEditions!.Version))
|
||||
return;
|
||||
|
||||
// Set the version
|
||||
info.VersionAndEditions.Version = valueFunc(drive) ?? string.Empty;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -740,7 +740,10 @@ namespace MPF.Frontend.ViewModels
|
||||
{
|
||||
VerboseLogLn($"Changed dumping program to: {((InternalProgram?)this.CurrentProgram).LongName()}");
|
||||
EnsureDiscInformation();
|
||||
// New output name depends on new environment
|
||||
GetOutputNames(false);
|
||||
// New environment depends on new output name
|
||||
EnsureDiscInformation();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1820,8 +1823,11 @@ namespace MPF.Frontend.ViewModels
|
||||
if (MediaTypes != null)
|
||||
{
|
||||
MediaType? mediaType = _environment.GetMediaType();
|
||||
int mediaTypeIndex = MediaTypes.FindIndex(m => m == mediaType);
|
||||
this.CurrentMediaType = (mediaTypeIndex > -1 ? MediaTypes[mediaTypeIndex] : MediaTypes[0]);
|
||||
if (mediaType != null)
|
||||
{
|
||||
int mediaTypeIndex = MediaTypes.FindIndex(m => m == mediaType);
|
||||
this.CurrentMediaType = (mediaTypeIndex > -1 ? MediaTypes[mediaTypeIndex] : MediaTypes[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Reenable change handling
|
||||
@@ -1911,7 +1917,7 @@ namespace MPF.Frontend.ViewModels
|
||||
{
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
case RedumpSystem.SonyPlayStation2:
|
||||
drive.GetPlayStationExecutableInfo(out string? serial, out _, out _);
|
||||
string? serial = PhysicalTool.GetPlayStationSerial(drive);
|
||||
volumeLabel = serial ?? "track";
|
||||
break;
|
||||
|
||||
@@ -1969,26 +1975,7 @@ namespace MPF.Frontend.ViewModels
|
||||
VerboseLogLn($"Supported media speeds: {string.Join(", ", this.DriveSpeeds.Select(ds => ds.ToString()).ToArray())}");
|
||||
|
||||
// Set the selected speed
|
||||
int speed = (CurrentMediaType) switch
|
||||
{
|
||||
// CD dump speed
|
||||
MediaType.CDROM => Options.PreferredDumpSpeedCD,
|
||||
MediaType.GDROM => Options.PreferredDumpSpeedCD,
|
||||
|
||||
// DVD dump speed
|
||||
MediaType.DVD => Options.PreferredDumpSpeedDVD,
|
||||
MediaType.NintendoGameCubeGameDisc => Options.PreferredDumpSpeedDVD,
|
||||
MediaType.NintendoWiiOpticalDisc => Options.PreferredDumpSpeedDVD,
|
||||
|
||||
// HD-DVD dump speed
|
||||
MediaType.HDDVD => Options.PreferredDumpSpeedHDDVD,
|
||||
|
||||
// BD dump speed
|
||||
MediaType.BluRay => Options.PreferredDumpSpeedBD,
|
||||
|
||||
// Default
|
||||
_ => Options.PreferredDumpSpeedCD,
|
||||
};
|
||||
int speed = FrontendTool.GetDefaultSpeedForMediaType(CurrentMediaType, Options);
|
||||
|
||||
VerboseLogLn($"Setting drive speed to: {speed}");
|
||||
this.DriveSpeed = speed;
|
||||
@@ -2112,11 +2099,7 @@ namespace MPF.Frontend.ViewModels
|
||||
_environment.ReportStatus += ProgressUpdated;
|
||||
|
||||
// Run the program with the parameters
|
||||
#if NET40
|
||||
ResultEventArgs result = _environment.Run(resultProgress);
|
||||
#else
|
||||
ResultEventArgs result = await _environment.Run(resultProgress);
|
||||
#endif
|
||||
|
||||
// If we didn't execute a dumping command we cannot get submission output
|
||||
if (!_environment.IsDumpingCommand())
|
||||
@@ -2223,7 +2206,7 @@ namespace MPF.Frontend.ViewModels
|
||||
string outputFilename = Path.GetFileName(_environment.OutputPath);
|
||||
|
||||
// If a complete dump already exists
|
||||
bool foundFiles = _environment.FoundAllFiles(outputDirectory, outputFilename, true);
|
||||
bool foundFiles = _environment.FoundAllFiles(outputDirectory, outputFilename);
|
||||
if (foundFiles && _displayUserMessage != null)
|
||||
{
|
||||
bool? mbresult = _displayUserMessage("Overwrite?", "A complete dump already exists! Are you sure you want to overwrite?", 2, true);
|
||||
|
||||
@@ -121,19 +121,9 @@ namespace MPF.Frontend.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
|
||||
return await RedumpClient.ValidateCredentials(username, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -27,77 +27,6 @@ namespace MPF.Processors
|
||||
|
||||
#region BaseProcessor Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
var missingFiles = new List<string>();
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}.cicm.xml"))
|
||||
missingFiles.Add($"{basePath}.cicm.xml");
|
||||
if (!File.Exists($"{basePath}.ibg"))
|
||||
missingFiles.Add($"{basePath}.ibg");
|
||||
if (!File.Exists($"{basePath}.log"))
|
||||
missingFiles.Add($"{basePath}.log");
|
||||
if (!File.Exists($"{basePath}.mhddlog.bin"))
|
||||
missingFiles.Add($"{basePath}.mhddlog.bin");
|
||||
if (!File.Exists($"{basePath}.resume.xml"))
|
||||
missingFiles.Add($"{basePath}.resume.xml");
|
||||
if (!File.Exists($"{basePath}.sub.log"))
|
||||
missingFiles.Add($"{basePath}.sub.log");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}.cicm.xml"))
|
||||
missingFiles.Add($"{basePath}.cicm.xml");
|
||||
if (!File.Exists($"{basePath}.ibg"))
|
||||
missingFiles.Add($"{basePath}.ibg");
|
||||
if (!File.Exists($"{basePath}.log"))
|
||||
missingFiles.Add($"{basePath}.log");
|
||||
if (!File.Exists($"{basePath}.mhddlog.bin"))
|
||||
missingFiles.Add($"{basePath}.mhddlog.bin");
|
||||
if (!File.Exists($"{basePath}.resume.xml"))
|
||||
missingFiles.Add($"{basePath}.resume.xml");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
missingFiles.Add("Media and system combination not supported for Aaru");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateArtifacts(SubmissionInfo info, string basePath)
|
||||
{
|
||||
info.Artifacts ??= [];
|
||||
|
||||
if (File.Exists(basePath + ".cicm.xml"))
|
||||
info.Artifacts["cicm"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + ".cicm.xml")) ?? string.Empty;
|
||||
if (File.Exists(basePath + ".ibg"))
|
||||
info.Artifacts["ibg"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".ibg"));
|
||||
if (File.Exists(basePath + ".log"))
|
||||
info.Artifacts["log"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + ".log")) ?? string.Empty;
|
||||
if (File.Exists(basePath + ".mhddlog.bin"))
|
||||
info.Artifacts["mhddlog_bin"] = Convert.ToBase64String(File.ReadAllBytes(basePath + ".mhddlog.bin"));
|
||||
if (File.Exists(basePath + ".resume.xml"))
|
||||
info.Artifacts["resume"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + ".resume.xml")) ?? string.Empty;
|
||||
if (File.Exists(basePath + ".sub.log"))
|
||||
info.Artifacts["sub_log"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + ".sub.log")) ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
|
||||
{
|
||||
@@ -257,49 +186,74 @@ namespace MPF.Processors
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
internal override List<OutputFile> GetOutputFiles(string baseFilename)
|
||||
{
|
||||
var logFiles = new List<string>();
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
if (File.Exists($"{basePath}.cicm.xml"))
|
||||
logFiles.Add($"{basePath}.cicm.xml");
|
||||
if (File.Exists($"{basePath}.error.log"))
|
||||
logFiles.Add($"{basePath}.error.log");
|
||||
if (File.Exists($"{basePath}.ibg"))
|
||||
logFiles.Add($"{basePath}.ibg");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
if (File.Exists($"{basePath}.mhddlog.bin"))
|
||||
logFiles.Add($"{basePath}.mhddlog.bin");
|
||||
if (File.Exists($"{basePath}.resume.xml"))
|
||||
logFiles.Add($"{basePath}.resume.xml");
|
||||
if (File.Exists($"{basePath}.sub.log"))
|
||||
logFiles.Add($"{basePath}.sub.log");
|
||||
|
||||
break;
|
||||
return [
|
||||
new($"{baseFilename}.aaruf", OutputFileFlags.Required),
|
||||
new($"{baseFilename}.cicm.xml", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"cicm"),
|
||||
new($"{baseFilename}.error.log", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"error_log"),
|
||||
new($"{baseFilename}.ibg", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"ibg"),
|
||||
new($"{baseFilename}.log", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"log"),
|
||||
new($"{baseFilename}.mhddlog.bin", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"mhddlog"),
|
||||
new($"{baseFilename}.resume.xml", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"resume"),
|
||||
new($"{baseFilename}.sub.log", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"sub_log"),
|
||||
];
|
||||
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
if (File.Exists($"{basePath}.cicm.xml"))
|
||||
logFiles.Add($"{basePath}.cicm.xml");
|
||||
if (File.Exists($"{basePath}.error.log"))
|
||||
logFiles.Add($"{basePath}.error.log");
|
||||
if (File.Exists($"{basePath}.ibg"))
|
||||
logFiles.Add($"{basePath}.ibg");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
if (File.Exists($"{basePath}.mhddlog.bin"))
|
||||
logFiles.Add($"{basePath}.mhddlog.bin");
|
||||
if (File.Exists($"{basePath}.resume.xml"))
|
||||
logFiles.Add($"{basePath}.resume.xml");
|
||||
|
||||
break;
|
||||
return [
|
||||
new($"{baseFilename}.aaruf", OutputFileFlags.Required),
|
||||
new($"{baseFilename}.cicm.xml", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"cicm"),
|
||||
new($"{baseFilename}.error.log", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"error_log"),
|
||||
new($"{baseFilename}.ibg", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"ibg"),
|
||||
new($"{baseFilename}.log", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"log"),
|
||||
new($"{baseFilename}.mhddlog.bin", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"mhddlog"),
|
||||
new($"{baseFilename}.resume.xml", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"resume"),
|
||||
];
|
||||
}
|
||||
|
||||
return logFiles;
|
||||
return [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -480,6 +434,7 @@ namespace MPF.Processors
|
||||
|
||||
// Add the track to the file
|
||||
cueTracks.Add(cueTrack);
|
||||
cueFile.Tracks = [.. cueTracks];
|
||||
|
||||
// Add the file to the cuesheet
|
||||
cueFiles.Add(cueFile);
|
||||
|
||||
@@ -44,21 +44,6 @@ namespace MPF.Processors
|
||||
|
||||
#region Abstract Methods
|
||||
|
||||
/// <summary>
|
||||
/// Validate if all required output files exist
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <param name="preCheck">True if this is a check done before a dump, false if done after</param>
|
||||
/// <returns>Tuple of true if all required files exist, false otherwise and a list representing missing files</returns>
|
||||
public abstract (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck);
|
||||
|
||||
/// <summary>
|
||||
/// Generate artifacts and add to the SubmissionInfo
|
||||
/// </summary>
|
||||
/// <param name="submissionInfo">Base submission info to fill in specifics for</param>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
public abstract void GenerateArtifacts(SubmissionInfo submissionInfo, string basePath);
|
||||
|
||||
/// <summary>
|
||||
/// Generate a SubmissionInfo for the output files
|
||||
/// </summary>
|
||||
@@ -67,27 +52,16 @@ namespace MPF.Processors
|
||||
/// <param name="redumpCompat">Determines if outputs are processed according to Redump specifications</param>
|
||||
public abstract void GenerateSubmissionInfo(SubmissionInfo submissionInfo, string basePath, bool redumpCompat);
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all log files generated
|
||||
// <summary>
|
||||
/// Generate a list of all output files generated
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <returns>List of all log file paths, empty otherwise</returns>
|
||||
public abstract List<string> GetLogFilePaths(string basePath);
|
||||
/// <param name="baseFilename">Base filename to use for checking</param>
|
||||
/// <returns>List of all output files, empty otherwise</returns>
|
||||
internal abstract List<OutputFile> GetOutputFiles(string baseFilename);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Virtual Methods
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all deleteable files generated
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <returns>List of all deleteable file paths, empty otherwise</returns>
|
||||
public virtual List<string> GetDeleteableFilePaths(string basePath) => [];
|
||||
|
||||
#endregion
|
||||
|
||||
#region Shared Methods
|
||||
#region Output Files
|
||||
|
||||
/// <summary>
|
||||
/// Compress log files to save space
|
||||
@@ -102,7 +76,6 @@ namespace MPF.Processors
|
||||
#if NET20 || NET35 || NET40
|
||||
return (false, "Log compression is not available for this framework version");
|
||||
#else
|
||||
|
||||
// Prepare the necessary paths
|
||||
outputFilename = Path.GetFileNameWithoutExtension(outputFilename);
|
||||
string combinedBase;
|
||||
@@ -111,16 +84,15 @@ namespace MPF.Processors
|
||||
else
|
||||
combinedBase = Path.Combine(outputDirectory, outputFilename);
|
||||
|
||||
string archiveName = combinedBase + "_logs.zip";
|
||||
// Generate the archive filename
|
||||
string archiveName = $"{combinedBase}_logs.zip";
|
||||
|
||||
// Get the list of log files from the parameters object
|
||||
var files = GetLogFilePaths(combinedBase);
|
||||
// Get the lists of zippable files
|
||||
var zippableFiles = GetZippableFilePaths(combinedBase);
|
||||
var generatedFiles = GetGeneratedFilePaths(outputDirectory, filenameSuffix);
|
||||
|
||||
// Add on generated log files if they exist
|
||||
var mpfFiles = GetGeneratedFilePaths(outputDirectory, filenameSuffix);
|
||||
files.AddRange(mpfFiles);
|
||||
|
||||
if (!files.Any())
|
||||
// Don't create an archive if there are no paths
|
||||
if (!zippableFiles.Any() && !generatedFiles.Any())
|
||||
return (true, "No files to compress!");
|
||||
|
||||
// If the file already exists, we want to delete the old one
|
||||
@@ -139,33 +111,9 @@ namespace MPF.Processors
|
||||
try
|
||||
{
|
||||
zf = ZipFile.Open(archiveName, ZipArchiveMode.Create);
|
||||
foreach (string file in files)
|
||||
{
|
||||
if (string.IsNullOrEmpty(outputDirectory))
|
||||
{
|
||||
zf.CreateEntryFromFile(file, file, CompressionLevel.Optimal);
|
||||
}
|
||||
else
|
||||
{
|
||||
string entryName = file[outputDirectory!.Length..].TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
|
||||
#if NETFRAMEWORK || NETCOREAPP3_1 || NET5_0
|
||||
zf.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
|
||||
#else
|
||||
zf.CreateEntryFromFile(file, entryName, CompressionLevel.SmallestSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
// If the file is MPF-specific, don't delete
|
||||
if (mpfFiles.Contains(file))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
_ = AddToArchive(zf, zippableFiles, outputDirectory, true);
|
||||
_ = AddToArchive(zf, generatedFiles, outputDirectory, false);
|
||||
|
||||
return (true, "Compression complete!");
|
||||
}
|
||||
@@ -204,23 +152,16 @@ namespace MPF.Processors
|
||||
return (true, "No files to delete!");
|
||||
|
||||
// Attempt to delete all of the files
|
||||
try
|
||||
foreach (string file in files)
|
||||
{
|
||||
foreach (string file in files)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
catch { }
|
||||
File.Delete(file);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return (true, "Deletion complete!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return (false, $"Deletion could not complete: {ex}");
|
||||
}
|
||||
return (true, "Deletion complete!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -229,9 +170,8 @@ namespace MPF.Processors
|
||||
/// <param name="outputDirectory">Output folder to write to</param>
|
||||
/// <param name="outputFilename">Output filename to use as the base path</param>
|
||||
/// <param name="processor">Processor object representing how to process the outputs</param>
|
||||
/// <param name="preCheck">True if this is a check done before a dump, false if done after</param>
|
||||
/// <returns>Tuple of true if all required files exist, false otherwise and a list representing missing files</returns>
|
||||
public (bool, List<string>) FoundAllFiles(string? outputDirectory, string outputFilename, bool preCheck)
|
||||
public (bool, List<string>) FoundAllFiles(string? outputDirectory, string outputFilename)
|
||||
{
|
||||
// First, sanitized the output filename to strip off any potential extension
|
||||
outputFilename = Path.GetFileNameWithoutExtension(outputFilename);
|
||||
@@ -244,9 +184,360 @@ namespace MPF.Processors
|
||||
basePath = Path.Combine(outputDirectory, outputFilename);
|
||||
|
||||
// Finally, let the parameters say if all files exist
|
||||
return CheckAllOutputFilesExist(basePath, preCheck);
|
||||
return CheckRequiredFiles(basePath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate artifacts and return them as a dictionary
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <returns>Dictiionary of artifact keys to Base64-encoded values, if possible</param>
|
||||
public Dictionary<string, string> GenerateArtifacts(string basePath)
|
||||
{
|
||||
// Get the base filename and directory from the base path
|
||||
string baseFilename = Path.GetFileName(basePath);
|
||||
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
|
||||
|
||||
// Get the list of output files
|
||||
var outputFiles = GetOutputFiles(baseFilename);
|
||||
if (outputFiles.Count == 0)
|
||||
return [];
|
||||
|
||||
// Create the artifacts dictionary
|
||||
var artifacts = new Dictionary<string, string>();
|
||||
|
||||
// Only try to create artifacts for files that exist
|
||||
foreach (var outputFile in outputFiles)
|
||||
{
|
||||
// Skip non-artifact files
|
||||
if (!outputFile.IsArtifact || outputFile.ArtifactKey == null)
|
||||
continue;
|
||||
|
||||
// Skip non-existent files
|
||||
foreach (string filename in outputFile.Filenames)
|
||||
{
|
||||
string outputFilePath = Path.Combine(baseDirectory, filename);
|
||||
if (!File.Exists(outputFilePath))
|
||||
continue;
|
||||
|
||||
// Get binary artifacts as a byte array
|
||||
if (outputFile.IsBinaryArtifact)
|
||||
{
|
||||
byte[] data = File.ReadAllBytes(filename);
|
||||
string str = Convert.ToBase64String(data);
|
||||
artifacts.Add(outputFile.ArtifactKey, str);
|
||||
}
|
||||
else
|
||||
{
|
||||
string? data = ProcessingTool.GetFullFile(filename);
|
||||
string str = ProcessingTool.GetBase64(data) ?? string.Empty;
|
||||
artifacts.Add(outputFile.ArtifactKey, str);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return artifacts;
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
/// <summary>
|
||||
/// Try to add a set of files to an existing archive
|
||||
/// </summary>
|
||||
/// <param name="archive">Archive to add the file to</param>
|
||||
/// <param name="files">Full path to a set of existing files</param>
|
||||
/// <param name="outputDirectory">Directory that the existing files live in</param>
|
||||
/// <param name="delete">Indicates if the files should be deleted after adding</param>
|
||||
/// <returns>True if all files were added successfully, false otherwise</returns>
|
||||
private static bool AddToArchive(ZipArchive archive, List<string> files, string? outputDirectory, bool delete)
|
||||
{
|
||||
// An empty list means success
|
||||
if (files.Count == 0)
|
||||
return true;
|
||||
|
||||
// Loop through and add all files
|
||||
bool allAdded = true;
|
||||
foreach (string file in files)
|
||||
{
|
||||
allAdded &= AddToArchive(archive, file, outputDirectory, delete);
|
||||
}
|
||||
|
||||
return allAdded;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to add a file to an existing archive
|
||||
/// </summary>
|
||||
/// <param name="archive">Archive to add the file to</param>
|
||||
/// <param name="file">Full path to an existing file</param>
|
||||
/// <param name="outputDirectory">Directory that the existing file lives in</param>
|
||||
/// <param name="delete">Indicates if the file should be deleted after adding</param>
|
||||
/// <returns>True if the file was added successfully, false otherwise</returns>
|
||||
private static bool AddToArchive(ZipArchive archive, string file, string? outputDirectory, bool delete)
|
||||
{
|
||||
// Check if the file exists
|
||||
if (!File.Exists(file))
|
||||
return false;
|
||||
|
||||
// Get the entry name from the file
|
||||
string entryName = file;
|
||||
if (!string.IsNullOrEmpty(outputDirectory))
|
||||
entryName = entryName.Substring(outputDirectory!.Length);
|
||||
|
||||
// Ensure the entry is formatted correctly
|
||||
entryName = entryName.TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
|
||||
|
||||
// Create and add the entry
|
||||
try
|
||||
{
|
||||
#if NETFRAMEWORK || NETCOREAPP3_1 || NET5_0
|
||||
archive.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
|
||||
#else
|
||||
archive.CreateEntryFromFile(file, entryName, CompressionLevel.SmallestSize);
|
||||
#endif
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to delete the file if requested
|
||||
if (delete)
|
||||
{
|
||||
try { File.Delete(file); } catch { }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Validate if all required output files exist
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <returns>Tuple of true if all required files exist, false otherwise and a list representing missing files</returns>
|
||||
private (bool, List<string>) CheckRequiredFiles(string basePath)
|
||||
{
|
||||
// Get the base filename and directory from the base path
|
||||
string baseFilename = Path.GetFileName(basePath);
|
||||
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
|
||||
|
||||
// Get the list of output files
|
||||
var outputFiles = GetOutputFiles(baseFilename);
|
||||
if (outputFiles.Count == 0)
|
||||
return (false, ["Media and system combination not supported"]);
|
||||
|
||||
// Check for the log file
|
||||
bool logArchiveExists = false;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
ZipArchive? logArchive = null;
|
||||
#endif
|
||||
if (File.Exists($"{basePath}_logs.zip"))
|
||||
{
|
||||
logArchiveExists = true;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
try
|
||||
{
|
||||
// Try to open the archive
|
||||
logArchive = ZipFile.OpenRead($"{basePath}_logs.zip");
|
||||
}
|
||||
catch
|
||||
{
|
||||
logArchiveExists = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get a list of all missing required files
|
||||
var missingFiles = new List<string>();
|
||||
foreach (var outputFile in outputFiles)
|
||||
{
|
||||
// Only check required files
|
||||
if (!outputFile.IsRequired)
|
||||
continue;
|
||||
|
||||
// Use the built-in existence function
|
||||
if (outputFile.Exists(baseDirectory))
|
||||
continue;
|
||||
|
||||
// If the log archive doesn't exist
|
||||
if (!logArchiveExists)
|
||||
{
|
||||
missingFiles.Add(outputFile.Filenames[0]);
|
||||
continue;
|
||||
}
|
||||
|
||||
#if NET20 || NET35 || NET40
|
||||
// Assume the zipfile has the file in it
|
||||
continue;
|
||||
#else
|
||||
// Check the log archive
|
||||
if (outputFile.Exists(logArchive))
|
||||
continue;
|
||||
|
||||
// Add the file to the missing list
|
||||
missingFiles.Add(outputFile.Filenames[0]);
|
||||
#endif
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all deleteable filenames
|
||||
/// </summary>
|
||||
/// <param name="baseFilename">Base filename to use for generation</param>
|
||||
/// <returns>List of all deleteable filenames, empty otherwise</returns>
|
||||
private List<string> GetDeleteableFilenames(string baseFilename)
|
||||
{
|
||||
// Get the list of output files
|
||||
var outputFiles = GetOutputFiles(baseFilename);
|
||||
if (outputFiles.Count == 0)
|
||||
return [];
|
||||
|
||||
// Filter down to deleteable files
|
||||
var deleteableFiles = outputFiles.Where(of => of.IsDeleteable);
|
||||
return deleteableFiles.SelectMany(of => of.Filenames).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all deleteable file paths
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <returns>List of all deleteable file paths, empty otherwise</returns>
|
||||
private List<string> GetDeleteableFilePaths(string basePath)
|
||||
{
|
||||
// Get the base filename and directory from the base path
|
||||
string baseFilename = Path.GetFileName(basePath);
|
||||
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
|
||||
|
||||
// Get the list of deleteable files
|
||||
var deleteableFilenames = GetDeleteableFilenames(baseFilename);
|
||||
if (deleteableFilenames.Count == 0)
|
||||
return [];
|
||||
|
||||
// Return only files that exist
|
||||
var deleteableFiles = new List<string>();
|
||||
foreach (var filename in deleteableFilenames)
|
||||
{
|
||||
// Skip non-existent files
|
||||
string outputFilePath = Path.Combine(baseDirectory, filename);
|
||||
if (!File.Exists(outputFilePath))
|
||||
continue;
|
||||
|
||||
deleteableFiles.Add(outputFilePath);
|
||||
}
|
||||
|
||||
return deleteableFiles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all MPF-generated filenames
|
||||
/// </summary>
|
||||
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
|
||||
/// <returns>List of all MPF-generated filenames, empty otherwise</returns>
|
||||
private static List<string> GetGeneratedFilenames(string? filenameSuffix)
|
||||
{
|
||||
// Set the base file path names
|
||||
const string submissionInfoBase = "!submissionInfo";
|
||||
const string protectionInfoBase = "!protectionInfo";
|
||||
|
||||
// Ensure the filename suffix is formatted correctly
|
||||
filenameSuffix = string.IsNullOrEmpty(filenameSuffix) ? string.Empty : $"_{filenameSuffix}";
|
||||
|
||||
// Define the output filenames
|
||||
return [
|
||||
$"{protectionInfoBase}{filenameSuffix}.txt",
|
||||
$"{submissionInfoBase}{filenameSuffix}.json",
|
||||
$"{submissionInfoBase}{filenameSuffix}.json.gz",
|
||||
$"{submissionInfoBase}{filenameSuffix}.txt",
|
||||
];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all MPF-specific log files generated
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output folder to write to</param>
|
||||
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
|
||||
/// <returns>List of all log file paths, empty otherwise</returns>
|
||||
private static List<string> GetGeneratedFilePaths(string? outputDirectory, string? filenameSuffix)
|
||||
{
|
||||
// Get the list of generated files
|
||||
var generatedFilenames = GetGeneratedFilenames(filenameSuffix);
|
||||
if (generatedFilenames.Count == 0)
|
||||
return [];
|
||||
|
||||
// Ensure the output directory
|
||||
outputDirectory ??= string.Empty;
|
||||
|
||||
// Return only files that exist
|
||||
var generatedFiles = new List<string>();
|
||||
foreach (var filename in generatedFilenames)
|
||||
{
|
||||
// Skip non-existent files
|
||||
string outputFilePath = Path.Combine(outputDirectory, filename);
|
||||
if (!File.Exists(outputFilePath))
|
||||
continue;
|
||||
|
||||
generatedFiles.Add(outputFilePath);
|
||||
}
|
||||
|
||||
return generatedFiles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all zippable filenames
|
||||
/// </summary>
|
||||
/// <param name="baseFilename">Base filename to use for generation</param>
|
||||
/// <returns>List of all zippable filenames, empty otherwise</returns>
|
||||
private List<string> GetZippableFilenames(string baseFilename)
|
||||
{
|
||||
// Get the list of output files
|
||||
var outputFiles = GetOutputFiles(baseFilename);
|
||||
if (outputFiles.Count == 0)
|
||||
return [];
|
||||
|
||||
// Filter down to deleteable files
|
||||
var deleteableFiles = outputFiles.Where(of => of.IsZippable);
|
||||
return deleteableFiles.SelectMany(of => of.Filenames).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all zippable file paths
|
||||
/// </summary>
|
||||
/// <param name="basePath">Base filename and path to use for checking</param>
|
||||
/// <returns>List of all zippable file paths, empty otherwise</returns>
|
||||
private List<string> GetZippableFilePaths(string basePath)
|
||||
{
|
||||
// Get the base filename and directory from the base path
|
||||
string baseFilename = Path.GetFileName(basePath);
|
||||
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
|
||||
|
||||
// Get the list of zippable files
|
||||
var zippableFilenames = GetZippableFilenames(baseFilename);
|
||||
if (zippableFilenames.Count == 0)
|
||||
return [];
|
||||
|
||||
// Return only files that exist
|
||||
var zippableFiles = new List<string>();
|
||||
foreach (var filename in zippableFilenames)
|
||||
{
|
||||
// Skip non-existent files
|
||||
string outputFilePath = Path.Combine(baseDirectory, filename);
|
||||
if (!File.Exists(outputFilePath))
|
||||
continue;
|
||||
|
||||
zippableFiles.Add(outputFilePath);
|
||||
}
|
||||
|
||||
return zippableFiles;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Shared Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get the hex contents of the PIC file
|
||||
/// </summary>
|
||||
@@ -334,64 +625,6 @@ namespace MPF.Processors
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a list of all MPF-specific log files generated
|
||||
/// </summary>
|
||||
/// <param name="outputDirectory">Output folder to write to</param>
|
||||
/// <param name="filenameSuffix">Optional suffix to append to the filename</param>
|
||||
/// <returns>List of all log file paths, empty otherwise</returns>
|
||||
private static List<string> GetGeneratedFilePaths(string? outputDirectory, string? filenameSuffix)
|
||||
{
|
||||
var files = new List<string>();
|
||||
|
||||
if (string.IsNullOrEmpty(outputDirectory) && string.IsNullOrEmpty(filenameSuffix))
|
||||
{
|
||||
if (File.Exists("!submissionInfo.txt"))
|
||||
files.Add("!submissionInfo.txt");
|
||||
if (File.Exists("!submissionInfo.json"))
|
||||
files.Add("!submissionInfo.json");
|
||||
if (File.Exists("!submissionInfo.json.gz"))
|
||||
files.Add("!submissionInfo.json.gz");
|
||||
if (File.Exists("!protectionInfo.txt"))
|
||||
files.Add("!protectionInfo.txt");
|
||||
}
|
||||
else if (string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
|
||||
{
|
||||
if (File.Exists($"!submissionInfo_{filenameSuffix}.txt"))
|
||||
files.Add($"!submissionInfo_{filenameSuffix}.txt");
|
||||
if (File.Exists($"!submissionInfo_{filenameSuffix}.json"))
|
||||
files.Add($"!submissionInfo_{filenameSuffix}.json");
|
||||
if (File.Exists($"!submissionInfo_{filenameSuffix}.json.gz"))
|
||||
files.Add($"!submissionInfo_{filenameSuffix}.json.gz");
|
||||
if (File.Exists($"!protectionInfo_{filenameSuffix}.txt"))
|
||||
files.Add($"!protectionInfo_{filenameSuffix}.txt");
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(outputDirectory) && string.IsNullOrEmpty(filenameSuffix))
|
||||
{
|
||||
if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.txt")))
|
||||
files.Add(Path.Combine(outputDirectory, "!submissionInfo.txt"));
|
||||
if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.json")))
|
||||
files.Add(Path.Combine(outputDirectory, "!submissionInfo.json"));
|
||||
if (File.Exists(Path.Combine(outputDirectory, "!submissionInfo.json.gz")))
|
||||
files.Add(Path.Combine(outputDirectory, "!submissionInfo.json.gz"));
|
||||
if (File.Exists(Path.Combine(outputDirectory, "!protectionInfo.txt")))
|
||||
files.Add(Path.Combine(outputDirectory, "!protectionInfo.txt"));
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(outputDirectory) && !string.IsNullOrEmpty(filenameSuffix))
|
||||
{
|
||||
if (File.Exists(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.txt")))
|
||||
files.Add(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.txt"));
|
||||
if (File.Exists(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json")))
|
||||
files.Add(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json"));
|
||||
if (File.Exists(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json.gz")))
|
||||
files.Add(Path.Combine(outputDirectory, $"!submissionInfo_{filenameSuffix}.json.gz"));
|
||||
if (File.Exists(Path.Combine(outputDirectory, $"!protectionInfo_{filenameSuffix}.txt")))
|
||||
files.Add(Path.Combine(outputDirectory, $"!protectionInfo_{filenameSuffix}.txt"));
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,44 +19,6 @@ namespace MPF.Processors
|
||||
|
||||
#region BaseProcessor Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
var missingFiles = new List<string>();
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.DVD: // Only added here to help users; not strictly correct
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}-dumpinfo.txt"))
|
||||
missingFiles.Add($"{basePath}-dumpinfo.txt");
|
||||
if (!File.Exists($"{basePath}.bca"))
|
||||
missingFiles.Add($"{basePath}.bca");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
missingFiles.Add("Media and system combination not supported for CleanRip");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateArtifacts(SubmissionInfo info, string basePath)
|
||||
{
|
||||
info.Artifacts ??= [];
|
||||
|
||||
if (File.Exists(basePath + ".bca"))
|
||||
info.Artifacts["bca"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + ".bca", binary: true)) ?? string.Empty;
|
||||
if (File.Exists(basePath + "-dumpinfo.txt"))
|
||||
info.Artifacts["dumpinfo"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(basePath + "-dumpinfo.txt")) ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
|
||||
{
|
||||
@@ -105,23 +67,28 @@ namespace MPF.Processors
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
internal override List<OutputFile> GetOutputFiles(string baseFilename)
|
||||
{
|
||||
var logFiles = new List<string>();
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.DVD: // Only added here to help users; not strictly correct
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (File.Exists($"{basePath}-dumpinfo.txt"))
|
||||
logFiles.Add($"{basePath}-dumpinfo.txt");
|
||||
if (File.Exists($"{basePath}.bca"))
|
||||
logFiles.Add($"{basePath}.bca");
|
||||
return [
|
||||
new($"{baseFilename}.bca", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"bca"),
|
||||
new($"{baseFilename}.iso", OutputFileFlags.Required),
|
||||
|
||||
break;
|
||||
new($"{baseFilename}-dumpinfo.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"dumpinfo"),
|
||||
];
|
||||
}
|
||||
|
||||
return logFiles;
|
||||
return [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
97
MPF.Processors/CustomOutputFile.cs
Normal file
97
MPF.Processors/CustomOutputFile.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
|
||||
namespace MPF.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single output file with custom detection rules
|
||||
/// </summary>
|
||||
internal class CustomOutputFile : OutputFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Optional func for determining if a file exists
|
||||
/// </summary>
|
||||
private readonly Func<string, bool> _existsFunc;
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with a single filename
|
||||
/// </summary>
|
||||
public CustomOutputFile(string filename, OutputFileFlags flags, Func<string, bool> existsFunc)
|
||||
: base([filename], flags)
|
||||
{
|
||||
_existsFunc = existsFunc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with a single filename
|
||||
/// </summary>
|
||||
public CustomOutputFile(string filename, OutputFileFlags flags, string artifactKey, Func<string, bool> existsFunc)
|
||||
: base([filename], flags, artifactKey)
|
||||
{
|
||||
_existsFunc = existsFunc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with set of filenames
|
||||
/// </summary>
|
||||
public CustomOutputFile(string[] filenames, OutputFileFlags flags, Func<string, bool> existsFunc)
|
||||
: base(filenames, flags)
|
||||
{
|
||||
_existsFunc = existsFunc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with set of filenames
|
||||
/// </summary>
|
||||
public CustomOutputFile(string[] filenames, OutputFileFlags flags, string artifactKey, Func<string, bool> existsFunc)
|
||||
: base(filenames, flags, artifactKey)
|
||||
{
|
||||
_existsFunc = existsFunc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if an output file exists in a base directory
|
||||
/// </summary>
|
||||
/// <param name="baseDirectory">Base directory to check in</param>
|
||||
public override bool Exists(string baseDirectory)
|
||||
{
|
||||
// If the base directory is invalid
|
||||
if (string.IsNullOrEmpty(baseDirectory))
|
||||
return false;
|
||||
if (!Directory.Exists(baseDirectory))
|
||||
return false;
|
||||
|
||||
foreach (string filename in Filenames)
|
||||
{
|
||||
// Check for invalid filenames
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
string possiblePath = Path.Combine(baseDirectory, filename);
|
||||
if (_existsFunc(possiblePath))
|
||||
return true;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
/// <summary>
|
||||
/// Indicates if an output file exists in an archive
|
||||
/// </summary>
|
||||
/// <param name="archive">Zip archive to check in</param>
|
||||
public override bool Exists(ZipArchive? archive)
|
||||
{
|
||||
// Files aren't extracted so this check can't be done
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,52 @@ using SabreTools.Models.Logiqx;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
|
||||
/*
|
||||
If there are no external programs, such as error checking, etc., DIC outputs
|
||||
a slightly different set of files. This reduced set needs to be documented in
|
||||
order for special use cases, such as self-built versions of DIC or removed
|
||||
helper programs, can be detected to the best of our ability. Below is the list
|
||||
of files that are generated in that case:
|
||||
|
||||
.bin
|
||||
.c2
|
||||
.ccd
|
||||
.cue
|
||||
.img/.imgtmp
|
||||
.scm/.scmtmp
|
||||
.sub/.subtmp
|
||||
_cmd.txt (formerly)
|
||||
_img.cue
|
||||
|
||||
This list needs to be translated into the minimum viable set of information
|
||||
such that things like error checking can be passed back as a flag, or some
|
||||
similar method.
|
||||
|
||||
Here are some notes about the various output files and what they represent:
|
||||
- bin - Final split output disc image (CD/GD only)
|
||||
- c2 - Represents each byte per sector as one bit; 0 means no error, 1 means error
|
||||
- c2Error - Human-readable version of `c2`; only errors are printed
|
||||
- ccd - CloneCD control file referencing the `img` file
|
||||
- cmd - Represents the commandline that was run
|
||||
- cue - CDRWIN cuesheet referencing the `bin` file(s)
|
||||
- dat - Logiqx datfile referencing the `bin` file(s)
|
||||
- disc - Disc metadata and information
|
||||
- drive - Drive metadata and information
|
||||
- img - CloneCD output disc image (CD/GD only)
|
||||
- img.cue - CDRWIN cuesheet referencing the `img` file
|
||||
- img_EdcEcc - ECC check output as run on the `img` file
|
||||
- iso - Final output disc image (DVD/BD only)
|
||||
- mainError - Read, drive, or system errors
|
||||
- mainInfo - ISOBuster-formatted sector information
|
||||
- scm - Scrambled disc image
|
||||
- sub - Binary subchannel data as read from the disc
|
||||
- subError - Subchannel read errors
|
||||
- subInfo - Subchannel informational messages
|
||||
- subIntention - Subchannel intentional error information
|
||||
- subReadable - Human-readable version of `sub`
|
||||
- toc - Binary representation of the table of contents
|
||||
- volDesc - Volume descriptor information
|
||||
*/
|
||||
namespace MPF.Processors
|
||||
{
|
||||
/// <summary>
|
||||
@@ -21,270 +67,6 @@ namespace MPF.Processors
|
||||
#region BaseProcessor Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
/*
|
||||
If there are no external programs, such as error checking, etc., DIC outputs
|
||||
a slightly different set of files. This reduced set needs to be documented in
|
||||
order for special use cases, such as self-built versions of DIC or removed
|
||||
helper programs, can be detected to the best of our ability. Below is the list
|
||||
of files that are generated in that case:
|
||||
|
||||
.bin
|
||||
.c2
|
||||
.ccd
|
||||
.cue
|
||||
.img/.imgtmp
|
||||
.scm/.scmtmp
|
||||
.sub/.subtmp
|
||||
_cmd.txt (formerly)
|
||||
_img.cue
|
||||
|
||||
This list needs to be translated into the minimum viable set of information
|
||||
such that things like error checking can be passed back as a flag, or some
|
||||
similar method.
|
||||
|
||||
Here are some notes about the various output files and what they represent:
|
||||
- bin - Final split output disc image (CD/GD only)
|
||||
- c2 - Represents each byte per sector as one bit; 0 means no error, 1 means error
|
||||
- c2Error - Human-readable version of `c2`; only errors are printed
|
||||
- ccd - CloneCD control file referencing the `img` file
|
||||
- cmd - Represents the commandline that was run
|
||||
- cue - CDRWIN cuesheet referencing the `bin` file(s)
|
||||
- dat - Logiqx datfile referencing the `bin` file(s)
|
||||
- disc - Disc metadata and information
|
||||
- drive - Drive metadata and information
|
||||
- img - CloneCD output disc image (CD/GD only)
|
||||
- img.cue - CDRWIN cuesheet referencing the `img` file
|
||||
- img_EdcEcc - ECC check output as run on the `img` file
|
||||
- iso - Final output disc image (DVD/BD only)
|
||||
- mainError - Read, drive, or system errors
|
||||
- mainInfo - ISOBuster-formatted sector information
|
||||
- scm - Scrambled disc image
|
||||
- sub - Binary subchannel data as read from the disc
|
||||
- subError - Subchannel read errors
|
||||
- subInfo - Subchannel informational messages
|
||||
- subIntention - Subchannel intentional error information
|
||||
- subReadable - Human-readable version of `sub`
|
||||
- toc - Binary representation of the table of contents
|
||||
- volDesc - Volume descriptor information
|
||||
*/
|
||||
|
||||
var missingFiles = new List<string>();
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
case MediaType.GDROM:
|
||||
if (!File.Exists($"{basePath}.cue"))
|
||||
missingFiles.Add($"{basePath}.cue");
|
||||
if (!File.Exists($"{basePath}.img") && !File.Exists($"{basePath}.imgtmp"))
|
||||
missingFiles.Add($"{basePath}.img");
|
||||
|
||||
// Audio-only discs don't output these files
|
||||
if (!System.IsAudio())
|
||||
{
|
||||
if (!File.Exists($"{basePath}.scm") && !File.Exists($"{basePath}.scmtmp"))
|
||||
missingFiles.Add($"{basePath}.scm");
|
||||
}
|
||||
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
// GD-ROM and GD-R don't output this for the HD area
|
||||
if (Type != MediaType.GDROM)
|
||||
{
|
||||
if (!File.Exists($"{basePath}.ccd"))
|
||||
missingFiles.Add($"{basePath}.ccd");
|
||||
}
|
||||
|
||||
if (!File.Exists($"{basePath}.dat"))
|
||||
missingFiles.Add($"{basePath}.dat");
|
||||
if (!File.Exists($"{basePath}.sub") && !File.Exists($"{basePath}.subtmp"))
|
||||
missingFiles.Add($"{basePath}.sub");
|
||||
if (!File.Exists($"{basePath}_disc.txt"))
|
||||
missingFiles.Add($"{basePath}_disc.txt");
|
||||
if (!File.Exists($"{basePath}_drive.txt"))
|
||||
missingFiles.Add($"{basePath}_drive.txt");
|
||||
if (!File.Exists($"{basePath}_img.cue"))
|
||||
missingFiles.Add($"{basePath}_img.cue");
|
||||
if (!File.Exists($"{basePath}_mainError.txt"))
|
||||
missingFiles.Add($"{basePath}_mainError.txt");
|
||||
if (!File.Exists($"{basePath}_mainInfo.txt"))
|
||||
missingFiles.Add($"{basePath}_mainInfo.txt");
|
||||
if (!File.Exists($"{basePath}_subError.txt"))
|
||||
missingFiles.Add($"{basePath}_subError.txt");
|
||||
if (!File.Exists($"{basePath}_subInfo.txt"))
|
||||
missingFiles.Add($"{basePath}_subInfo.txt");
|
||||
if (!File.Exists($"{basePath}_subReadable.txt") && !File.Exists($"{basePath}_sub.txt"))
|
||||
missingFiles.Add($"{basePath}_subReadable.txt");
|
||||
if (!File.Exists($"{basePath}_volDesc.txt"))
|
||||
missingFiles.Add($"{basePath}_volDesc.txt");
|
||||
|
||||
// Audio-only discs don't output these files
|
||||
if (!System.IsAudio())
|
||||
{
|
||||
if (!File.Exists($"{basePath}.img_EdcEcc.txt") && !File.Exists($"{basePath}.img_EccEdc.txt"))
|
||||
missingFiles.Add($"{basePath}.img_EdcEcc.txt");
|
||||
}
|
||||
}
|
||||
|
||||
// Removed or inconsistent files
|
||||
//{
|
||||
// // Doesn't output on Linux
|
||||
// if (!File.Exists($"{basePath}.c2"))
|
||||
// missingFiles.Add($"{basePath}.c2");
|
||||
|
||||
// // Doesn't output on Linux
|
||||
// if (!File.Exists($"{basePath}_c2Error.txt"))
|
||||
// missingFiles.Add($"{basePath}_c2Error.txt");
|
||||
|
||||
// // Replaced by timestamp-named file
|
||||
// if (!File.Exists($"{basePath}_cmd.txt"))
|
||||
// missingFiles.Add($"{basePath}_cmd.txt");
|
||||
|
||||
// // Not guaranteed output
|
||||
// if (!File.Exists($"{basePath}_subIntention.txt"))
|
||||
// missingFiles.Add($"{basePath}_subIntention.txt");
|
||||
|
||||
// // Not guaranteed output
|
||||
// if (File.Exists($"{basePath}_suppl.dat"))
|
||||
// missingFiles.Add($"{basePath}_suppl.dat");
|
||||
|
||||
// // Not guaranteed output (at least PCE)
|
||||
// if (!File.Exists($"{basePath}.toc"))
|
||||
// missingFiles.Add($"{basePath}.toc");
|
||||
//}
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}.dat"))
|
||||
missingFiles.Add($"{basePath}.dat");
|
||||
if (!File.Exists($"{basePath}_disc.txt"))
|
||||
missingFiles.Add($"{basePath}_disc.txt");
|
||||
if (!File.Exists($"{basePath}_drive.txt"))
|
||||
missingFiles.Add($"{basePath}_drive.txt");
|
||||
if (!File.Exists($"{basePath}_mainError.txt"))
|
||||
missingFiles.Add($"{basePath}_mainError.txt");
|
||||
if (!File.Exists($"{basePath}_mainInfo.txt"))
|
||||
missingFiles.Add($"{basePath}_mainInfo.txt");
|
||||
if (!File.Exists($"{basePath}_volDesc.txt"))
|
||||
missingFiles.Add($"{basePath}_volDesc.txt");
|
||||
}
|
||||
|
||||
// Removed or inconsistent files
|
||||
//{
|
||||
// // Replaced by timestamp-named file
|
||||
// if (!File.Exists($"{basePath}_cmd.txt"))
|
||||
// missingFiles.Add($"{basePath}_cmd.txt");
|
||||
|
||||
// // Not guaranteed output
|
||||
// if (File.Exists($"{basePath}_CSSKey.txt"))
|
||||
// missingFiles.Add($"{basePath}_CSSKey.txt");
|
||||
|
||||
// // Only output for some parameters
|
||||
// if (File.Exists($"{basePath}.raw"))
|
||||
// missingFiles.Add($"{basePath}.raw");
|
||||
|
||||
// // Not guaranteed output
|
||||
// if (File.Exists($"{basePath}_suppl.dat"))
|
||||
// missingFiles.Add($"{basePath}_suppl.dat");
|
||||
//}
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.FloppyDisk:
|
||||
case MediaType.HardDisk:
|
||||
// TODO: Determine what outputs come out from a HDD, SD, etc.
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}.dat"))
|
||||
missingFiles.Add($"{basePath}.dat");
|
||||
if (!File.Exists($"{basePath}_disc.txt"))
|
||||
missingFiles.Add($"{basePath}_disc.txt");
|
||||
}
|
||||
|
||||
// Removed or inconsistent files
|
||||
//{
|
||||
// // Replaced by timestamp-named file
|
||||
// if (!File.Exists($"{basePath}_cmd.txt"))
|
||||
// missingFiles.Add($"{basePath}_cmd.txt");
|
||||
//}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
missingFiles.Add("Media and system combination not supported for DiscImageCreator");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateArtifacts(SubmissionInfo info, string basePath)
|
||||
{
|
||||
info.Artifacts ??= [];
|
||||
|
||||
//if (File.Exists($"{basePath}.c2"))
|
||||
// info.Artifacts["c2"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.c2")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_c2Error.txt"))
|
||||
info.Artifacts["c2Error"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_c2Error.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.ccd"))
|
||||
info.Artifacts["ccd"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.ccd")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_cmd.txt")) // TODO: Figure out how to read in the timestamp-named file
|
||||
info.Artifacts["cmd"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_cmd.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_CSSKey.txt"))
|
||||
info.Artifacts["csskey"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_CSSKey.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.cue"))
|
||||
info.Artifacts["cue"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.cue")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.dat"))
|
||||
info.Artifacts["dat"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.dat")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_disc.txt"))
|
||||
info.Artifacts["disc"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_disc.txt")) ?? string.Empty;
|
||||
//if (File.Exists(Path.Combine(outputDirectory, $"{basePath}_DMI.bin")))
|
||||
// info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes(Path.Combine(outputDirectory, $"{basePath}_DMI.bin"))) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_drive.txt"))
|
||||
info.Artifacts["drive"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_drive.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_img.cue"))
|
||||
info.Artifacts["img_cue"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_img.cue")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.img_EdcEcc.txt"))
|
||||
info.Artifacts["img_EdcEcc"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.img_EdcEcc.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.img_EccEdc.txt"))
|
||||
info.Artifacts["img_EdcEcc"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.img_EccEdc.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_mainError.txt"))
|
||||
info.Artifacts["mainError"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_mainError.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_mainInfo.txt"))
|
||||
info.Artifacts["mainInfo"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_mainInfo.txt")) ?? string.Empty;
|
||||
//if (File.Exists($"{basePath}_PFI.bin"))
|
||||
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")) ?? string.Empty;
|
||||
//if (File.Exists($"{basePath}_PIC.bin"))
|
||||
// info.Artifacts["pic"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PIC.bin")) ?? string.Empty;
|
||||
//if (File.Exists($"{basePath}_SS.bin"))
|
||||
// info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_SS.bin")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.sub"))
|
||||
info.Artifacts["sub"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}.sub")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_subError.txt"))
|
||||
info.Artifacts["subError"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_subError.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_subInfo.txt"))
|
||||
info.Artifacts["subInfo"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_subInfo.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_subIntention.txt"))
|
||||
info.Artifacts["subIntention"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_subIntention.txt")) ?? string.Empty;
|
||||
//if (File.Exists($"{basePath}_sub.txt"))
|
||||
// info.Artifacts["subReadable"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_sub.txt")) ?? string.Empty;
|
||||
//if (File.Exists($"{basePath}_subReadable.txt"))
|
||||
// info.Artifacts["subReadable"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_subReadable.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_volDesc.txt"))
|
||||
info.Artifacts["volDesc"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_volDesc.txt")) ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <remarks>Determining the PSX/PS2 executable name is the last use of drive in this method</remarks>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
|
||||
{
|
||||
var outputDirectory = Path.GetDirectoryName(basePath);
|
||||
@@ -450,7 +232,7 @@ namespace MPF.Processors
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXbox:
|
||||
string xmidString = ProcessingTool.GetXGD1XMID($"{basePath}_DMI.bin");
|
||||
string xmidString = ProcessingTool.GetXMID($"{basePath}_DMI.bin");
|
||||
var xmid = SabreTools.Serialization.Wrappers.XMID.Create(xmidString);
|
||||
if (xmid != null)
|
||||
{
|
||||
@@ -494,7 +276,7 @@ namespace MPF.Processors
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXbox360:
|
||||
string xemidString = ProcessingTool.GetXGD23XeMID($"{basePath}_DMI.bin");
|
||||
string xemidString = ProcessingTool.GetXeMID($"{basePath}_DMI.bin");
|
||||
var xemid = SabreTools.Serialization.Wrappers.XeMID.Create(xemidString);
|
||||
if (xemid != null)
|
||||
{
|
||||
@@ -698,180 +480,229 @@ namespace MPF.Processors
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetDeleteableFilePaths(string basePath)
|
||||
internal override List<OutputFile> GetOutputFiles(string baseFilename)
|
||||
{
|
||||
var deleteableFiles = new List<string>();
|
||||
switch (Type)
|
||||
{
|
||||
// TODO: Handle (Pregap) files -- need examples
|
||||
case MediaType.CDROM:
|
||||
return [
|
||||
new($"{baseFilename}.c2", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"c2"), // Doesn't output on Linux
|
||||
new($"{baseFilename}.ccd", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"ccd"),
|
||||
new($"{baseFilename}.cue", OutputFileFlags.Required),
|
||||
new($"{baseFilename}.dat", OutputFileFlags.Required
|
||||
| OutputFileFlags.Zippable),
|
||||
new($"{baseFilename}.img", OutputFileFlags.Required
|
||||
| OutputFileFlags.Deleteable),
|
||||
new([$"{baseFilename}.img_EdcEcc.txt", $"{baseFilename}.img_EccEdc.txt"], System.IsAudio()
|
||||
? OutputFileFlags.Artifact | OutputFileFlags.Zippable
|
||||
: OutputFileFlags.Required | OutputFileFlags.Artifact | OutputFileFlags.Zippable,
|
||||
"img_edcecc"),
|
||||
new([$"{baseFilename}.scm", $"{baseFilename}.scmtmp"], System.IsAudio()
|
||||
? OutputFileFlags.Deleteable
|
||||
: OutputFileFlags.Required | OutputFileFlags.Deleteable),
|
||||
new([$"{baseFilename}.sub", $"{baseFilename}.subtmp"], OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"sub"),
|
||||
new($"{baseFilename}.toc", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"toc"),
|
||||
|
||||
new($"{baseFilename}_c2Error.txt", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"c2_error"), // Doesn't output on Linux
|
||||
new RegexOutputFile(Regex.Escape(baseFilename) + @"_(\d{8})T\d{6}\.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"cmd"),
|
||||
new($"{baseFilename}_cmd.txt", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"cmd"),
|
||||
new($"{baseFilename}_disc.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"disc"),
|
||||
new($"{baseFilename}_drive.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"drive"),
|
||||
new($"{baseFilename}_img.cue", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"img_cue"),
|
||||
new($"{baseFilename}_mainError.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"main_error"),
|
||||
new($"{baseFilename}_mainInfo.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"main_info"),
|
||||
new([$"{baseFilename}_sub.txt", $"{baseFilename}_subReadable.txt"], OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"sub_readable"),
|
||||
new($"{baseFilename}_subError.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"sub_error"),
|
||||
new($"{baseFilename}_subInfo.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"sub_info"),
|
||||
new($"{baseFilename}_subIntention.txt", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"sub_intention"),
|
||||
new($"{baseFilename}_suppl.dat", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"suppl_dat"),
|
||||
new($"{baseFilename}_volDesc.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"vol_desc"),
|
||||
|
||||
new([$"{baseFilename} (Track 0).sub", $"{baseFilename} (Track 00).sub"], OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"track00_sub"),
|
||||
new([$"{baseFilename} (Track 1)(-LBA).sub", $"{baseFilename} (Track 01)(-LBA).sub"], OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"track1_lba_sub"),
|
||||
new($"{baseFilename} (Track AA).sub", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"track_aa_sub"),
|
||||
];
|
||||
|
||||
// TODO: Confirm GD-ROM HD area outputs
|
||||
case MediaType.GDROM:
|
||||
if (File.Exists($"{basePath}.img"))
|
||||
deleteableFiles.Add($"{basePath}.img");
|
||||
if (File.Exists($"{basePath} (Track 0).img"))
|
||||
deleteableFiles.Add($"{basePath} (Track 0).img");
|
||||
if (File.Exists($"{basePath} (Track 00).img"))
|
||||
deleteableFiles.Add($"{basePath} (Track 00).img");
|
||||
if (File.Exists($"{basePath} (Track 1)(-LBA).img"))
|
||||
deleteableFiles.Add($"{basePath} (Track 1)(-LBA).img");
|
||||
if (File.Exists($"{basePath} (Track 01)(-LBA).img"))
|
||||
deleteableFiles.Add($"{basePath} (Track 01)(-LBA).img");
|
||||
if (File.Exists($"{basePath} (Track AA).img"))
|
||||
deleteableFiles.Add($"{basePath} (Track AA).img");
|
||||
return [
|
||||
new($"{baseFilename}.dat", OutputFileFlags.Required
|
||||
| OutputFileFlags.Zippable),
|
||||
new($"{baseFilename}.toc", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"toc"),
|
||||
|
||||
if (File.Exists($"{basePath}.scm"))
|
||||
deleteableFiles.Add($"{basePath}.scm");
|
||||
if (File.Exists($"{basePath} (Track 0).scm"))
|
||||
deleteableFiles.Add($"{basePath} (Track 0).scm");
|
||||
if (File.Exists($"{basePath} (Track 00).scm"))
|
||||
deleteableFiles.Add($"{basePath} (Track 00).scm");
|
||||
if (File.Exists($"{basePath} (Track 1)(-LBA).scm"))
|
||||
deleteableFiles.Add($"{basePath} (Track 1)(-LBA).scm");
|
||||
if (File.Exists($"{basePath} (Track 01)(-LBA).scm"))
|
||||
deleteableFiles.Add($"{basePath} (Track 01)(-LBA).scm");
|
||||
if (File.Exists($"{basePath} (Track AA).scm"))
|
||||
deleteableFiles.Add($"{basePath} (Track AA).scm");
|
||||
|
||||
break;
|
||||
new RegexOutputFile(Regex.Escape(baseFilename) + @"_(\d{8})T\d{6}\.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"cmd"),
|
||||
new($"{baseFilename}_cmd.txt", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"cmd"),
|
||||
new($"{baseFilename}_disc.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"disc"),
|
||||
new($"{baseFilename}_drive.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"drive"),
|
||||
new($"{baseFilename}_mainError.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"main_error"),
|
||||
new($"{baseFilename}_mainInfo.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"main_info"),
|
||||
new($"{baseFilename}_suppl.dat", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"suppl_dat"),
|
||||
new($"{baseFilename}_volDesc.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"vol_desc"),
|
||||
];
|
||||
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (File.Exists($"{basePath}.raw"))
|
||||
deleteableFiles.Add($"{basePath}.raw");
|
||||
return [
|
||||
new($"{baseFilename}.dat", OutputFileFlags.Required
|
||||
| OutputFileFlags.Zippable),
|
||||
new($"{baseFilename}.raw", OutputFileFlags.None),
|
||||
new($"{baseFilename}.toc", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"toc"),
|
||||
|
||||
break;
|
||||
}
|
||||
new RegexOutputFile(Regex.Escape(baseFilename) + @"_(\d{8})T\d{6}\.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"cmd"),
|
||||
new($"{baseFilename}_cmd.txt", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"cmd"),
|
||||
new($"{baseFilename}_CSSKey.txt", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"css_key"),
|
||||
new($"{baseFilename}_disc.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"disc"),
|
||||
new($"{baseFilename}_drive.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"drive"),
|
||||
new($"{baseFilename}_mainError.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"main_error"),
|
||||
new($"{baseFilename}_mainInfo.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"main_info"),
|
||||
new($"{baseFilename}_suppl.dat", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"suppl_dat"),
|
||||
new($"{baseFilename}_volDesc.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"vol_desc"),
|
||||
|
||||
return deleteableFiles;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
{
|
||||
(var cmdPath, _) = GetCommandFilePathAndVersion(basePath);
|
||||
|
||||
var logFiles = new List<string>();
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
case MediaType.GDROM:
|
||||
if (File.Exists($"{basePath}.c2"))
|
||||
logFiles.Add($"{basePath}.c2");
|
||||
if (File.Exists($"{basePath}_c2Error.txt"))
|
||||
logFiles.Add($"{basePath}_c2Error.txt");
|
||||
if (File.Exists($"{basePath}.ccd"))
|
||||
logFiles.Add($"{basePath}.ccd");
|
||||
if (cmdPath != null && File.Exists(cmdPath))
|
||||
logFiles.Add(cmdPath);
|
||||
if (File.Exists($"{basePath}_cmd.txt"))
|
||||
logFiles.Add($"{basePath}_cmd.txt");
|
||||
if (File.Exists($"{basePath}.dat"))
|
||||
logFiles.Add($"{basePath}.dat");
|
||||
if (File.Exists($"{basePath}.sub"))
|
||||
logFiles.Add($"{basePath}.sub");
|
||||
if (File.Exists($"{basePath} (Track 0).sub"))
|
||||
logFiles.Add($"{basePath} (Track 0).sub");
|
||||
if (File.Exists($"{basePath} (Track 00).sub"))
|
||||
logFiles.Add($"{basePath} (Track 00).sub");
|
||||
if (File.Exists($"{basePath} (Track 1)(-LBA).sub"))
|
||||
logFiles.Add($"{basePath} (Track 1)(-LBA).sub");
|
||||
if (File.Exists($"{basePath} (Track 01)(-LBA).sub"))
|
||||
logFiles.Add($"{basePath} (Track 01)(-LBA).sub");
|
||||
if (File.Exists($"{basePath} (Track AA).sub"))
|
||||
logFiles.Add($"{basePath} (Track AA).sub");
|
||||
if (File.Exists($"{basePath}.subtmp"))
|
||||
logFiles.Add($"{basePath}.subtmp");
|
||||
if (File.Exists($"{basePath}.toc"))
|
||||
logFiles.Add($"{basePath}.toc");
|
||||
if (File.Exists($"{basePath}_disc.txt"))
|
||||
logFiles.Add($"{basePath}_disc.txt");
|
||||
if (File.Exists($"{basePath}_drive.txt"))
|
||||
logFiles.Add($"{basePath}_drive.txt");
|
||||
if (File.Exists($"{basePath}_img.cue"))
|
||||
logFiles.Add($"{basePath}_img.cue");
|
||||
if (File.Exists($"{basePath}.img_EdcEcc.txt"))
|
||||
logFiles.Add($"{basePath}.img_EdcEcc.txt");
|
||||
if (File.Exists($"{basePath}.img_EccEdc.txt"))
|
||||
logFiles.Add($"{basePath}.img_EccEdc.txt");
|
||||
if (File.Exists($"{basePath}_mainError.txt"))
|
||||
logFiles.Add($"{basePath}_mainError.txt");
|
||||
if (File.Exists($"{basePath}_mainInfo.txt"))
|
||||
logFiles.Add($"{basePath}_mainInfo.txt");
|
||||
if (File.Exists($"{basePath}_sub.txt"))
|
||||
logFiles.Add($"{basePath}_sub.txt");
|
||||
if (File.Exists($"{basePath}_subError.txt"))
|
||||
logFiles.Add($"{basePath}_subError.txt");
|
||||
if (File.Exists($"{basePath}_subInfo.txt"))
|
||||
logFiles.Add($"{basePath}_subInfo.txt");
|
||||
if (File.Exists($"{basePath}_subIntention.txt"))
|
||||
logFiles.Add($"{basePath}_subIntention.txt");
|
||||
if (File.Exists($"{basePath}_subReadable.txt"))
|
||||
logFiles.Add($"{basePath}_subReadable.txt");
|
||||
if (File.Exists($"{basePath}_suppl.dat"))
|
||||
logFiles.Add($"{basePath}_suppl.dat");
|
||||
if (File.Exists($"{basePath}_volDesc.txt"))
|
||||
logFiles.Add($"{basePath}_volDesc.txt");
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.DVD:
|
||||
case MediaType.HDDVD:
|
||||
case MediaType.BluRay:
|
||||
case MediaType.NintendoGameCubeGameDisc:
|
||||
case MediaType.NintendoWiiOpticalDisc:
|
||||
if (cmdPath != null && File.Exists(cmdPath))
|
||||
logFiles.Add(cmdPath);
|
||||
if (File.Exists($"{basePath}_cmd.txt"))
|
||||
logFiles.Add($"{basePath}_cmd.txt");
|
||||
if (File.Exists($"{basePath}_CSSKey.txt"))
|
||||
logFiles.Add($"{basePath}_CSSKey.txt");
|
||||
if (File.Exists($"{basePath}.dat"))
|
||||
logFiles.Add($"{basePath}.dat");
|
||||
if (File.Exists($"{basePath}.toc"))
|
||||
logFiles.Add($"{basePath}.toc");
|
||||
if (File.Exists($"{basePath}_disc.txt"))
|
||||
logFiles.Add($"{basePath}_disc.txt");
|
||||
if (File.Exists($"{basePath}_drive.txt"))
|
||||
logFiles.Add($"{basePath}_drive.txt");
|
||||
if (File.Exists($"{basePath}_mainError.txt"))
|
||||
logFiles.Add($"{basePath}_mainError.txt");
|
||||
if (File.Exists($"{basePath}_mainInfo.txt"))
|
||||
logFiles.Add($"{basePath}_mainInfo.txt");
|
||||
if (File.Exists($"{basePath}_suppl.dat"))
|
||||
logFiles.Add($"{basePath}_suppl.dat");
|
||||
if (File.Exists($"{basePath}_volDesc.txt"))
|
||||
logFiles.Add($"{basePath}_volDesc.txt");
|
||||
|
||||
if (File.Exists($"{basePath}_DMI.bin"))
|
||||
logFiles.Add($"{basePath}_DMI.bin");
|
||||
if (File.Exists($"{basePath}_PFI.bin"))
|
||||
logFiles.Add($"{basePath}_PFI.bin");
|
||||
if (File.Exists($"{basePath}_PIC.bin"))
|
||||
logFiles.Add($"{basePath}_PIC.bin");
|
||||
if (File.Exists($"{basePath}_SS.bin"))
|
||||
logFiles.Add($"{basePath}_SS.bin");
|
||||
|
||||
break;
|
||||
// TODO: Figure out when these are required
|
||||
new($"{baseFilename}_DMI.bin", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"dmi"),
|
||||
new($"{baseFilename}_PFI.bin", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"pfi"),
|
||||
new($"{baseFilename}_PIC.bin", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"pic"),
|
||||
new($"{baseFilename}_SS.bin", System.IsXGD()
|
||||
? OutputFileFlags.Required | OutputFileFlags.Binary | OutputFileFlags.Zippable
|
||||
: OutputFileFlags.Binary | OutputFileFlags.Zippable,
|
||||
"ss"),
|
||||
];
|
||||
|
||||
case MediaType.FloppyDisk:
|
||||
case MediaType.HardDisk:
|
||||
// TODO: Determine what outputs come out from a HDD, SD, etc.
|
||||
if (cmdPath != null && File.Exists(cmdPath))
|
||||
logFiles.Add(cmdPath);
|
||||
if (File.Exists($"{basePath}_cmd.txt"))
|
||||
logFiles.Add($"{basePath}_cmd.txt");
|
||||
if (File.Exists($"{basePath}.dat"))
|
||||
logFiles.Add($"{basePath}.dat");
|
||||
if (File.Exists($"{basePath}_disc.txt"))
|
||||
logFiles.Add($"{basePath}_disc.txt");
|
||||
return [
|
||||
new($"{baseFilename}.dat", OutputFileFlags.Required
|
||||
| OutputFileFlags.Zippable),
|
||||
|
||||
break;
|
||||
new RegexOutputFile(Regex.Escape(baseFilename) + @"_(\d{8})T\d{6}\.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"cmd"),
|
||||
new($"{baseFilename}_cmd.txt", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"cmd"),
|
||||
new($"{baseFilename}_disc.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"disc"),
|
||||
];
|
||||
}
|
||||
|
||||
return logFiles;
|
||||
return [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -890,8 +721,8 @@ namespace MPF.Processors
|
||||
return (null, null);
|
||||
|
||||
// Generate the matching regex based on the base path
|
||||
string basePathFileName = Path.GetFileName(basePath);
|
||||
var cmdFilenameRegex = new Regex(Regex.Escape(basePathFileName) + @"_(\d{8})T\d{6}\.txt");
|
||||
string baseFilename = Path.GetFileName(basePath);
|
||||
var cmdFilenameRegex = new Regex(Regex.Escape(baseFilename) + @"_(\d{8})T\d{6}\.txt");
|
||||
|
||||
// Find the first match for the command file
|
||||
var parentDirectory = Path.GetDirectoryName(basePath);
|
||||
|
||||
@@ -1,49 +1,67 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- 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.2.0</VersionPrefix>
|
||||
<PropertyGroup>
|
||||
<!-- Assembly Properties -->
|
||||
<TargetFrameworks>net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<VersionPrefix>3.2.2</VersionPrefix>
|
||||
<WarningsNotAsErrors>NU5104</WarningsNotAsErrors>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<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>
|
||||
<!-- Package Properties -->
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<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>
|
||||
|
||||
<!-- 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(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<InternalsVisibleTo Include="MPF.Test" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="psxt001z.Library" Version="0.21.0-rc1" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.2.0" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.4.8" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.6.5" />
|
||||
</ItemGroup>
|
||||
<!-- Support All Frameworks -->
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net2`)) OR $(TargetFramework.StartsWith(`net3`)) OR $(TargetFramework.StartsWith(`net4`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`netcoreapp`)) OR $(TargetFramework.StartsWith(`net5`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(TargetFramework.StartsWith(`net6`)) OR $(TargetFramework.StartsWith(`net7`)) OR $(TargetFramework.StartsWith(`net8`))">
|
||||
<RuntimeIdentifiers>win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="$(RuntimeIdentifier.StartsWith(`osx-arm`))">
|
||||
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
<!-- 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(`net2`)) AND !$(TargetFramework.StartsWith(`net3`)) AND !$(TargetFramework.StartsWith(`net40`)) AND !$(TargetFramework.StartsWith(`net452`))">
|
||||
<PackageReference Include="System.IO.Compression.ZipFile" Version="4.3.0" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="psxt001z.Library" Version="0.21.0-rc1" />
|
||||
<PackageReference Include="SabreTools.Hashing" Version="1.2.0" />
|
||||
<PackageReference Include="SabreTools.Models" Version="1.4.8" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.1" />
|
||||
<PackageReference Include="SabreTools.Serialization" Version="1.6.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
252
MPF.Processors/OutputFile.cs
Normal file
252
MPF.Processors/OutputFile.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
namespace MPF.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents attributes about an <see cref="OutputFile"/>
|
||||
/// </summary>
|
||||
[Flags]
|
||||
internal enum OutputFileFlags : ushort
|
||||
{
|
||||
/// <summary>
|
||||
/// Default state
|
||||
/// </summary>
|
||||
None = 0x0000,
|
||||
|
||||
/// <summary>
|
||||
/// File is required to exist
|
||||
/// </summary>
|
||||
Required = 0x0001,
|
||||
|
||||
/// <summary>
|
||||
/// File is included as an artifact
|
||||
/// </summary>
|
||||
Artifact = 0x0002,
|
||||
|
||||
/// <summary>
|
||||
/// File is included as a binary artifact
|
||||
/// </summary>
|
||||
Binary = 0x0004,
|
||||
|
||||
/// <summary>
|
||||
/// File can be deleted after processing
|
||||
/// </summary>
|
||||
Deleteable = 0x0008,
|
||||
|
||||
/// <summary>
|
||||
/// File can be zipped after processing
|
||||
/// </summary>
|
||||
Zippable = 0x0010,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a single output file
|
||||
/// </summary>
|
||||
internal class OutputFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Set of all filename variants
|
||||
/// </summary>
|
||||
public string[] Filenames { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Key used when creating an artifact
|
||||
/// </summary>
|
||||
public string? ArtifactKey { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the file is required
|
||||
/// </summary>
|
||||
public bool IsRequired
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
return (_flags & OutputFileFlags.Required) != 0;
|
||||
#else
|
||||
return _flags.HasFlag(OutputFileFlags.Required);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the file is an artifact
|
||||
/// </summary>
|
||||
public bool IsArtifact
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
return (_flags & OutputFileFlags.Artifact) != 0
|
||||
|| (_flags & OutputFileFlags.Binary) != 0;
|
||||
#else
|
||||
return _flags.HasFlag(OutputFileFlags.Artifact)
|
||||
|| _flags.HasFlag(OutputFileFlags.Binary);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the file is a binary artifact
|
||||
/// </summary>
|
||||
public bool IsBinaryArtifact
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
return (_flags & OutputFileFlags.Binary) != 0;
|
||||
#else
|
||||
return _flags.HasFlag(OutputFileFlags.Binary);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the file is deleteable after processing
|
||||
/// </summary>
|
||||
public bool IsDeleteable
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
return (_flags & OutputFileFlags.Deleteable) != 0;
|
||||
#else
|
||||
return _flags.HasFlag(OutputFileFlags.Deleteable);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the file is zippable after processing
|
||||
/// </summary>
|
||||
public bool IsZippable
|
||||
{
|
||||
get
|
||||
{
|
||||
#if NET20 || NET35
|
||||
return (_flags & OutputFileFlags.Zippable) != 0;
|
||||
#else
|
||||
return _flags.HasFlag(OutputFileFlags.Zippable);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents attributes about the current file
|
||||
/// </summary>
|
||||
protected readonly OutputFileFlags _flags;
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with a single filename
|
||||
/// </summary>
|
||||
public OutputFile(string filename, OutputFileFlags flags)
|
||||
: this([filename], flags)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with a single filename
|
||||
/// </summary>
|
||||
public OutputFile(string filename, OutputFileFlags flags, string artifactKey)
|
||||
: this([filename], flags, artifactKey)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with set of filenames
|
||||
/// </summary>
|
||||
public OutputFile(string[] filenames, OutputFileFlags flags)
|
||||
{
|
||||
Filenames = filenames;
|
||||
ArtifactKey = null;
|
||||
_flags = flags;
|
||||
|
||||
// Validate the inputs
|
||||
if (filenames.Length == 0)
|
||||
throw new ArgumentException($"{nameof(filenames)} must contain at least one value");
|
||||
if (IsArtifact && string.IsNullOrEmpty(ArtifactKey))
|
||||
throw new ArgumentException($"{nameof(flags)} should not contain the Artifact or Binary flag");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with set of filenames
|
||||
/// </summary>
|
||||
public OutputFile(string[] filenames, OutputFileFlags flags, string artifactKey)
|
||||
{
|
||||
Filenames = filenames;
|
||||
ArtifactKey = artifactKey;
|
||||
_flags = flags;
|
||||
|
||||
// Validate the inputs
|
||||
if (filenames.Length == 0)
|
||||
throw new ArgumentException($"{nameof(filenames)} must contain at least one value");
|
||||
if (IsArtifact && string.IsNullOrEmpty(ArtifactKey))
|
||||
throw new ArgumentException($"{nameof(flags)} should not contain the Artifact or Binary flag");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if an output file exists in a base directory
|
||||
/// </summary>
|
||||
/// <param name="baseDirectory">Base directory to check in</param>
|
||||
public virtual bool Exists(string baseDirectory)
|
||||
{
|
||||
// If the base directory is invalid
|
||||
if (string.IsNullOrEmpty(baseDirectory))
|
||||
return false;
|
||||
if (!Directory.Exists(baseDirectory))
|
||||
return false;
|
||||
|
||||
foreach (string filename in Filenames)
|
||||
{
|
||||
// Check for invalid filenames
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
string possiblePath = Path.Combine(baseDirectory, filename);
|
||||
if (File.Exists(possiblePath))
|
||||
return true;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
/// <summary>
|
||||
/// Indicates if an output file exists in an archive
|
||||
/// </summary>
|
||||
/// <param name="archive">Zip archive to check in</param>
|
||||
public virtual bool Exists(ZipArchive? archive)
|
||||
{
|
||||
// If the archive is invalid
|
||||
if (archive == null)
|
||||
return false;
|
||||
|
||||
foreach (string filename in Filenames)
|
||||
{
|
||||
// Check for invalid filenames
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
// Check all entries on filename alone
|
||||
if (archive.Entries.Any(e => e.Name == filename))
|
||||
return true;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -18,40 +18,6 @@ namespace MPF.Processors
|
||||
|
||||
#region BaseProcessor Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
var missingFiles = new List<string>();
|
||||
|
||||
if (Type != MediaType.BluRay || 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 GenerateArtifacts(SubmissionInfo info, string basePath)
|
||||
{
|
||||
info.Artifacts ??= [];
|
||||
|
||||
string? getKeyBasePath = GetCFWBasePath(basePath);
|
||||
|
||||
if (File.Exists(getKeyBasePath + ".disc.pic"))
|
||||
info.Artifacts["discpic"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(getKeyBasePath + ".disc.pic", binary: true)) ?? string.Empty;
|
||||
if (File.Exists(getKeyBasePath + ".getkey.log"))
|
||||
info.Artifacts["getkeylog"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(getKeyBasePath + ".getkey.log")) ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
|
||||
{
|
||||
@@ -110,26 +76,25 @@ namespace MPF.Processors
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
internal override List<OutputFile> GetOutputFiles(string baseFilename)
|
||||
{
|
||||
var logFiles = new List<string>();
|
||||
string? getKeyBasePath = GetCFWBasePath(basePath);
|
||||
|
||||
if (System != RedumpSystem.SonyPlayStation3)
|
||||
return logFiles;
|
||||
|
||||
switch (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 [
|
||||
new($"{baseFilename}.iso", OutputFileFlags.Required),
|
||||
new($"{baseFilename}.getkey.log", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"getkey_log"),
|
||||
new($"{baseFilename}.disc.pic", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"disc_pic"),
|
||||
];
|
||||
}
|
||||
|
||||
return logFiles;
|
||||
return [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -352,6 +352,91 @@ namespace MPF.Processors
|
||||
|
||||
#region Region Extraction
|
||||
|
||||
/// <summary>
|
||||
/// Determine the region based on the PlayStation serial code
|
||||
/// </summary>
|
||||
/// <param name="serial">PlayStation serial code</param>
|
||||
/// <returns>Region mapped from name, if possible</returns>
|
||||
public static Region? GetPlayStationRegion(string? serial)
|
||||
{
|
||||
// If we have a fully invalid serial
|
||||
if (string.IsNullOrEmpty(serial))
|
||||
return null;
|
||||
|
||||
// Standardized "S" serials
|
||||
if (serial!.StartsWith("S"))
|
||||
{
|
||||
// string publisher = serial[0] + serial[1];
|
||||
// char secondRegion = serial[3];
|
||||
switch (serial[2])
|
||||
{
|
||||
case 'A': return Region.Asia;
|
||||
case 'C': return Region.China;
|
||||
case 'E': return Region.Europe;
|
||||
case 'K': return Region.SouthKorea;
|
||||
case 'U': return Region.UnitedStatesOfAmerica;
|
||||
case 'P':
|
||||
// Region of S_P_ serials may be Japan, Asia, or SouthKorea
|
||||
return serial[3] switch
|
||||
{
|
||||
// Check first two digits of S_PS serial
|
||||
'S' => (Region?)(serial.Substring(5, 2) switch
|
||||
{
|
||||
"46" => Region.SouthKorea,
|
||||
"51" => Region.Asia,
|
||||
"56" => Region.SouthKorea,
|
||||
"55" => Region.Asia,
|
||||
_ => Region.Japan,
|
||||
}),
|
||||
|
||||
// Check first three digits of S_PM serial
|
||||
'M' => (Region?)(serial.Substring(5, 3) switch
|
||||
{
|
||||
"645" => Region.SouthKorea,
|
||||
"675" => Region.SouthKorea,
|
||||
"885" => Region.SouthKorea,
|
||||
_ => Region.Japan, // Remaining S_PM serials may be Japan or Asia
|
||||
}),
|
||||
_ => (Region?)Region.Japan,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Japan-only special serial
|
||||
else if (serial.StartsWith("PAPX"))
|
||||
return Region.Japan;
|
||||
|
||||
// Region appears entirely random
|
||||
else if (serial.StartsWith("PABX"))
|
||||
return null;
|
||||
|
||||
// Region appears entirely random
|
||||
else if (serial.StartsWith("PBPX"))
|
||||
return null;
|
||||
|
||||
// Japan-only special serial
|
||||
else if (serial.StartsWith("PCBX"))
|
||||
return Region.Japan;
|
||||
|
||||
// Japan-only special serial
|
||||
else if (serial.StartsWith("PCXC"))
|
||||
return Region.Japan;
|
||||
|
||||
// Single disc known, Japan
|
||||
else if (serial.StartsWith("PDBX"))
|
||||
return Region.Japan;
|
||||
|
||||
// Single disc known, Europe
|
||||
else if (serial.StartsWith("PEBX"))
|
||||
return Region.Europe;
|
||||
|
||||
// Single disc known, USA
|
||||
else if (serial.StartsWith("PUBX"))
|
||||
return Region.UnitedStatesOfAmerica;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determine the region based on the XGD serial character
|
||||
/// </summary>
|
||||
@@ -731,7 +816,7 @@ namespace MPF.Processors
|
||||
/// </summary>
|
||||
/// <param name="dmi">DMI.bin file location</param>
|
||||
/// <returns>String representation of the XGD1 DMI information, empty string on error</returns>
|
||||
public static string GetXGD1XMID(string dmi)
|
||||
public static string GetXMID(string dmi)
|
||||
{
|
||||
if (!File.Exists(dmi))
|
||||
return string.Empty;
|
||||
@@ -753,7 +838,7 @@ namespace MPF.Processors
|
||||
/// </summary>
|
||||
/// <param name="dmi">DMI.bin file location</param>
|
||||
/// <returns>String representation of the XGD2/3 DMI information, empty string on error</returns>
|
||||
public static string GetXGD23XeMID(string dmi)
|
||||
public static string GetXeMID(string dmi)
|
||||
{
|
||||
if (!File.Exists(dmi))
|
||||
return string.Empty;
|
||||
@@ -988,7 +1073,7 @@ namespace MPF.Processors
|
||||
|
||||
case 3:
|
||||
// Determine if XGD3 SS.bin is SSv1 (Kreon) or SSv2 (0800)
|
||||
bool ssv2 = ss.Skip(32).Take(72).All(x => x == 0);
|
||||
bool ssv2 = ss.Skip(32).Take(72).Any(x => x != 0);
|
||||
|
||||
if (ssv2)
|
||||
{
|
||||
@@ -1089,12 +1174,18 @@ namespace MPF.Processors
|
||||
startLBA[i] = (1913776 + 0x030000) * 2 - (startPSN ^ 0xFFFFFF) - 0x030000 - 1;
|
||||
endLBA[i] = (1913776 + 0x030000) * 2 - (endPSN ^ 0xFFFFFF) - 0x030000 - 1;
|
||||
}
|
||||
else if (xgdType > 1 && startPSN >= (1913760 + 0x030000))
|
||||
else if (xgdType == 2 && startPSN >= (1913760 + 0x030000))
|
||||
{
|
||||
// Layer 1 of XGD2 or XGD3
|
||||
// Layer 1 of XGD2
|
||||
startLBA[i] = (1913760 + 0x030000) * 2 - (startPSN ^ 0xFFFFFF) - 0x030000 - 1;
|
||||
endLBA[i] = (1913760 + 0x030000) * 2 - (endPSN ^ 0xFFFFFF) - 0x030000 - 1;
|
||||
}
|
||||
else if (xgdType == 3 && startPSN >= (2133520 + 0x030000))
|
||||
{
|
||||
// Layer 1 of XGD3
|
||||
startLBA[i] = (2133520 + 0x030000) * 2 - (startPSN ^ 0xFFFFFF) - 0x030000 - 1;
|
||||
endLBA[i] = (2133520 + 0x030000) * 2 - (endPSN ^ 0xFFFFFF) - 0x030000 - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Layer 0
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Models.CueSheets;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
@@ -19,172 +20,6 @@ namespace MPF.Processors
|
||||
|
||||
#region BaseProcessor Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
var missingFiles = new List<string>();
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
if (!File.Exists($"{basePath}.cue"))
|
||||
missingFiles.Add($"{basePath}.cue");
|
||||
if (!File.Exists($"{basePath}.scram") && !File.Exists($"{basePath}.scrap"))
|
||||
missingFiles.Add($"{basePath}.scram");
|
||||
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}.fulltoc"))
|
||||
missingFiles.Add($"{basePath}.fulltoc");
|
||||
if (!File.Exists($"{basePath}.log"))
|
||||
missingFiles.Add($"{basePath}.log");
|
||||
else if (GetDatfile($"{basePath}.log") == null)
|
||||
missingFiles.Add($"{basePath}.log (dat section)");
|
||||
if (!File.Exists($"{basePath}.state"))
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
if (!File.Exists($"{basePath}.subcode"))
|
||||
missingFiles.Add($"{basePath}.subcode");
|
||||
if (!File.Exists($"{basePath}.toc"))
|
||||
missingFiles.Add($"{basePath}.toc");
|
||||
}
|
||||
|
||||
// Removed or inconsistent files
|
||||
//{
|
||||
// // Depends on the disc
|
||||
// if (!File.Exists($"{basePath}.cdtext"))
|
||||
// missingFiles.Add($"{basePath}.cdtext");
|
||||
//
|
||||
// // Not available in all versions
|
||||
// if (!File.Exists($"{basePath}.asus"))
|
||||
// missingFiles.Add($"{basePath}.asus");
|
||||
// if (!File.Exists($"{basePath}.atip"))
|
||||
// missingFiles.Add($"{basePath}.atip");
|
||||
// if (!File.Exists($"{basePath}.hash"))
|
||||
// missingFiles.Add($"{basePath}.hash");
|
||||
// // Also: "{basePath} (Track X).hash" (get from cuesheet)
|
||||
// if (!File.Exists($"{basePath}.pma"))
|
||||
// missingFiles.Add($"{basePath}.pma");
|
||||
// if (!File.Exists($"{basePath}.skeleton"))
|
||||
// missingFiles.Add($"{basePath}.skeleton");
|
||||
// // Also: "{basePath} (Track X).skeleton" (get from cuesheet)
|
||||
//}
|
||||
|
||||
break;
|
||||
|
||||
case MediaType.DVD:
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}.log"))
|
||||
missingFiles.Add($"{basePath}.log");
|
||||
else if (GetDatfile($"{basePath}.log") == null)
|
||||
missingFiles.Add($"{basePath}.dat");
|
||||
if (!File.Exists($"{basePath}.manufacturer") && !File.Exists($"{basePath}.1.manufacturer") && !File.Exists($"{basePath}.2.manufacturer"))
|
||||
missingFiles.Add($"{basePath}.manufacturer");
|
||||
if (!File.Exists($"{basePath}.physical") && !File.Exists($"{basePath}.0.physical") && !File.Exists($"{basePath}.1.physical") && !File.Exists($"{basePath}.2.physical"))
|
||||
missingFiles.Add($"{basePath}.physical");
|
||||
if (!File.Exists($"{basePath}.state"))
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
}
|
||||
|
||||
// Removed or inconsistent files
|
||||
//{
|
||||
// // Not available in all versions
|
||||
// if (!File.Exists($"{basePath}.asus"))
|
||||
// missingFiles.Add($"{basePath}.asus");
|
||||
// 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
|
||||
case MediaType.BluRay:
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}.log"))
|
||||
missingFiles.Add($"{basePath}.log");
|
||||
else if (GetDatfile($"{basePath}.log") == null)
|
||||
missingFiles.Add($"{basePath}.dat");
|
||||
if (!File.Exists($"{basePath}.physical") && !File.Exists($"{basePath}.0.physical") && !File.Exists($"{basePath}.1.physical") && !File.Exists($"{basePath}.2.physical"))
|
||||
missingFiles.Add($"{basePath}.physical");
|
||||
if (!File.Exists($"{basePath}.state"))
|
||||
missingFiles.Add($"{basePath}.state");
|
||||
}
|
||||
|
||||
// Removed or inconsistent files
|
||||
//{
|
||||
// // Not available in all versions
|
||||
// if (!File.Exists($"{basePath}.asus"))
|
||||
// missingFiles.Add($"{basePath}.asus");
|
||||
// if (!File.Exists($"{basePath}.hash"))
|
||||
// missingFiles.Add($"{basePath}.hash");
|
||||
// if (!File.Exists($"{basePath}.skeleton"))
|
||||
// missingFiles.Add($"{basePath}.skeleton");
|
||||
//}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
missingFiles.Add("Media and system combination not supported for Redumper");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateArtifacts(SubmissionInfo info, string basePath)
|
||||
{
|
||||
info.Artifacts ??= [];
|
||||
|
||||
if (File.Exists($"{basePath}.asus"))
|
||||
info.Artifacts["asus"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.asus")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.atip"))
|
||||
info.Artifacts["atip"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.atip")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.cdtext"))
|
||||
info.Artifacts["cdtext"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.cdtext")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.cue"))
|
||||
info.Artifacts["cue"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.cue")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.fulltoc"))
|
||||
info.Artifacts["fulltoc"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.fulltoc")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
info.Artifacts["hash"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.hash")) ?? string.Empty;
|
||||
// TODO: "{basePath} (Track X).hash" (get from cuesheet)
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
info.Artifacts["log"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.log")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.manufacturer"))
|
||||
info.Artifacts["manufacturer"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.manufacturer")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.1.manufacturer"))
|
||||
info.Artifacts["manufacturer1"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.1.manufacturer")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.2.manufacturer"))
|
||||
info.Artifacts["manufacturer2"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.2.manufacturer")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.physical"))
|
||||
info.Artifacts["physical"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.physical")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.0.physical"))
|
||||
info.Artifacts["physical0"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.0.physical")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.1.physical"))
|
||||
info.Artifacts["physical1"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.1.physical")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.2.physical"))
|
||||
info.Artifacts["physical2"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.2.physical")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.pma"))
|
||||
info.Artifacts["pma"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.pma")) ?? string.Empty;
|
||||
// if (File.Exists($"{basePath}.skeleton"))
|
||||
// info.Artifacts["skeleton"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.skeleton")) ?? string.Empty;
|
||||
// // Also: "{basePath} (Track X).skeleton" (get from cuesheet)
|
||||
// if (File.Exists($"{basePath}.scram"))
|
||||
// info.Artifacts["scram"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.scram")) ?? string.Empty;
|
||||
// if (File.Exists($"{basePath}.scrap"))
|
||||
// info.Artifacts["scrap"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.scrap")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.state"))
|
||||
info.Artifacts["state"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.state")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.subcode"))
|
||||
info.Artifacts["subcode"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.subcode")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.toc"))
|
||||
info.Artifacts["toc"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.toc")) ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
|
||||
{
|
||||
@@ -338,15 +173,60 @@ namespace MPF.Processors
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXbox:
|
||||
// TODO: Support DMI and additional file information when generated
|
||||
string xmidString = ProcessingTool.GetXMID($"{basePath}.manufacturer");
|
||||
var xmid = SabreTools.Serialization.Wrappers.XMID.Create(xmidString);
|
||||
if (xmid != null)
|
||||
{
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XMID] = xmidString?.TrimEnd('\0') ?? string.Empty;
|
||||
info.CommonDiscInfo.Serial = xmid.Serial ?? string.Empty;
|
||||
if (!redumpCompat)
|
||||
info.VersionAndEditions!.Version = xmid.Version ?? string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Region = ProcessingTool.GetXGDRegion(xmid.Model.RegionIdentifier);
|
||||
}
|
||||
|
||||
if (HashTool.GetStandardHashes($"{basePath}.manufacturer", out _, out string? dmi1Crc, out _, out _))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = dmi1Crc ?? string.Empty;
|
||||
if (HashTool.GetStandardHashes($"{basePath}.physical", out _, out string? pfi1Crc, out _, out _))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.PFIHash] = pfi1Crc ?? string.Empty;
|
||||
|
||||
// TODO: Support SS information when generated
|
||||
break;
|
||||
|
||||
case RedumpSystem.MicrosoftXbox360:
|
||||
// TODO: Support DMI and additional file information when generated
|
||||
string xemidString = ProcessingTool.GetXeMID($"{basePath}.manufacturer");
|
||||
var xemid = SabreTools.Serialization.Wrappers.XeMID.Create(xemidString);
|
||||
if (xemid != null)
|
||||
{
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.XeMID] = xemidString?.TrimEnd('\0') ?? string.Empty;
|
||||
info.CommonDiscInfo.Serial = xemid.Serial ?? string.Empty;
|
||||
if (!redumpCompat)
|
||||
info.VersionAndEditions!.Version = xemid.Version ?? string.Empty;
|
||||
|
||||
info.CommonDiscInfo.Region = ProcessingTool.GetXGDRegion(xemid.Model.RegionIdentifier);
|
||||
}
|
||||
|
||||
if (HashTool.GetStandardHashes($"{basePath}.manufacturer", out _, out string? dmi23Crc, out _, out _))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = dmi23Crc ?? string.Empty;
|
||||
if (HashTool.GetStandardHashes($"{basePath}.physical", out _, out string? pfi23Crc, out _, out _))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.PFIHash] = pfi23Crc ?? string.Empty;
|
||||
|
||||
// TODO: Support SS information when generated
|
||||
break;
|
||||
|
||||
case RedumpSystem.NamcoSegaNintendoTriforce:
|
||||
// TODO: Support header information and GD-ROM info when generated
|
||||
if (Type == MediaType.CDROM)
|
||||
{
|
||||
info.Extras!.Header = GetGDROMHeader($"{basePath}.log",
|
||||
out string? buildDate,
|
||||
out string? serial,
|
||||
out _,
|
||||
out string? version) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = serial ?? string.Empty;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? string.Empty;
|
||||
// TODO: Support region setting from parsed value
|
||||
info.VersionAndEditions!.Version = version ?? string.Empty;
|
||||
}
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaMegaCDSegaCD:
|
||||
@@ -357,36 +237,75 @@ namespace MPF.Processors
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaChihiro:
|
||||
// TODO: Support header information and GD-ROM info when generated
|
||||
if (Type == MediaType.CDROM)
|
||||
{
|
||||
info.Extras!.Header = GetGDROMHeader($"{basePath}.log",
|
||||
out string? buildDate,
|
||||
out string? serial,
|
||||
out _,
|
||||
out string? version) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = serial ?? string.Empty;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? string.Empty;
|
||||
// TODO: Support region setting from parsed value
|
||||
info.VersionAndEditions!.Version = version ?? string.Empty;
|
||||
}
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaDreamcast:
|
||||
// TODO: Support header information and GD-ROM info when generated
|
||||
if (Type == MediaType.CDROM)
|
||||
{
|
||||
info.Extras!.Header = GetGDROMHeader($"{basePath}.log",
|
||||
out string? buildDate,
|
||||
out string? serial,
|
||||
out _,
|
||||
out string? version) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = serial ?? string.Empty;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? string.Empty;
|
||||
// TODO: Support region setting from parsed value
|
||||
info.VersionAndEditions!.Version = version ?? string.Empty;
|
||||
}
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaNaomi:
|
||||
// TODO: Support header information and GD-ROM info when generated
|
||||
if (Type == MediaType.CDROM)
|
||||
{
|
||||
info.Extras!.Header = GetGDROMHeader($"{basePath}.log",
|
||||
out string? buildDate,
|
||||
out string? serial,
|
||||
out _,
|
||||
out string? version) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = serial ?? string.Empty;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? string.Empty;
|
||||
// TODO: Support region setting from parsed value
|
||||
info.VersionAndEditions!.Version = version ?? string.Empty;
|
||||
}
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaNaomi2:
|
||||
// TODO: Support header information and GD-ROM info when generated
|
||||
if (Type == MediaType.CDROM)
|
||||
{
|
||||
info.Extras!.Header = GetGDROMHeader($"{basePath}.log",
|
||||
out string? buildDate,
|
||||
out string? serial,
|
||||
out _,
|
||||
out string? version) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = serial ?? string.Empty;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? string.Empty;
|
||||
// TODO: Support region setting from parsed value
|
||||
info.VersionAndEditions!.Version = version ?? string.Empty;
|
||||
}
|
||||
break;
|
||||
|
||||
case RedumpSystem.SegaSaturn:
|
||||
info.Extras!.Header = GetSaturnHeader($"{basePath}.log") ?? string.Empty;
|
||||
|
||||
// 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).ToArray());
|
||||
|
||||
if (GetSaturnBuildInfo(info.Extras.Header, out var saturnSerial, out var saturnVersion, out var buildDate))
|
||||
{
|
||||
// Ensure internal serial is pulled from local data
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = saturnSerial ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = saturnVersion ?? string.Empty;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = buildDate ?? string.Empty;
|
||||
}
|
||||
|
||||
info.Extras!.Header = GetSaturnHeader($"{basePath}.log",
|
||||
out string? saturnBuildDate,
|
||||
out string? saturnSerial,
|
||||
out _,
|
||||
out string? saturnVersion) ?? string.Empty;
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.InternalSerialName] = saturnSerial ?? string.Empty;
|
||||
info.CommonDiscInfo.EXEDateBuildDate = saturnBuildDate ?? string.Empty;
|
||||
// TODO: Support region setting from parsed value
|
||||
info.VersionAndEditions!.Version = saturnVersion ?? string.Empty;
|
||||
break;
|
||||
|
||||
case RedumpSystem.SonyPlayStation:
|
||||
@@ -442,128 +361,187 @@ namespace MPF.Processors
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetDeleteableFilePaths(string basePath)
|
||||
internal override List<OutputFile> GetOutputFiles(string baseFilename)
|
||||
{
|
||||
var deleteableFiles = new List<string>();
|
||||
|
||||
if (File.Exists($"{basePath}.scram"))
|
||||
deleteableFiles.Add($"{basePath}.scram");
|
||||
if (File.Exists($"{basePath}.scrap"))
|
||||
deleteableFiles.Add($"{basePath}.scrap");
|
||||
|
||||
return deleteableFiles;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
{
|
||||
var logFiles = new List<string>();
|
||||
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.CDROM:
|
||||
if (File.Exists($"{basePath}.asus"))
|
||||
logFiles.Add($"{basePath}.asus");
|
||||
if (File.Exists($"{basePath}.atip"))
|
||||
logFiles.Add($"{basePath}.atip");
|
||||
if (File.Exists($"{basePath}.cdtext"))
|
||||
logFiles.Add($"{basePath}.cdtext");
|
||||
if (File.Exists($"{basePath}.fulltoc"))
|
||||
logFiles.Add($"{basePath}.fulltoc");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
if (File.Exists($"{basePath}.pma"))
|
||||
logFiles.Add($"{basePath}.pma");
|
||||
if (File.Exists($"{basePath}.state"))
|
||||
logFiles.Add($"{basePath}.state");
|
||||
if (File.Exists($"{basePath}.subcode"))
|
||||
logFiles.Add($"{basePath}.subcode");
|
||||
if (File.Exists($"{basePath}.toc"))
|
||||
logFiles.Add($"{basePath}.toc");
|
||||
List<OutputFile> cdrom = [
|
||||
new($"{baseFilename}.asus", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"asus"),
|
||||
new($"{baseFilename}.atip", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"atip"),
|
||||
new($"{baseFilename}.cdtext", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"cdtext"),
|
||||
new($"{baseFilename}.cue", OutputFileFlags.Required),
|
||||
new($"{baseFilename}.fulltoc", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"fulltoc"),
|
||||
new($"{baseFilename}.log", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"log"),
|
||||
new CustomOutputFile($"{baseFilename}.log", OutputFileFlags.Required,
|
||||
DatfileExists),
|
||||
new($"{baseFilename}.pma", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"pma"),
|
||||
new([$"{baseFilename}.scram", $"{baseFilename}.scrap"], OutputFileFlags.Required
|
||||
| OutputFileFlags.Deleteable),
|
||||
new($"{baseFilename}.state", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"state"),
|
||||
new($"{baseFilename}.subcode", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"subcode"),
|
||||
new($"{baseFilename}.toc", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"toc"),
|
||||
];
|
||||
|
||||
// Include .hash and .skeleton for all files in cuesheet
|
||||
var cueSheet = SabreTools.Serialization.Deserializers.CueSheet.DeserializeFile($"{basePath}.cue");
|
||||
string? baseDir = Path.GetDirectoryName(basePath);
|
||||
if (cueSheet?.Files != null && baseDir != null)
|
||||
var cueSheet = SabreTools.Serialization.Deserializers.CueSheet.DeserializeFile($"{baseFilename}.cue");
|
||||
if (cueSheet?.Files != null)
|
||||
{
|
||||
int trackId = 1;
|
||||
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");
|
||||
cdrom.Add(new($"{trackName}.hash", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
$"hash_{trackId}"));
|
||||
cdrom.Add(new($"{trackName}.skeleton", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
$"skeleton_{trackId}"));
|
||||
trackId++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
logFiles.Add($"{basePath}.hash");
|
||||
if (File.Exists($"{basePath}.skeleton"))
|
||||
logFiles.Add($"{basePath}.skeleton");
|
||||
cdrom.Add(new($"{baseFilename}.hash", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"hash"));
|
||||
cdrom.Add(new($"{baseFilename}.skeleton", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"skeleton"));
|
||||
}
|
||||
|
||||
break;
|
||||
return cdrom;
|
||||
|
||||
case MediaType.DVD:
|
||||
if (File.Exists($"{basePath}.asus"))
|
||||
logFiles.Add($"{basePath}.asus");
|
||||
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}.0.physical"))
|
||||
logFiles.Add($"{basePath}.0.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;
|
||||
return [
|
||||
new($"{baseFilename}.asus", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"asus"),
|
||||
new($"{baseFilename}.hash", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"hash"),
|
||||
new($"{baseFilename}.log", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"log"),
|
||||
new CustomOutputFile($"{baseFilename}.log", OutputFileFlags.Required,
|
||||
DatfileExists),
|
||||
new([$"{baseFilename}.manufacturer", $"{baseFilename}.0.manufacturer"], OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"manufacturer_0"),
|
||||
new($"{baseFilename}.1.manufacturer", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"manufacturer_1"),
|
||||
new([$"{baseFilename}.physical", $"{baseFilename}.0.physical"], OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"physical_0"),
|
||||
new($"{baseFilename}.1.physical", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"physical_1"),
|
||||
new($"{baseFilename}.security", System.IsXGD()
|
||||
? OutputFileFlags.Required | OutputFileFlags.Binary | OutputFileFlags.Zippable
|
||||
: OutputFileFlags.Binary | OutputFileFlags.Zippable,
|
||||
"security"),
|
||||
new($"{baseFilename}.skeleton", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"skeleton"),
|
||||
new($"{baseFilename}.ss", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"ss"),
|
||||
new($"{baseFilename}.ssv1", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"ssv1"),
|
||||
new($"{baseFilename}.ssv2", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"ssv2"),
|
||||
new($"{baseFilename}.state", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"state"),
|
||||
];
|
||||
|
||||
case MediaType.HDDVD: // TODO: Confirm that this information outputs
|
||||
case MediaType.BluRay:
|
||||
if (File.Exists($"{basePath}.asus"))
|
||||
logFiles.Add($"{basePath}.asus");
|
||||
if (File.Exists($"{basePath}.hash"))
|
||||
logFiles.Add($"{basePath}.hash");
|
||||
if (File.Exists($"{basePath}.log"))
|
||||
logFiles.Add($"{basePath}.log");
|
||||
if (File.Exists($"{basePath}.physical"))
|
||||
logFiles.Add($"{basePath}.physical");
|
||||
if (File.Exists($"{basePath}.0.physical"))
|
||||
logFiles.Add($"{basePath}.0.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;
|
||||
return [
|
||||
new($"{baseFilename}.asus", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"asus"),
|
||||
new($"{baseFilename}.hash", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"hash"),
|
||||
new($"{baseFilename}.log", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"log"),
|
||||
new CustomOutputFile($"{baseFilename}.log", OutputFileFlags.Required,
|
||||
DatfileExists),
|
||||
new([$"{baseFilename}.physical", $"{baseFilename}.0.physical"], OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"physical_0"),
|
||||
new($"{baseFilename}.1.physical", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"physical_1"),
|
||||
new($"{baseFilename}.2.physical", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"physical_2"),
|
||||
new($"{baseFilename}.3.physical", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"physical_3"),
|
||||
new($"{baseFilename}.skeleton", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"skeleton"),
|
||||
new($"{baseFilename}.state", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"state"),
|
||||
];
|
||||
}
|
||||
|
||||
return logFiles;
|
||||
return [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Extra Methods
|
||||
|
||||
/// <summary>
|
||||
/// Get if the datfile exists in the log
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
private static bool DatfileExists(string log)
|
||||
=> GetDatfile(log) != null;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Information Extraction Methods
|
||||
|
||||
/// <summary>
|
||||
@@ -797,7 +775,7 @@ namespace MPF.Processors
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>True if error counts could be retrieved, false otherwise</returns>
|
||||
public static bool GetErrorCount(string log, out long redumpErrors, out long c2Errors)
|
||||
private static bool GetErrorCount(string log, out long redumpErrors, out long c2Errors)
|
||||
{
|
||||
// Set the default values for error counts
|
||||
redumpErrors = -1; c2Errors = -1;
|
||||
@@ -844,6 +822,79 @@ namespace MPF.Processors
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the header from a GD-ROM LD area, if possible
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>Header as a string if possible, null on error</returns>
|
||||
private static string? GetGDROMHeader(string log, out string? buildDate, out string? serial, out string? region, out string? version)
|
||||
{
|
||||
// Set the default values
|
||||
buildDate = null; serial = null; region = null; version = null;
|
||||
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(log))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// Fast forward to the MCD line
|
||||
using var sr = File.OpenText(log);
|
||||
while (!sr.EndOfStream && sr.ReadLine()?.TrimStart()?.StartsWith("DC [") == false) ;
|
||||
if (sr.EndOfStream)
|
||||
return null;
|
||||
|
||||
string? line, headerString = string.Empty;
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
line = sr.ReadLine()?.TrimStart();
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
if (line.StartsWith("build date:"))
|
||||
{
|
||||
buildDate = line.Substring("build date: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("serial:"))
|
||||
{
|
||||
serial = line.Substring("serial: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("region:"))
|
||||
{
|
||||
region = line.Substring("region: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("regions:"))
|
||||
{
|
||||
region = line.Substring("regions: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("version:"))
|
||||
{
|
||||
version = line.Substring("version: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("header:"))
|
||||
{
|
||||
line = sr.ReadLine()?.TrimStart();
|
||||
while (line?.StartsWith("00") == true)
|
||||
{
|
||||
headerString += line + "\n";
|
||||
line = sr.ReadLine()?.Trim();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return headerString.TrimEnd('\n');
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get hardware information from the input file, if possible
|
||||
/// </summary>
|
||||
@@ -1135,11 +1186,11 @@ namespace MPF.Processors
|
||||
|
||||
if (line.StartsWith("anti-modchip:"))
|
||||
{
|
||||
// Valid but skip
|
||||
// Valid but skip
|
||||
}
|
||||
else if (line.StartsWith("EXE:"))
|
||||
{
|
||||
// Valid but skip
|
||||
// Valid but skip
|
||||
}
|
||||
else if (line.StartsWith("EXE date:"))
|
||||
{
|
||||
@@ -1147,11 +1198,11 @@ namespace MPF.Processors
|
||||
}
|
||||
else if (line.StartsWith("libcrypt:"))
|
||||
{
|
||||
// Valid but skip
|
||||
// Valid but skip
|
||||
}
|
||||
else if (line.StartsWith("region:"))
|
||||
{
|
||||
// Valid but skip
|
||||
// Valid but skip
|
||||
}
|
||||
else if (line.StartsWith("serial:"))
|
||||
{
|
||||
@@ -1324,9 +1375,9 @@ namespace MPF.Processors
|
||||
/// <<param name="segaHeader">String representing a formatter variant of the Saturn header</param>
|
||||
/// <returns>True on successful extraction of info, false otherwise</returns>
|
||||
/// TODO: Remove when Redumper gets native reading support
|
||||
private static bool GetSaturnBuildInfo(string? segaHeader, out string? serial, out string? version, out string? date)
|
||||
private static bool GetSaturnBuildInfo(string? segaHeader, out string? buildDate, out string? serial, out string? version)
|
||||
{
|
||||
serial = null; version = null; date = null;
|
||||
buildDate = null; serial = null; version = null;
|
||||
|
||||
// If the input header is null, we can't do a thing
|
||||
if (string.IsNullOrEmpty(segaHeader))
|
||||
@@ -1340,8 +1391,8 @@ namespace MPF.Processors
|
||||
string dateLine = header[3].Substring(58);
|
||||
serial = serialVersionLine.Substring(0, 10).Trim();
|
||||
version = serialVersionLine.Substring(10, 6).TrimStart('V', 'v');
|
||||
date = dateLine.Substring(0, 8);
|
||||
date = $"{date[0]}{date[1]}{date[2]}{date[3]}-{date[4]}{date[5]}-{date[6]}{date[7]}";
|
||||
buildDate = dateLine.Substring(0, 8);
|
||||
buildDate = $"{buildDate[0]}{buildDate[1]}{buildDate[2]}{buildDate[3]}-{buildDate[4]}{buildDate[5]}-{buildDate[6]}{buildDate[7]}";
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
@@ -1356,8 +1407,11 @@ namespace MPF.Processors
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>Header as a byte array if possible, null on error</returns>
|
||||
private static string? GetSaturnHeader(string log)
|
||||
private static string? GetSaturnHeader(string log, out string? buildDate, out string? serial, out string? region, out string? version)
|
||||
{
|
||||
// Set the default values
|
||||
buildDate = null; serial = null; region = null; version = null;
|
||||
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(log))
|
||||
return null;
|
||||
@@ -1374,7 +1428,30 @@ namespace MPF.Processors
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
line = sr.ReadLine()?.TrimStart();
|
||||
if (line?.StartsWith("header:") == true)
|
||||
if (line == null)
|
||||
break;
|
||||
|
||||
if (line.StartsWith("build date:"))
|
||||
{
|
||||
buildDate = line.Substring("build date: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("serial:"))
|
||||
{
|
||||
serial = line.Substring("serial: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("region:"))
|
||||
{
|
||||
region = line.Substring("region: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("regions:"))
|
||||
{
|
||||
region = line.Substring("regions: ".Length).Trim();
|
||||
}
|
||||
else if (line.StartsWith("version:"))
|
||||
{
|
||||
version = line.Substring("version: ".Length).Trim();
|
||||
}
|
||||
else if (line?.StartsWith("header:") == true)
|
||||
{
|
||||
line = sr.ReadLine()?.TrimStart();
|
||||
while (line?.StartsWith("00") == true)
|
||||
@@ -1389,7 +1466,18 @@ namespace MPF.Processors
|
||||
}
|
||||
}
|
||||
|
||||
return headerString.TrimEnd('\n');
|
||||
// Trim the header
|
||||
headerString = headerString.TrimEnd('\n');
|
||||
|
||||
// Fallback if any info could not be found
|
||||
if (GetSaturnBuildInfo(headerString, out string? buildDateP, out string? serialP, out string? versionP))
|
||||
{
|
||||
buildDate ??= buildDateP;
|
||||
serial ??= serialP;
|
||||
version ??= versionP;
|
||||
}
|
||||
|
||||
return headerString;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -1543,38 +1631,6 @@ namespace MPF.Processors
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the write offset from the input file, if possible
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>Sample write offset if possible, null on error</returns>
|
||||
private static string? GetWriteOffset(string log)
|
||||
{
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(log))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// If we find the disc write offset line, return the offset
|
||||
using var sr = File.OpenText(log);
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string? line = sr.ReadLine()?.TrimStart();
|
||||
if (line?.StartsWith("disc write offset") == true)
|
||||
return line.Substring("disc write offset: ".Length).Trim();
|
||||
}
|
||||
|
||||
// We couldn't detect it then
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the version. if possible
|
||||
/// </summary>
|
||||
@@ -1673,6 +1729,38 @@ namespace MPF.Processors
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the write offset from the input file, if possible
|
||||
/// </summary>
|
||||
/// <param name="log">Log file location</param>
|
||||
/// <returns>Sample write offset if possible, null on error</returns>
|
||||
private static string? GetWriteOffset(string log)
|
||||
{
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(log))
|
||||
return null;
|
||||
|
||||
try
|
||||
{
|
||||
// If we find the disc write offset line, return the offset
|
||||
using var sr = File.OpenText(log);
|
||||
while (!sr.EndOfStream)
|
||||
{
|
||||
string? line = sr.ReadLine()?.TrimStart();
|
||||
if (line?.StartsWith("disc write offset") == true)
|
||||
return line.Substring("disc write offset: ".Length).Trim();
|
||||
}
|
||||
|
||||
// We couldn't detect it then
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// We don't care what the exception is right now
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
90
MPF.Processors/RegexOutputFile.cs
Normal file
90
MPF.Processors/RegexOutputFile.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System.IO;
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
using System.IO.Compression;
|
||||
#endif
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace MPF.Processors
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a single output file with a Regex-matched name
|
||||
/// </summary>
|
||||
internal class RegexOutputFile : OutputFile
|
||||
{
|
||||
/// <summary>
|
||||
/// Create an OutputFile with a single filename
|
||||
/// </summary>
|
||||
public RegexOutputFile(string filename, OutputFileFlags flags)
|
||||
: base([filename], flags)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with a single filename
|
||||
/// </summary>
|
||||
public RegexOutputFile(string filename, OutputFileFlags flags, string artifactKey)
|
||||
: base([filename], flags, artifactKey)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with set of filenames
|
||||
/// </summary>
|
||||
public RegexOutputFile(string[] filenames, OutputFileFlags flags)
|
||||
: base(filenames, flags)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an OutputFile with set of filenames
|
||||
/// </summary>
|
||||
public RegexOutputFile(string[] filenames, OutputFileFlags flags, string artifactKey)
|
||||
: base(filenames, flags, artifactKey)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Exists(string baseDirectory)
|
||||
{
|
||||
// If the base directory is invalid
|
||||
if (string.IsNullOrEmpty(baseDirectory))
|
||||
return false;
|
||||
if (!Directory.Exists(baseDirectory))
|
||||
return false;
|
||||
|
||||
// Get list of all files in directory
|
||||
var directoryFiles = Directory.GetFiles(baseDirectory);
|
||||
foreach (string file in directoryFiles)
|
||||
{
|
||||
if (Filenames.Any(pattern => Regex.IsMatch(file, pattern)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if NET452_OR_GREATER || NETCOREAPP
|
||||
/// <summary>
|
||||
/// Indicates if an output file exists in an archive
|
||||
/// </summary>
|
||||
/// <param name="archive">Zip archive to check in</param>
|
||||
public override bool Exists(ZipArchive? archive)
|
||||
{
|
||||
// If the archive is invalid
|
||||
if (archive == null)
|
||||
return false;
|
||||
|
||||
// Get list of all files in archive
|
||||
var archiveFiles = archive.Entries.Select(e => e.Name).ToList();
|
||||
foreach (string file in archiveFiles)
|
||||
{
|
||||
if (Filenames.Any(pattern => Regex.IsMatch(file, pattern)))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -19,54 +19,6 @@ namespace MPF.Processors
|
||||
|
||||
#region BaseProcessor Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
var missingFiles = new List<string>();
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.UMD:
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
if (!File.Exists($"{basePath}_disc.txt"))
|
||||
missingFiles.Add($"{basePath}_disc.txt");
|
||||
if (!File.Exists($"{basePath}_mainError.txt"))
|
||||
missingFiles.Add($"{basePath}_mainError.txt");
|
||||
if (!File.Exists($"{basePath}_mainInfo.txt"))
|
||||
missingFiles.Add($"{basePath}_mainInfo.txt");
|
||||
if (!File.Exists($"{basePath}_volDesc.txt"))
|
||||
missingFiles.Add($"{basePath}_volDesc.txt");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
missingFiles.Add("Media and system combination not supported for UmdImageCreator");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateArtifacts(SubmissionInfo info, string basePath)
|
||||
{
|
||||
info.Artifacts ??= [];
|
||||
|
||||
if (File.Exists($"{basePath}_disc.txt"))
|
||||
info.Artifacts["disc"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_disc.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_drive.txt"))
|
||||
info.Artifacts["drive"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_drive.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_mainError.txt"))
|
||||
info.Artifacts["mainError"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_mainError.txt")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_mainInfo.txt"))
|
||||
info.Artifacts["mainInfo"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_mainInfo.txt")) ?? string.Empty;
|
||||
//if (File.Exists($"{basePath}_PFI.bin"))
|
||||
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{basePath}_PFI.bin")) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}_volDesc.txt"))
|
||||
info.Artifacts["volDesc"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}_volDesc.txt")) ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
|
||||
{
|
||||
@@ -103,15 +55,22 @@ namespace MPF.Processors
|
||||
info.SizeAndChecksums.SHA1 = sha1;
|
||||
}
|
||||
|
||||
if (GetUMDAuxInfo(basePath + "_disc.txt", out var title, out DiscCategory? umdcat, out var umdversion, out var umdlayer, out long umdsize))
|
||||
if (GetUMDAuxInfo(basePath + "_disc.txt",
|
||||
out var title,
|
||||
out DiscCategory? category,
|
||||
out string? serial,
|
||||
out var version,
|
||||
out var layer,
|
||||
out long size))
|
||||
{
|
||||
info.CommonDiscInfo!.Title = title ?? string.Empty;
|
||||
info.CommonDiscInfo.Category = umdcat ?? DiscCategory.Games;
|
||||
info.VersionAndEditions!.Version = umdversion ?? string.Empty;
|
||||
info.SizeAndChecksums!.Size = umdsize;
|
||||
info.CommonDiscInfo.Category = category ?? DiscCategory.Games;
|
||||
info.CommonDiscInfo.CommentsSpecialFields![SiteCode.InternalSerialName] = serial ?? string.Empty;
|
||||
info.VersionAndEditions!.Version = version ?? string.Empty;
|
||||
info.SizeAndChecksums!.Size = size;
|
||||
|
||||
if (!string.IsNullOrEmpty(umdlayer))
|
||||
info.SizeAndChecksums.Layerbreak = Int64.Parse(umdlayer ?? "-1");
|
||||
if (!string.IsNullOrEmpty(layer))
|
||||
info.SizeAndChecksums.Layerbreak = Int64.Parse(layer ?? "-1");
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -119,30 +78,40 @@ namespace MPF.Processors
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
internal override List<OutputFile> GetOutputFiles(string baseFilename)
|
||||
{
|
||||
var logFiles = new List<string>();
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.UMD:
|
||||
if (File.Exists($"{basePath}_disc.txt"))
|
||||
logFiles.Add($"{basePath}_disc.txt");
|
||||
if (File.Exists($"{basePath}_drive.txt"))
|
||||
logFiles.Add($"{basePath}_drive.txt");
|
||||
if (File.Exists($"{basePath}_mainError.txt"))
|
||||
logFiles.Add($"{basePath}_mainError.txt");
|
||||
if (File.Exists($"{basePath}_mainInfo.txt"))
|
||||
logFiles.Add($"{basePath}_mainInfo.txt");
|
||||
if (File.Exists($"{basePath}_volDesc.txt"))
|
||||
logFiles.Add($"{basePath}_volDesc.txt");
|
||||
return [
|
||||
new($"{baseFilename}.iso", OutputFileFlags.Required),
|
||||
|
||||
if (File.Exists($"{basePath}_PFI.bin"))
|
||||
logFiles.Add($"{basePath}_PFI.bin");
|
||||
|
||||
break;
|
||||
new($"{baseFilename}_disc.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"disc"),
|
||||
new($"{baseFilename}_drive.txt", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"drive"),
|
||||
new($"{baseFilename}_mainError.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"main_error"),
|
||||
new($"{baseFilename}_mainInfo.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"main_info"),
|
||||
new($"{baseFilename}_PFI.bin", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"pfi"),
|
||||
new($"{baseFilename}_volDesc.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"vol_desc"),
|
||||
];
|
||||
}
|
||||
|
||||
return logFiles;
|
||||
return [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -188,9 +157,17 @@ namespace MPF.Processors
|
||||
/// </summary>
|
||||
/// <param name="disc">_disc.txt file location</param>
|
||||
/// <returns>True on successful extraction of info, false otherwise</returns>
|
||||
private static bool GetUMDAuxInfo(string disc, out string? title, out DiscCategory? umdcat, out string? umdversion, out string? umdlayer, out long umdsize)
|
||||
private static bool GetUMDAuxInfo(string disc,
|
||||
out string? title,
|
||||
out DiscCategory? category,
|
||||
out string? serial,
|
||||
out string? version,
|
||||
out string? layer,
|
||||
out long size)
|
||||
{
|
||||
title = null; umdcat = null; umdversion = null; umdlayer = null; umdsize = -1;
|
||||
title = null; serial = null; version = null; layer = null;
|
||||
category = null;
|
||||
size = -1;
|
||||
|
||||
// If the file doesn't exist, we can't get info from it
|
||||
if (!File.Exists(disc))
|
||||
@@ -207,20 +184,26 @@ namespace MPF.Processors
|
||||
break;
|
||||
|
||||
if (line.StartsWith("TITLE") && title == null)
|
||||
title = line.Substring("TITLE: ".Length);
|
||||
else if (line.StartsWith("DISC_VERSION") && umdversion == null)
|
||||
umdversion = line.Split(' ')[1];
|
||||
title = line.Split(' ')[1];
|
||||
else if (line.StartsWith("DISC_ID") && version == null)
|
||||
serial = line.Split(' ')[1];
|
||||
else if (line.StartsWith("DISC_VERSION") && version == null)
|
||||
version = line.Split(' ')[1];
|
||||
else if (line.StartsWith("pspUmdTypes"))
|
||||
umdcat = ProcessingTool.GetUMDCategory(line.Split(' ')[1]);
|
||||
category = ProcessingTool.GetUMDCategory(line.Split(' ')[1]);
|
||||
else if (line.StartsWith("L0 length"))
|
||||
umdlayer = line.Split(' ')[2];
|
||||
layer = line.Split(' ')[2];
|
||||
else if (line.StartsWith("FileSize:"))
|
||||
umdsize = Int64.Parse(line.Split(' ')[1]);
|
||||
size = Int64.Parse(line.Split(' ')[1]);
|
||||
}
|
||||
|
||||
// If we have a serial, format it
|
||||
if (!string.IsNullOrEmpty(serial) && serial!.Length > 4)
|
||||
serial = serial.Substring(0, 4) + "-" + serial.Substring(4);
|
||||
|
||||
// If the L0 length is the size of the full disc, there's no layerbreak
|
||||
if (Int64.TryParse(umdlayer, out long umdlayerValue) && umdlayerValue * 2048 == umdsize)
|
||||
umdlayer = null;
|
||||
if (Int64.TryParse(layer, out long umdlayerValue) && umdlayerValue * 2048 == size)
|
||||
layer = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using SabreTools.Hashing;
|
||||
using SabreTools.Models.Logiqx;
|
||||
using SabreTools.RedumpLib;
|
||||
@@ -18,64 +19,6 @@ namespace MPF.Processors
|
||||
|
||||
#region BaseProcessor Implementations
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override (bool, List<string>) CheckAllOutputFilesExist(string basePath, bool preCheck)
|
||||
{
|
||||
var missingFiles = new List<string>();
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.DVD:
|
||||
if (!File.Exists($"{basePath}_logs.zip") || !preCheck)
|
||||
{
|
||||
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
|
||||
string? logPath = GetLogName(baseDir);
|
||||
if (string.IsNullOrEmpty(logPath))
|
||||
missingFiles.Add($"{baseDir}Log.txt");
|
||||
if (!File.Exists($"{baseDir}DMI.bin"))
|
||||
missingFiles.Add($"{baseDir}DMI.bin");
|
||||
if (!File.Exists($"{baseDir}PFI.bin"))
|
||||
missingFiles.Add($"{baseDir}PFI.bin");
|
||||
if (!File.Exists($"{baseDir}SS.bin"))
|
||||
missingFiles.Add($"{baseDir}SS.bin");
|
||||
|
||||
// Not required from XBC
|
||||
//if (!File.Exists($"{basePath}.dvd"))
|
||||
// missingFiles.Add($"{basePath}.dvd");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
missingFiles.Add("Media and system combination not supported for XboxBackupCreator");
|
||||
break;
|
||||
}
|
||||
|
||||
return (!missingFiles.Any(), missingFiles);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateArtifacts(SubmissionInfo info, string basePath)
|
||||
{
|
||||
info.Artifacts ??= [];
|
||||
|
||||
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
|
||||
string? logPath = GetLogName(baseDir);
|
||||
|
||||
if (File.Exists(logPath))
|
||||
info.Artifacts["log"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile(logPath!)) ?? string.Empty;
|
||||
if (File.Exists($"{basePath}.dvd"))
|
||||
info.Artifacts["dvd"] = ProcessingTool.GetBase64(ProcessingTool.GetFullFile($"{basePath}.dvd")) ?? string.Empty;
|
||||
//if (File.Exists($"{baseDir}DMI.bin"))
|
||||
// info.Artifacts["dmi"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}DMI.bin")) ?? string.Empty;
|
||||
// TODO: Include PFI artifact only if the hash doesn't match known PFI hashes
|
||||
//if (File.Exists($"{baseDir}PFI.bin"))
|
||||
// info.Artifacts["pfi"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}PFI.bin")) ?? string.Empty;
|
||||
//if (File.Exists($"{baseDir}SS.bin"))
|
||||
// info.Artifacts["ss"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}SS.bin")) ?? string.Empty;
|
||||
//if (File.Exists($"{baseDir}RawSS.bin"))
|
||||
// info.Artifacts["rawss"] = Convert.ToBase64String(File.ReadAllBytes($"{baseDir}RawSS.bin")) ?? string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void GenerateSubmissionInfo(SubmissionInfo info, string basePath, bool redumpCompat)
|
||||
{
|
||||
@@ -83,10 +26,10 @@ namespace MPF.Processors
|
||||
info = Builder.EnsureAllSections(info);
|
||||
|
||||
// Get base directory
|
||||
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
|
||||
string baseDirectory = Path.GetDirectoryName(basePath) ?? string.Empty;
|
||||
|
||||
// Get log filename
|
||||
string? logPath = GetLogName(baseDir);
|
||||
string? logPath = GetLogName(baseDirectory);
|
||||
if (string.IsNullOrEmpty(logPath))
|
||||
return;
|
||||
|
||||
@@ -132,7 +75,7 @@ namespace MPF.Processors
|
||||
case RedumpSystem.MicrosoftXbox:
|
||||
|
||||
// Parse DMI.bin
|
||||
string xmidString = ProcessingTool.GetXGD1XMID($"{baseDir}DMI.bin");
|
||||
string xmidString = ProcessingTool.GetXMID(Path.Combine(baseDirectory, "DMI.bin"));
|
||||
var xmid = SabreTools.Serialization.Wrappers.XMID.Create(xmidString);
|
||||
if (xmid != null)
|
||||
{
|
||||
@@ -156,7 +99,7 @@ namespace MPF.Processors
|
||||
//string? mediaID = GetMediaID(logPath);
|
||||
|
||||
// Parse DMI.bin
|
||||
string xemidString = ProcessingTool.GetXGD23XeMID($"{baseDir}DMI.bin");
|
||||
string xemidString = ProcessingTool.GetXeMID(Path.Combine(baseDirectory, "DMI.bin"));
|
||||
var xemid = SabreTools.Serialization.Wrappers.XeMID.Create(xemidString);
|
||||
if (xemid != null)
|
||||
{
|
||||
@@ -171,11 +114,16 @@ namespace MPF.Processors
|
||||
break;
|
||||
}
|
||||
|
||||
// Get the output file paths
|
||||
string dmiPath = Path.Combine(baseDirectory, "DMI.bin");
|
||||
string pfiPath = Path.Combine(baseDirectory, "PFI.bin");
|
||||
string ssPath = Path.Combine(baseDirectory, "SS.bin");
|
||||
|
||||
// Deal with SS.bin
|
||||
if (File.Exists($"{baseDir}SS.bin"))
|
||||
if (File.Exists(ssPath))
|
||||
{
|
||||
// Save security sector ranges
|
||||
string? ranges = ProcessingTool.GetSSRanges($"{baseDir}SS.bin");
|
||||
string? ranges = ProcessingTool.GetSSRanges(ssPath);
|
||||
if (!string.IsNullOrEmpty(ranges))
|
||||
info.Extras!.SecuritySectorRanges = ranges;
|
||||
|
||||
@@ -183,50 +131,59 @@ namespace MPF.Processors
|
||||
//info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSVersion] =
|
||||
|
||||
// Recreate RawSS.bin
|
||||
RecreateSS(logPath!, $"{baseDir}SS.bin", $"{baseDir}RawSS.bin");
|
||||
RecreateSS(logPath!, ssPath, Path.Combine(baseDirectory, "RawSS.bin"));
|
||||
|
||||
// Run ss_sector_range to get repeatable SS hash
|
||||
ProcessingTool.CleanSS($"{baseDir}SS.bin", $"{baseDir}SS.bin");
|
||||
ProcessingTool.CleanSS(ssPath, ssPath);
|
||||
}
|
||||
|
||||
// DMI/PFI/SS CRC32 hashes
|
||||
if (File.Exists($"{baseDir}DMI.bin"))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = HashTool.GetFileHash($"{baseDir}DMI.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
|
||||
if (File.Exists($"{baseDir}PFI.bin"))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.PFIHash] = HashTool.GetFileHash($"{baseDir}PFI.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
|
||||
if (File.Exists($"{baseDir}SS.bin"))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSHash] = HashTool.GetFileHash($"{baseDir}SS.bin", HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
|
||||
if (File.Exists(dmiPath))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.DMIHash] = HashTool.GetFileHash(dmiPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
|
||||
if (File.Exists(pfiPath))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.PFIHash] = HashTool.GetFileHash(pfiPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
|
||||
if (File.Exists(ssPath))
|
||||
info.CommonDiscInfo!.CommentsSpecialFields![SiteCode.SSHash] = HashTool.GetFileHash(ssPath, HashType.CRC32)?.ToUpperInvariant() ?? string.Empty;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override List<string> GetLogFilePaths(string basePath)
|
||||
internal override List<OutputFile> GetOutputFiles(string baseFilename)
|
||||
{
|
||||
var logFiles = new List<string>();
|
||||
string baseDir = Path.GetDirectoryName(basePath) + Path.DirectorySeparatorChar;
|
||||
switch (Type)
|
||||
{
|
||||
case MediaType.DVD:
|
||||
string? logPath = GetLogName(baseDir);
|
||||
if (!string.IsNullOrEmpty(logPath))
|
||||
logFiles.Add(logPath!);
|
||||
if (File.Exists($"{basePath}.dvd"))
|
||||
logFiles.Add($"{basePath}.dvd");
|
||||
if (File.Exists($"{baseDir}DMI.bin"))
|
||||
logFiles.Add($"{baseDir}DMI.bin");
|
||||
if (File.Exists($"{baseDir}PFI.bin"))
|
||||
logFiles.Add($"{baseDir}PFI.bin");
|
||||
if (File.Exists($"{baseDir}SS.bin"))
|
||||
logFiles.Add($"{baseDir}SS.bin");
|
||||
if (File.Exists($"{baseDir}RawSS.bin"))
|
||||
logFiles.Add($"{baseDir}RawSS.bin");
|
||||
|
||||
break;
|
||||
return [
|
||||
new($"{baseFilename}.dvd", OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"dvd"),
|
||||
new($"{baseFilename}.iso", OutputFileFlags.Required),
|
||||
|
||||
new("DMI.bin", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"dmi"),
|
||||
new RegexOutputFile("[lL]og.txt", OutputFileFlags.Required
|
||||
| OutputFileFlags.Artifact
|
||||
| OutputFileFlags.Zippable,
|
||||
"log"),
|
||||
new("PFI.bin", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"pfi"),
|
||||
new("RawSS.bin", OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"raw_ss"),
|
||||
new("SS.bin", OutputFileFlags.Required
|
||||
| OutputFileFlags.Binary
|
||||
| OutputFileFlags.Zippable,
|
||||
"ss"),
|
||||
];
|
||||
}
|
||||
|
||||
return logFiles;
|
||||
return [];
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -240,8 +197,8 @@ namespace MPF.Processors
|
||||
/// <returns>Log path if found, null otherwise</returns>
|
||||
private static string? GetLogName(string baseDir)
|
||||
{
|
||||
if (IsSuccessfulLog($"{baseDir}Log.txt"))
|
||||
return $"{baseDir}Log.txt";
|
||||
if (IsSuccessfulLog(Path.Combine(baseDir, "Log.txt")))
|
||||
return Path.Combine(baseDir, "Log.txt");
|
||||
|
||||
// Search for a renamed log file (assume there is only one)
|
||||
string[] files = Directory.GetFiles(baseDir, "*.txt", SearchOption.TopDirectoryOnly);
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace MPF.Test.Frontend.Tools
|
||||
[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 = FrontendTool.NormalizeOutputPaths(outputPath, true);
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
|
||||
<CheckEolTargetFramework>false</CheckEolTargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.ExecutionContexts\MPF.ExecutionContexts.csproj" />
|
||||
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.ExecutionContexts\MPF.ExecutionContexts.csproj" />
|
||||
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.10.0-release-24177-07" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-release-24177-07" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
|
||||
<PackageReference Include="xunit" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="1.13.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.core" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.8.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.CodeCoverage" Version="17.10.0-release-24177-07" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-release-24177-07" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.1" />
|
||||
<PackageReference Include="xunit" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||
<PackageReference Include="xunit.analyzers" Version="1.13.0" />
|
||||
<PackageReference Include="xunit.assert" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.core" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.extensibility.core" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.extensibility.execution" Version="2.8.0" />
|
||||
<PackageReference Include="xunit.runner.console" Version="2.8.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -1,201 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.RedumpLib
|
||||
{
|
||||
public class EnumExtensionsTests
|
||||
{
|
||||
/// <summary>
|
||||
/// MediaType values that support drive speeds
|
||||
/// </summary>
|
||||
private static readonly MediaType?[] _supportDriveSpeeds =
|
||||
[
|
||||
MediaType.CDROM,
|
||||
MediaType.DVD,
|
||||
MediaType.GDROM,
|
||||
MediaType.HDDVD,
|
||||
MediaType.BluRay,
|
||||
MediaType.NintendoGameCubeGameDisc,
|
||||
MediaType.NintendoWiiOpticalDisc,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// RedumpSystem values that are considered Audio
|
||||
/// </summary>
|
||||
private static readonly RedumpSystem?[] _audioSystems =
|
||||
[
|
||||
RedumpSystem.AtariJaguarCDInteractiveMultimediaSystem,
|
||||
RedumpSystem.AudioCD,
|
||||
RedumpSystem.DVDAudio,
|
||||
RedumpSystem.HasbroiONEducationalGamingSystem,
|
||||
RedumpSystem.HasbroVideoNow,
|
||||
RedumpSystem.HasbroVideoNowColor,
|
||||
RedumpSystem.HasbroVideoNowJr,
|
||||
RedumpSystem.HasbroVideoNowXP,
|
||||
RedumpSystem.PlayStationGameSharkUpdates,
|
||||
RedumpSystem.PhilipsCDi,
|
||||
RedumpSystem.SuperAudioCD,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// RedumpSystem values that are considered markers
|
||||
/// </summary>
|
||||
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 =
|
||||
[
|
||||
RedumpSystem.SonyPlayStation2,
|
||||
RedumpSystem.SonyPlayStation3,
|
||||
RedumpSystem.SonyPlayStation4,
|
||||
RedumpSystem.SonyPlayStation5,
|
||||
RedumpSystem.SonyPlayStationPortable,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// RedumpSystem values that are considered XGD
|
||||
/// </summary>
|
||||
private static readonly RedumpSystem?[] _xgdSystems =
|
||||
[
|
||||
RedumpSystem.MicrosoftXbox,
|
||||
RedumpSystem.MicrosoftXbox360,
|
||||
RedumpSystem.MicrosoftXboxOne,
|
||||
RedumpSystem.MicrosoftXboxSeriesXS,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Check that all systems with reversed ringcodes are marked properly
|
||||
/// </summary>
|
||||
/// <param name="redumpSystem">RedumpSystem value to check</param>
|
||||
/// <param name="expected">The expected value to come from the check</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateReversedRingcodeSystemsTestData))]
|
||||
public void HasReversedRingcodesTest(RedumpSystem? redumpSystem, bool expected)
|
||||
{
|
||||
bool actual = redumpSystem.HasReversedRingcodes();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that all audio systems are marked properly
|
||||
/// </summary>
|
||||
/// <param name="redumpSystem">RedumpSystem value to check</param>
|
||||
/// <param name="expected">The expected value to come from the check</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateAudioSystemsTestData))]
|
||||
public void IsAudioTest(RedumpSystem? redumpSystem, bool expected)
|
||||
{
|
||||
bool actual = redumpSystem.IsAudio();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that all marker systems are marked properly
|
||||
/// </summary>
|
||||
/// <param name="redumpSystem">RedumpSystem value to check</param>
|
||||
/// <param name="expected">The expected value to come from the check</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateMarkerSystemsTestData))]
|
||||
public void IsMarkerTest(RedumpSystem? redumpSystem, bool expected)
|
||||
{
|
||||
bool actual = redumpSystem.IsMarker();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that all XGD systems are marked properly
|
||||
/// </summary>
|
||||
/// <param name="redumpSystem">RedumpSystem value to check</param>
|
||||
/// <param name="expected">The expected value to come from the check</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateXGDSystemsTestData))]
|
||||
public void IsXGDTest(RedumpSystem? redumpSystem, bool expected)
|
||||
{
|
||||
bool actual = redumpSystem.IsXGD();
|
||||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of RedumpSystem values that are considered Audio
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object?[]> GenerateAudioSystemsTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_audioSystems.Contains(redumpSystem))
|
||||
testData.Add([redumpSystem, true]);
|
||||
else
|
||||
testData.Add([redumpSystem, false]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of RedumpSystem values that are considered markers
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object?[]> GenerateMarkerSystemsTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_markerSystems.Contains(redumpSystem))
|
||||
testData.Add([redumpSystem, true]);
|
||||
else
|
||||
testData.Add([redumpSystem, false]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of RedumpSystem values that are considered markers
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object?[]> GenerateReversedRingcodeSystemsTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_reverseRingcodeSystems.Contains(redumpSystem))
|
||||
testData.Add([redumpSystem, true]);
|
||||
else
|
||||
testData.Add([redumpSystem, false]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of RedumpSystem values that are considered XGD
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object?[]> GenerateXGDSystemsTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (RedumpSystem redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
if (_xgdSystems.Contains(redumpSystem))
|
||||
testData.Add([redumpSystem, true]);
|
||||
else
|
||||
testData.Add([redumpSystem, false]);
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,717 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.RedumpLib
|
||||
{
|
||||
// TODO: Add tests for string-to-enum conversion
|
||||
public class ExtensionsTests
|
||||
{
|
||||
#region Cross-Enumeration
|
||||
|
||||
/// <summary>
|
||||
/// DiscType values that map to MediaType
|
||||
/// </summary>
|
||||
private static readonly DiscType?[] _mappableDiscTypes = new DiscType?[]
|
||||
{
|
||||
DiscType.BD25,
|
||||
DiscType.BD33,
|
||||
DiscType.BD50,
|
||||
DiscType.BD66,
|
||||
DiscType.BD100,
|
||||
DiscType.BD128,
|
||||
DiscType.CD,
|
||||
DiscType.DVD5,
|
||||
DiscType.DVD9,
|
||||
DiscType.GDROM,
|
||||
DiscType.HDDVDSL,
|
||||
DiscType.HDDVDDL,
|
||||
DiscType.NintendoGameCubeGameDisc,
|
||||
DiscType.NintendoWiiOpticalDiscSL,
|
||||
DiscType.NintendoWiiOpticalDiscDL,
|
||||
DiscType.NintendoWiiUOpticalDiscSL,
|
||||
DiscType.UMDSL,
|
||||
DiscType.UMDDL,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// MediaType values that map to DiscType
|
||||
/// </summary>
|
||||
private static readonly MediaType?[] _mappableMediaTypes = new MediaType?[]
|
||||
{
|
||||
MediaType.BluRay,
|
||||
MediaType.CDROM,
|
||||
MediaType.DVD,
|
||||
MediaType.GDROM,
|
||||
MediaType.HDDVD,
|
||||
MediaType.NintendoGameCubeGameDisc,
|
||||
MediaType.NintendoWiiOpticalDisc,
|
||||
MediaType.NintendoWiiUOpticalDisc,
|
||||
MediaType.UMD,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Check that every supported system has some set of MediaTypes supported
|
||||
/// </summary>
|
||||
/// <param name="redumpSystem">RedumpSystem value to check</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateRedumpSystemMappingTestData))]
|
||||
public void MediaTypesTest(RedumpSystem? redumpSystem)
|
||||
{
|
||||
var actual = redumpSystem.MediaTypes();
|
||||
Assert.NotEmpty(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that both mappable and unmappable media types output correctly
|
||||
/// </summary>
|
||||
/// <param name="mediaType">MediaType value to check</param>
|
||||
/// <param name="expectNull">True to expect a null mapping, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateMediaTypeMappingTestData))]
|
||||
public void ToDiscTypeTest(MediaType? mediaType, bool expectNull)
|
||||
{
|
||||
DiscType? actual = mediaType.ToDiscType();
|
||||
Assert.Equal(expectNull, actual == null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that DiscType values all map to something appropriate
|
||||
/// </summary>
|
||||
/// <param name="discType">DiscType value to check</param>
|
||||
/// <param name="expectNull">True to expect a null mapping, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateDiscTypeMappingTestData))]
|
||||
public void ToMediaTypeTest(DiscType? discType, bool expectNull)
|
||||
{
|
||||
MediaType? actual = discType.ToMediaType();
|
||||
Assert.Equal(expectNull, actual == null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of DiscType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DiscType values</returns>
|
||||
public static List<object?[]> GenerateDiscTypeMappingTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (DiscType? discType in Enum.GetValues(typeof(DiscType)))
|
||||
{
|
||||
if (_mappableDiscTypes.Contains(discType))
|
||||
testData.Add(new object?[] { discType, false });
|
||||
else
|
||||
testData.Add(new object?[] { discType, true });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of RedumpSystem values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object?[]> GenerateRedumpSystemMappingTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null } };
|
||||
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
testData.Add(new object?[] { redumpSystem });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of mappable media types
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of MediaTypes</returns>
|
||||
public static List<object?[]> GenerateMediaTypeMappingTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
|
||||
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
|
||||
{
|
||||
if (_mappableMediaTypes.Contains(mediaType))
|
||||
testData.Add(new object?[] { mediaType, false });
|
||||
else
|
||||
testData.Add(new object?[] { mediaType, true });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Disc Category
|
||||
|
||||
/// <summary>
|
||||
/// Check that every DiscCategory has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="discCategory">DiscCategory value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateDiscCategoryTestData))]
|
||||
public void DiscCategoryLongNameTest(DiscCategory? discCategory, bool expectNull)
|
||||
{
|
||||
var actual = discCategory.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of DiscCategory values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DiscCategory values</returns>
|
||||
public static List<object?[]> GenerateDiscCategoryTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (DiscCategory? discCategory in Enum.GetValues(typeof(DiscCategory)))
|
||||
{
|
||||
testData.Add(new object?[] { discCategory, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Disc Type
|
||||
|
||||
/// <summary>
|
||||
/// Check that every DiscType has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="discType">DiscType value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateDiscTypeTestData))]
|
||||
public void DiscTypeLongNameTest(DiscType? discType, bool expectNull)
|
||||
{
|
||||
var actual = discType.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of DiscType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of DiscType values</returns>
|
||||
public static List<object?[]> GenerateDiscTypeTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (DiscType? discType in Enum.GetValues(typeof(DiscType)))
|
||||
{
|
||||
if (discType == DiscType.NONE)
|
||||
testData.Add(new object?[] { discType, true });
|
||||
else
|
||||
testData.Add(new object?[] { discType, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Language
|
||||
|
||||
/// <summary>
|
||||
/// Check that every Language has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="language">Language value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateLanguageTestData))]
|
||||
public void LanguageLongNameTest(Language? language, bool expectNull)
|
||||
{
|
||||
var actual = language.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that every Language has a short name provided
|
||||
/// </summary>
|
||||
/// <param name="language">Language value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateLanguageTestData))]
|
||||
public void LanguageShortNameTest(Language? language, bool expectNull)
|
||||
{
|
||||
var actual = language.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that every Language that has an ISO 639-1 code is unique
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LanguageNoDuplicateTwoLetterCodeTest()
|
||||
{
|
||||
var fullLanguages = Enum.GetValues(typeof(Language)).Cast<Language?>().ToList();
|
||||
var filteredLanguages = new Dictionary<string, Language?>();
|
||||
|
||||
int totalCount = 0;
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
var code = language.TwoLetterCode();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
if (filteredLanguages.ContainsKey(code))
|
||||
throw new DuplicateNameException($"Code {code} already in dictionary");
|
||||
|
||||
filteredLanguages[code] = language;
|
||||
totalCount++;
|
||||
}
|
||||
|
||||
Assert.Equal(totalCount, filteredLanguages.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that every Language that has a standard/bibliographic ISO 639-2 code is unique
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LanguageNoDuplicateThreeLetterCodeTest()
|
||||
{
|
||||
var fullLanguages = Enum.GetValues(typeof(Language)).Cast<Language?>().ToList();
|
||||
var filteredLanguages = new Dictionary<string, Language?>();
|
||||
|
||||
int totalCount = 0;
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
var code = language.ThreeLetterCode();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
if (filteredLanguages.ContainsKey(code))
|
||||
throw new DuplicateNameException($"Code {code} already in dictionary");
|
||||
|
||||
filteredLanguages[code] = language;
|
||||
totalCount++;
|
||||
}
|
||||
|
||||
Assert.Equal(totalCount, filteredLanguages.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that every Language that has a terminology ISO 639-2 code is unique
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void LanguageNoDuplicateThreeLetterCodeAltTest()
|
||||
{
|
||||
var fullLanguages = Enum.GetValues(typeof(Language)).Cast<Language?>().ToList();
|
||||
var filteredLanguages = new Dictionary<string, Language?>();
|
||||
|
||||
int totalCount = 0;
|
||||
foreach (Language? language in fullLanguages)
|
||||
{
|
||||
var code = language.ThreeLetterCodeAlt();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
if (filteredLanguages.ContainsKey(code))
|
||||
throw new DuplicateNameException($"Code {code} already in dictionary");
|
||||
|
||||
filteredLanguages[code] = language;
|
||||
totalCount++;
|
||||
}
|
||||
|
||||
Assert.Equal(totalCount, filteredLanguages.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of Language values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of Language values</returns>
|
||||
public static List<object?[]> GenerateLanguageTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (Language? language in Enum.GetValues(typeof(Language)))
|
||||
{
|
||||
testData.Add(new object?[] { language, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Language Selection
|
||||
|
||||
/// <summary>
|
||||
/// Check that every LanguageSelection has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="languageSelection">LanguageSelection value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateLanguageSelectionTestData))]
|
||||
public void LanguageSelectionLongNameTest(LanguageSelection? languageSelection, bool expectNull)
|
||||
{
|
||||
var actual = languageSelection.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of LanguageSelection values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of LanguageSelection values</returns>
|
||||
public static List<object?[]> GenerateLanguageSelectionTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (LanguageSelection? languageSelection in Enum.GetValues(typeof(LanguageSelection)))
|
||||
{
|
||||
testData.Add(new object?[] { languageSelection, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Media Type
|
||||
|
||||
/// <summary>
|
||||
/// Check that every MediaType has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="mediaType">MediaType value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateMediaTypeTestData))]
|
||||
public void MediaTypeLongNameTest(MediaType? mediaType, bool expectNull)
|
||||
{
|
||||
var actual = mediaType.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that every MediaType has a short name provided
|
||||
/// </summary>
|
||||
/// <param name="mediaType">MediaType value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateMediaTypeTestData))]
|
||||
public void MediaTypeShortNameTest(MediaType? mediaType, bool expectNull)
|
||||
{
|
||||
var actual = mediaType.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of MediaType values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of MediaType values</returns>
|
||||
public static List<object?[]> GenerateMediaTypeTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (MediaType? mediaType in Enum.GetValues(typeof(MediaType)))
|
||||
{
|
||||
testData.Add(new object?[] { mediaType, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Region
|
||||
|
||||
/// <summary>
|
||||
/// Check that every Region has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="region">Region value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateRegionTestData))]
|
||||
public void RegionLongNameTest(Region? region, bool expectNull)
|
||||
{
|
||||
var actual = region.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that every Region has a short name provided
|
||||
/// </summary>
|
||||
/// <param name="region">Region value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateRegionTestData))]
|
||||
public void RegionShortNameTest(Region? region, bool expectNull)
|
||||
{
|
||||
var actual = region.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure that every Language that has an ISO 639-1 code is unique
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void RegionNoDuplicateShortNameTest()
|
||||
{
|
||||
var fullRegions = Enum.GetValues(typeof(Region)).Cast<Region?>().ToList();
|
||||
var filteredRegions = new Dictionary<string, Region?>();
|
||||
|
||||
int totalCount = 0;
|
||||
foreach (Region? region in fullRegions)
|
||||
{
|
||||
var code = region.ShortName();
|
||||
if (string.IsNullOrEmpty(code))
|
||||
continue;
|
||||
|
||||
// Throw if the code already exists
|
||||
if (filteredRegions.ContainsKey(code))
|
||||
throw new DuplicateNameException($"Code {code} already in dictionary");
|
||||
|
||||
filteredRegions[code] = region;
|
||||
totalCount++;
|
||||
}
|
||||
|
||||
Assert.Equal(totalCount, filteredRegions.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of Region values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of Region values</returns>
|
||||
public static List<object?[]> GenerateRegionTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (Region? region in Enum.GetValues(typeof(Region)))
|
||||
{
|
||||
testData.Add(new object?[] { region, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Site Code
|
||||
|
||||
/// <summary>
|
||||
/// Check that every SiteCode has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="siteCode">SiteCode value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateSiteCodeTestData))]
|
||||
public void SiteCodeLongNameTest(SiteCode? siteCode, bool expectNull)
|
||||
{
|
||||
var actual = siteCode.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check that every SiteCode has a short name provided
|
||||
/// </summary>
|
||||
/// <param name="siteCode">SiteCode value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateSiteCodeTestData))]
|
||||
public void SiteCodeShortNameTest(SiteCode? siteCode, bool expectNull)
|
||||
{
|
||||
var actual = siteCode.ShortName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of SiteCode values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of SiteCode values</returns>
|
||||
public static List<object?[]> GenerateSiteCodeTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (SiteCode? siteCode in Enum.GetValues(typeof(SiteCode)))
|
||||
{
|
||||
testData.Add(new object?[] { siteCode, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System
|
||||
|
||||
/// <summary>
|
||||
/// Check that every RedumpSystem has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="redumpSystem">RedumpSystem value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateRedumpSystemTestData))]
|
||||
public void RedumpSystemLongNameTest(RedumpSystem? redumpSystem, bool expectNull)
|
||||
{
|
||||
var actual = redumpSystem.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
// TODO: Re-enable the following test once non-Redump systems are accounted for
|
||||
|
||||
/// <summary>
|
||||
/// Check that every RedumpSystem has a short name provided
|
||||
/// </summary>
|
||||
/// <param name="redumpSystem">RedumpSystem value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
//[Theory]
|
||||
//[MemberData(nameof(GenerateRedumpSystemTestData))]
|
||||
//public void RedumpSystemShortNameTest(RedumpSystem? redumpSystem, bool expectNull)
|
||||
//{
|
||||
// string actual = redumpSystem.ShortName();
|
||||
|
||||
// if (expectNull)
|
||||
// Assert.Null(actual);
|
||||
// else
|
||||
// Assert.NotNull(actual);
|
||||
//}
|
||||
|
||||
// TODO: Test the other attributes as well
|
||||
// Most are bool checks so they're not as interesting to have unit tests around
|
||||
// SystemCategory always returns something as well, so is it worth testing?
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of RedumpSystem values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of RedumpSystem values</returns>
|
||||
public static List<object?[]> GenerateRedumpSystemTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (RedumpSystem? redumpSystem in Enum.GetValues(typeof(RedumpSystem)))
|
||||
{
|
||||
// We want to skip all markers for this
|
||||
if (redumpSystem.IsMarker())
|
||||
continue;
|
||||
|
||||
testData.Add(new object?[] { redumpSystem, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region System Category
|
||||
|
||||
/// <summary>
|
||||
/// Check that every SystemCategory has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="systemCategory">SystemCategory value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateSystemCategoryTestData))]
|
||||
public void SystemCategoryLongNameTest(SystemCategory? systemCategory, bool expectNull)
|
||||
{
|
||||
var actual = systemCategory.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of SystemCategory values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of SystemCategory values</returns>
|
||||
public static List<object?[]> GenerateSystemCategoryTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, true } };
|
||||
foreach (SystemCategory? systemCategory in Enum.GetValues(typeof(SystemCategory)))
|
||||
{
|
||||
if (systemCategory == SystemCategory.NONE)
|
||||
testData.Add(new object?[] { systemCategory, true });
|
||||
else
|
||||
testData.Add(new object?[] { systemCategory, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Yes/No
|
||||
|
||||
/// <summary>
|
||||
/// Check that every YesNo has a long name provided
|
||||
/// </summary>
|
||||
/// <param name="yesNo">YesNo value to check</param>
|
||||
/// <param name="expectNull">True to expect a null value, false otherwise</param>
|
||||
[Theory]
|
||||
[MemberData(nameof(GenerateYesNoTestData))]
|
||||
public void YesNoLongNameTest(YesNo? yesNo, bool expectNull)
|
||||
{
|
||||
string actual = yesNo.LongName();
|
||||
|
||||
if (expectNull)
|
||||
Assert.Null(actual);
|
||||
else
|
||||
Assert.NotNull(actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a test set of YesNo values
|
||||
/// </summary>
|
||||
/// <returns>MemberData-compatible list of YesNo values</returns>
|
||||
public static List<object?[]> GenerateYesNoTestData()
|
||||
{
|
||||
var testData = new List<object?[]>() { new object?[] { null, false } };
|
||||
foreach (YesNo? yesNo in Enum.GetValues(typeof(YesNo)))
|
||||
{
|
||||
testData.Add(new object?[] { yesNo, false });
|
||||
}
|
||||
|
||||
return testData;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
using Xunit;
|
||||
|
||||
namespace MPF.Test.RedumpLib
|
||||
{
|
||||
public class SubmissionInfoTests
|
||||
{
|
||||
[Fact]
|
||||
public void EmptySerializationTest()
|
||||
{
|
||||
var submissionInfo = new SubmissionInfo();
|
||||
string json = JsonConvert.SerializeObject(submissionInfo, Formatting.Indented);
|
||||
Assert.NotNull(json);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PartialSerializationTest()
|
||||
{
|
||||
var submissionInfo = new SubmissionInfo()
|
||||
{
|
||||
CommonDiscInfo = new CommonDiscInfoSection(),
|
||||
VersionAndEditions = new VersionAndEditionsSection(),
|
||||
EDC = new EDCSection(),
|
||||
ParentCloneRelationship = new ParentCloneRelationshipSection(),
|
||||
Extras = new ExtrasSection(),
|
||||
CopyProtection = new CopyProtectionSection(),
|
||||
DumpersAndStatus = new DumpersAndStatusSection(),
|
||||
TracksAndWriteOffsets = new TracksAndWriteOffsetsSection(),
|
||||
SizeAndChecksums = new SizeAndChecksumsSection(),
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(submissionInfo, Formatting.Indented);
|
||||
Assert.NotNull(json);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FullSerializationTest()
|
||||
{
|
||||
var submissionInfo = new SubmissionInfo()
|
||||
{
|
||||
SchemaVersion = 1,
|
||||
FullyMatchedID = 3,
|
||||
PartiallyMatchedIDs = new List<int> { 0, 1, 2, 3 },
|
||||
Added = DateTime.UtcNow,
|
||||
LastModified = DateTime.UtcNow,
|
||||
|
||||
CommonDiscInfo = new CommonDiscInfoSection()
|
||||
{
|
||||
System = RedumpSystem.IBMPCcompatible,
|
||||
Media = DiscType.CD,
|
||||
Title = "Game Title",
|
||||
ForeignTitleNonLatin = "Foreign Game Title",
|
||||
DiscNumberLetter = "1",
|
||||
DiscTitle = "Install Disc",
|
||||
Category = DiscCategory.Games,
|
||||
Region = Region.World,
|
||||
Languages = new Language?[] { Language.English, Language.Spanish, Language.French },
|
||||
LanguageSelection = new LanguageSelection?[] { LanguageSelection.BiosSettings },
|
||||
Serial = "Disc Serial",
|
||||
Layer0MasteringRing = "L0 Mastering Ring",
|
||||
Layer0MasteringSID = "L0 Mastering SID",
|
||||
Layer0ToolstampMasteringCode = "L0 Toolstamp",
|
||||
Layer0MouldSID = "L0 Mould SID",
|
||||
Layer0AdditionalMould = "L0 Additional Mould",
|
||||
Layer1MasteringRing = "L1 Mastering Ring",
|
||||
Layer1MasteringSID = "L1 Mastering SID",
|
||||
Layer1ToolstampMasteringCode = "L1 Toolstamp",
|
||||
Layer1MouldSID = "L1 Mould SID",
|
||||
Layer1AdditionalMould = "L1 Additional Mould",
|
||||
Layer2MasteringRing = "L2 Mastering Ring",
|
||||
Layer2MasteringSID = "L2 Mastering SID",
|
||||
Layer2ToolstampMasteringCode = "L2 Toolstamp",
|
||||
Layer3MasteringRing = "L3 Mastering Ring",
|
||||
Layer3MasteringSID = "L3 Mastering SID",
|
||||
Layer3ToolstampMasteringCode = "L3 Toolstamp",
|
||||
RingWriteOffset = "+12",
|
||||
Barcode = "UPC Barcode",
|
||||
EXEDateBuildDate = "19xx-xx-xx",
|
||||
ErrorsCount = "0",
|
||||
Comments = "Comment data line 1\r\nComment data line 2",
|
||||
CommentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
{
|
||||
[SiteCode.ISBN] = "ISBN",
|
||||
},
|
||||
Contents = "Special contents 1\r\nSpecial contents 2",
|
||||
ContentsSpecialFields = new Dictionary<SiteCode, string>()
|
||||
{
|
||||
[SiteCode.PlayableDemos] = "Game Demo 1",
|
||||
},
|
||||
},
|
||||
|
||||
VersionAndEditions = new VersionAndEditionsSection()
|
||||
{
|
||||
Version = "Original",
|
||||
VersionDatfile = "Alt",
|
||||
CommonEditions = new string[] { "Taikenban" },
|
||||
OtherEditions = "Rerelease",
|
||||
},
|
||||
|
||||
EDC = new EDCSection()
|
||||
{
|
||||
EDC = YesNo.Yes,
|
||||
},
|
||||
|
||||
ParentCloneRelationship = new ParentCloneRelationshipSection()
|
||||
{
|
||||
ParentID = "12345",
|
||||
RegionalParent = false,
|
||||
},
|
||||
|
||||
Extras = new ExtrasSection()
|
||||
{
|
||||
PVD = "PVD",
|
||||
DiscKey = "Disc key",
|
||||
DiscID = "Disc ID",
|
||||
PIC = "PIC",
|
||||
Header = "Header",
|
||||
BCA = "BCA",
|
||||
SecuritySectorRanges = "SSv1 Ranges",
|
||||
},
|
||||
|
||||
CopyProtection = new CopyProtectionSection()
|
||||
{
|
||||
AntiModchip = YesNo.Yes,
|
||||
LibCrypt = YesNo.No,
|
||||
LibCryptData = "LibCrypt data",
|
||||
Protection = "List of protections",
|
||||
SecuROMData = "SecuROM data",
|
||||
},
|
||||
|
||||
DumpersAndStatus = new DumpersAndStatusSection()
|
||||
{
|
||||
Status = DumpStatus.TwoOrMoreGreen,
|
||||
Dumpers = new string[] { "Dumper1", "Dumper2" },
|
||||
OtherDumpers = "Dumper3",
|
||||
},
|
||||
|
||||
TracksAndWriteOffsets = new TracksAndWriteOffsetsSection()
|
||||
{
|
||||
ClrMameProData = "Datfile",
|
||||
Cuesheet = "Cuesheet",
|
||||
CommonWriteOffsets = new int[] { 0, 12, -12 },
|
||||
OtherWriteOffsets = "-2",
|
||||
},
|
||||
|
||||
SizeAndChecksums = new SizeAndChecksumsSection()
|
||||
{
|
||||
Layerbreak = 0,
|
||||
Layerbreak2 = 1,
|
||||
Layerbreak3 = 2,
|
||||
Size = 12345,
|
||||
CRC32 = "CRC32",
|
||||
MD5 = "MD5",
|
||||
SHA1 = "SHA1",
|
||||
},
|
||||
|
||||
DumpingInfo = new DumpingInfoSection()
|
||||
{
|
||||
DumpingProgram = "DiscImageCreator 20500101",
|
||||
DumpingDate = DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||
Manufacturer = "ATAPI",
|
||||
Model = "Optical Drive",
|
||||
Firmware = "1.23",
|
||||
ReportedDiscType = "CD-R",
|
||||
},
|
||||
|
||||
Artifacts = new Dictionary<string, string>()
|
||||
{
|
||||
["Sample Artifact"] = "Sample Data",
|
||||
},
|
||||
};
|
||||
|
||||
string json = JsonConvert.SerializeObject(submissionInfo, Formatting.Indented);
|
||||
Assert.NotNull(json);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using System.Windows.Controls;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using System.Windows;
|
||||
|
||||
namespace MPF.UI
|
||||
{
|
||||
|
||||
@@ -1,130 +1,130 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- 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>
|
||||
<VersionPrefix>3.2.0</VersionPrefix>
|
||||
<PropertyGroup>
|
||||
<!-- 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>
|
||||
<VersionPrefix>3.2.2</VersionPrefix>
|
||||
|
||||
<!-- Package Properties -->
|
||||
<AssemblyName>MPF</AssemblyName>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<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>
|
||||
<!-- Package Properties -->
|
||||
<AssemblyName>MPF</AssemblyName>
|
||||
<Authors>Matt Nadareski;ReignStumble;Jakz</Authors>
|
||||
<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>
|
||||
<!-- 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" />
|
||||
<Resource Include="Images\ring-code-guide-1-layer.png" />
|
||||
<Resource Include="Images\ring-code-guide-2-layer.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.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.1.13" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.3.8" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="UserControls\UserInput.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="Windows\DiscInformationWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="Windows\RingCodeGuideWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- 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')" />
|
||||
<InternalsVisibleTo Include="MPF.Test" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Images\Icon.ico" />
|
||||
<Resource Include="Images\ring-code-guide-1-layer.png" />
|
||||
<Resource Include="Images\ring-code-guide-2-layer.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MPF.Frontend\MPF.Frontend.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.1.13" GeneratePathProperty="true">
|
||||
<IncludeAssets>runtime; compile; build; native; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SabreTools.RedumpLib" Version="1.4.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="$(PkgBinaryObjectScanner)\content\**" PackagePath="contentFiles\any\any;content" CopyToOutputDirectory="Always" PackageCopyToOutput="true" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Page Update="UserControls\UserInput.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="Windows\DiscInformationWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
<Page Update="Windows\RingCodeGuideWindow.xaml">
|
||||
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- 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>
|
||||
143
MPF.UI/Theme.cs
143
MPF.UI/Theme.cs
@@ -254,147 +254,4 @@ namespace MPF.UI
|
||||
Application.Current.Resources["TextBox.Static.Background"] = this.TextBox_Static_Background;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default light-mode theme
|
||||
/// </summary>
|
||||
public class LightModeTheme : Theme
|
||||
{
|
||||
public LightModeTheme()
|
||||
{
|
||||
// Handle application-wide resources
|
||||
this.ActiveBorderBrush = null;
|
||||
this.ControlBrush = null;
|
||||
this.ControlTextBrush = null;
|
||||
this.GrayTextBrush = null;
|
||||
this.WindowBrush = null;
|
||||
this.WindowTextBrush = null;
|
||||
|
||||
// Handle Button-specific resources
|
||||
this.Button_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF4));
|
||||
this.Button_MouseOver_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xBE, 0xE6, 0xFD));
|
||||
this.Button_Pressed_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xC4, 0xE5, 0xF6));
|
||||
this.Button_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xDD, 0xDD, 0xDD));
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
this.ComboBox_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
|
||||
this.ComboBox_Disabled_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Disabled_Editable_Button_Background = Brushes.Transparent;
|
||||
this.ComboBox_MouseOver_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xEC, 0xF4, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_MouseOver_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_MouseOver_Editable_Button_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xEB, 0xF4, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Pressed_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xDA, 0xEC, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Pressed_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Pressed_Editable_Button_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xDA, 0xEB, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Static_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
|
||||
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Static_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Static_Editable_Button_Background = Brushes.Transparent;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
this.CustomMessageBox_Static_Background = null;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
this.MenuItem_SubMenu_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
|
||||
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
this.ProgressBar_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xE6, 0xE6, 0xE6));
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
this.ScrollViewer_ScrollBar_Background = Brushes.LightGray;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
this.TabItem_Selected_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.TabItem_Static_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
|
||||
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.TabItem_Static_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
this.TextBox_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default dark-mode theme
|
||||
/// </summary>
|
||||
public class DarkModeTheme : Theme
|
||||
{
|
||||
public DarkModeTheme()
|
||||
{
|
||||
// Setup needed brushes
|
||||
var darkModeBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, 0x20, 0x20, 0x20) };
|
||||
|
||||
// Handle application-wide resources
|
||||
this.ActiveBorderBrush = Brushes.Black;
|
||||
this.ControlBrush = darkModeBrush;
|
||||
this.ControlTextBrush = Brushes.White;
|
||||
this.GrayTextBrush = Brushes.DarkGray;
|
||||
this.WindowBrush = darkModeBrush;
|
||||
this.WindowTextBrush = Brushes.White;
|
||||
|
||||
// Handle Button-specific resources
|
||||
this.Button_Disabled_Background = darkModeBrush;
|
||||
this.Button_MouseOver_Background = darkModeBrush;
|
||||
this.Button_Pressed_Background = darkModeBrush;
|
||||
this.Button_Static_Background = darkModeBrush;
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
this.ComboBox_Disabled_Background = darkModeBrush;
|
||||
this.ComboBox_Disabled_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Disabled_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Editable_Button_Background = darkModeBrush;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
this.CustomMessageBox_Static_Background = darkModeBrush;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
this.MenuItem_SubMenu_Background = darkModeBrush;
|
||||
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
this.ProgressBar_Background = darkModeBrush;
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
this.ScrollViewer_ScrollBar_Background = darkModeBrush;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
this.TabItem_Selected_Background = darkModeBrush;
|
||||
this.TabItem_Static_Background = darkModeBrush;
|
||||
this.TabItem_Static_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
this.TextBox_Static_Background = darkModeBrush;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
88
MPF.UI/Themes/CustomTheme.cs
Normal file
88
MPF.UI/Themes/CustomTheme.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using System;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MPF.UI.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom colour theme
|
||||
/// </summary>
|
||||
public sealed class CustomTheme : Theme
|
||||
{
|
||||
public CustomTheme(string? backgroundColor, string? foregroundColor)
|
||||
{
|
||||
// Convert hex colors to byte values
|
||||
byte backgroundR;
|
||||
byte backgroundG;
|
||||
byte backgroundB;
|
||||
byte foregroundR;
|
||||
byte foregroundG;
|
||||
byte foregroundB;
|
||||
try
|
||||
{
|
||||
backgroundR = Convert.ToByte(backgroundColor!.Substring(0, 2), 16);
|
||||
backgroundG = Convert.ToByte(backgroundColor!.Substring(2, 2), 16);
|
||||
backgroundB = Convert.ToByte(backgroundColor!.Substring(4, 2), 16);
|
||||
foregroundR = Convert.ToByte(foregroundColor!.Substring(0, 2), 16);
|
||||
foregroundG = Convert.ToByte(foregroundColor!.Substring(2, 2), 16);
|
||||
foregroundB = Convert.ToByte(foregroundColor!.Substring(4, 2), 16);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup needed brushes
|
||||
var backgroundBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, backgroundR, backgroundG, backgroundB) };
|
||||
var foregroundBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, foregroundR, foregroundG, foregroundB) };
|
||||
|
||||
// Handle application-wide resources
|
||||
this.ActiveBorderBrush = foregroundBrush;
|
||||
this.ControlBrush = backgroundBrush;
|
||||
this.ControlTextBrush = foregroundBrush;
|
||||
this.GrayTextBrush = foregroundBrush;
|
||||
this.WindowBrush = backgroundBrush;
|
||||
this.WindowTextBrush = foregroundBrush;
|
||||
|
||||
// Handle Button-specific resources
|
||||
this.Button_Disabled_Background = backgroundBrush;
|
||||
this.Button_MouseOver_Background = backgroundBrush;
|
||||
this.Button_Pressed_Background = backgroundBrush;
|
||||
this.Button_Static_Background = backgroundBrush;
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
this.ComboBox_Disabled_Background = backgroundBrush;
|
||||
this.ComboBox_Disabled_Editable_Background = backgroundBrush;
|
||||
this.ComboBox_Disabled_Editable_Button_Background = backgroundBrush;
|
||||
this.ComboBox_MouseOver_Background = backgroundBrush;
|
||||
this.ComboBox_MouseOver_Editable_Background = backgroundBrush;
|
||||
this.ComboBox_MouseOver_Editable_Button_Background = backgroundBrush;
|
||||
this.ComboBox_Pressed_Background = backgroundBrush;
|
||||
this.ComboBox_Pressed_Editable_Background = backgroundBrush;
|
||||
this.ComboBox_Pressed_Editable_Button_Background = backgroundBrush;
|
||||
this.ComboBox_Static_Background = backgroundBrush;
|
||||
this.ComboBox_Static_Editable_Background = backgroundBrush;
|
||||
this.ComboBox_Static_Editable_Button_Background = backgroundBrush;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
this.CustomMessageBox_Static_Background = backgroundBrush;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
this.MenuItem_SubMenu_Background = backgroundBrush;
|
||||
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
this.ProgressBar_Background = backgroundBrush;
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
this.ScrollViewer_ScrollBar_Background = backgroundBrush;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
this.TabItem_Selected_Background = backgroundBrush;
|
||||
this.TabItem_Static_Background = backgroundBrush;
|
||||
this.TabItem_Static_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
this.TextBox_Static_Background = backgroundBrush;
|
||||
}
|
||||
}
|
||||
}
|
||||
65
MPF.UI/Themes/DarkModeTheme.cs
Normal file
65
MPF.UI/Themes/DarkModeTheme.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MPF.UI.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// Default dark-mode theme
|
||||
/// </summary>
|
||||
public sealed class DarkModeTheme : Theme
|
||||
{
|
||||
public DarkModeTheme()
|
||||
{
|
||||
// Setup needed brushes
|
||||
var darkModeBrush = new SolidColorBrush { Color = Color.FromArgb(0xff, 0x20, 0x20, 0x20) };
|
||||
|
||||
// Handle application-wide resources
|
||||
this.ActiveBorderBrush = Brushes.Black;
|
||||
this.ControlBrush = darkModeBrush;
|
||||
this.ControlTextBrush = Brushes.White;
|
||||
this.GrayTextBrush = Brushes.DarkGray;
|
||||
this.WindowBrush = darkModeBrush;
|
||||
this.WindowTextBrush = Brushes.White;
|
||||
|
||||
// Handle Button-specific resources
|
||||
this.Button_Disabled_Background = darkModeBrush;
|
||||
this.Button_MouseOver_Background = darkModeBrush;
|
||||
this.Button_Pressed_Background = darkModeBrush;
|
||||
this.Button_Static_Background = darkModeBrush;
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
this.ComboBox_Disabled_Background = darkModeBrush;
|
||||
this.ComboBox_Disabled_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Disabled_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_MouseOver_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Pressed_Editable_Button_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Editable_Background = darkModeBrush;
|
||||
this.ComboBox_Static_Editable_Button_Background = darkModeBrush;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
this.CustomMessageBox_Static_Background = darkModeBrush;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
this.MenuItem_SubMenu_Background = darkModeBrush;
|
||||
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
this.ProgressBar_Background = darkModeBrush;
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
this.ScrollViewer_ScrollBar_Background = darkModeBrush;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
this.TabItem_Selected_Background = darkModeBrush;
|
||||
this.TabItem_Static_Background = darkModeBrush;
|
||||
this.TabItem_Static_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
this.TextBox_Static_Background = darkModeBrush;
|
||||
}
|
||||
}
|
||||
}
|
||||
87
MPF.UI/Themes/LightModeTheme.cs
Normal file
87
MPF.UI/Themes/LightModeTheme.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace MPF.UI.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// Default light-mode theme
|
||||
/// </summary>
|
||||
public sealed class LightModeTheme : Theme
|
||||
{
|
||||
public LightModeTheme()
|
||||
{
|
||||
// Handle application-wide resources
|
||||
this.ActiveBorderBrush = null;
|
||||
this.ControlBrush = null;
|
||||
this.ControlTextBrush = null;
|
||||
this.GrayTextBrush = null;
|
||||
this.WindowBrush = null;
|
||||
this.WindowTextBrush = null;
|
||||
|
||||
// Handle Button-specific resources
|
||||
this.Button_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF4, 0xF4, 0xF4));
|
||||
this.Button_MouseOver_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xBE, 0xE6, 0xFD));
|
||||
this.Button_Pressed_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xC4, 0xE5, 0xF6));
|
||||
this.Button_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xDD, 0xDD, 0xDD));
|
||||
|
||||
// Handle ComboBox-specific resources
|
||||
this.ComboBox_Disabled_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
|
||||
this.ComboBox_Disabled_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Disabled_Editable_Button_Background = Brushes.Transparent;
|
||||
this.ComboBox_MouseOver_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xEC, 0xF4, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_MouseOver_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_MouseOver_Editable_Button_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xEB, 0xF4, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xDC, 0xEC, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Pressed_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xDA, 0xEC, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Pressed_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Pressed_Editable_Button_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xDA, 0xEB, 0xFC),
|
||||
Color.FromArgb(0xFF, 0xC4, 0xE0, 0xFC),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Static_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
|
||||
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.ComboBox_Static_Editable_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.ComboBox_Static_Editable_Button_Background = Brushes.Transparent;
|
||||
|
||||
// Handle CustomMessageBox-specific resources
|
||||
this.CustomMessageBox_Static_Background = null;
|
||||
|
||||
// Handle MenuItem-specific resources
|
||||
this.MenuItem_SubMenu_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0));
|
||||
this.MenuItem_SubMenu_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle ProgressBar-specific resources
|
||||
this.ProgressBar_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xE6, 0xE6, 0xE6));
|
||||
|
||||
// Handle ScrollViewer-specific resources
|
||||
this.ScrollViewer_ScrollBar_Background = Brushes.LightGray;
|
||||
|
||||
// Handle TabItem-specific resources
|
||||
this.TabItem_Selected_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
this.TabItem_Static_Background = new LinearGradientBrush(
|
||||
Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0),
|
||||
Color.FromArgb(0xFF, 0xE5, 0xE5, 0xE5),
|
||||
new Point(0, 0),
|
||||
new Point(0, 1));
|
||||
this.TabItem_Static_Border = Brushes.DarkGray;
|
||||
|
||||
// Handle TextBox-specific resources
|
||||
this.TextBox_Static_Background = new SolidColorBrush(Color.FromArgb(0xFF, 0xFF, 0xFF, 0xFF));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,7 +237,7 @@
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)ActivisionID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="BandaiIDTextBox" Label="Bandai ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BandaiID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="BethesdaIDTextBox" Label="Bandai ID"
|
||||
<controls:UserInput x:Name="BethesdaIDTextBox" Label="Bethesda ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)BethesdaID], Mode=TwoWay}"/>
|
||||
<controls:UserInput x:Name="CDProjektIDTextBox" Label="CD Projekt ID"
|
||||
Text="{Binding Path=SubmissionInfo.CommonDiscInfo.CommentsSpecialFields[(redump:SiteCode)CDProjektID], Mode=TwoWay}"/>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using MPF.Frontend.ViewModels;
|
||||
using MPF.UI.Themes;
|
||||
using MPF.UI.UserControls;
|
||||
using SabreTools.RedumpLib;
|
||||
using SabreTools.RedumpLib.Data;
|
||||
@@ -397,13 +398,66 @@ namespace MPF.UI.Windows
|
||||
Theme theme;
|
||||
if (MainViewModel.Options.EnableDarkMode)
|
||||
theme = new DarkModeTheme();
|
||||
else if (MainViewModel.Options.EnablePurpMode)
|
||||
theme = new CustomTheme("111111", "9A5EC0");
|
||||
else if (IsHexColor(MainViewModel.Options.CustomBackgroundColor) && IsHexColor(MainViewModel.Options.CustomTextColor))
|
||||
theme = new CustomTheme(MainViewModel.Options.CustomBackgroundColor, MainViewModel.Options.CustomTextColor);
|
||||
else
|
||||
theme = new LightModeTheme();
|
||||
|
||||
theme.Apply();
|
||||
}
|
||||
|
||||
#endregion
|
||||
/// <summary>
|
||||
/// Check whether a string represents a valid hex color
|
||||
/// </summary>
|
||||
public static bool IsHexColor(string? color)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(color))
|
||||
return false;
|
||||
|
||||
if (color!.Length == 7 && color[0] == '#')
|
||||
color = color.Substring(1);
|
||||
|
||||
if (color.Length != 6)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < color.Length; i++)
|
||||
{
|
||||
switch (color[i])
|
||||
{
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case 'E':
|
||||
case 'F':
|
||||
case 'a':
|
||||
case 'b':
|
||||
case 'c':
|
||||
case 'd':
|
||||
case 'e':
|
||||
case 'f':
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
|
||||
6
MPF.sln
6
MPF.sln
@@ -38,6 +38,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.Processors", "MPF.Proce
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MPF.ExecutionContexts", "MPF.ExecutionContexts\MPF.ExecutionContexts.csproj", "{75609610-CE85-486E-8F42-9525A52B80BF}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MPF.CLI", "MPF.CLI\MPF.CLI.csproj", "{A825771B-98AC-44A5-8076-C9AB3D469751}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -68,6 +70,10 @@ Global
|
||||
{75609610-CE85-486E-8F42-9525A52B80BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{75609610-CE85-486E-8F42-9525A52B80BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{75609610-CE85-486E-8F42-9525A52B80BF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A825771B-98AC-44A5-8076-C9AB3D469751}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A825771B-98AC-44A5-8076-C9AB3D469751}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A825771B-98AC-44A5-8076-C9AB3D469751}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A825771B-98AC-44A5-8076-C9AB3D469751}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
37
README.md
37
README.md
@@ -4,6 +4,7 @@ Redumper/Aaru/DiscImageCreator UI in C#
|
||||
|
||||
[](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_cli.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 :)
|
||||
@@ -14,11 +15,11 @@ For the most recent stable build, download the latest release here: [Releases Pa
|
||||
|
||||
For the latest WIP build here: [Rolling Release](https://github.com/SabreTools/MPF/releases/tag/rolling)
|
||||
|
||||
## Media Preservation Frontend UI (MPF)
|
||||
## Media Preservation Frontend UI (MPF.UI)
|
||||
|
||||
MPF is the main, UI-centric application of the MPF suite. This program allows users to use Redumper, Aaru, or DiscImageCreator in a more user-friendly way. Each backend dumping program is supported as fully as possible to ensure that all information is captured on output. There are many customization options and quality of life settings that can be access through the Options menu.
|
||||
|
||||
### Support Limitations
|
||||
### UI Support Limitations
|
||||
|
||||
The main UI has some known limitations that are documented in code and in some prior support tickets:
|
||||
|
||||
@@ -32,13 +33,29 @@ The main UI has some known limitations that are documented in code and in some p
|
||||
- Consider using a third-party scanning tool, such as Protection ID, if this is not sufficient for your needs
|
||||
- See [Compatibility Notes](https://github.com/SabreTools/BinaryObjectScanner?tab=readme-ov-file#compatibility-notes) for more details
|
||||
|
||||
## Media Preservation Frontend CLI (MPF.CLI)
|
||||
|
||||
MPF.CLI is a commandline-only program that allows users to use Redumper, Aaru, or DiscImageCreator in a more user-friendly way. Each backend dumping program is supported as fully as possible to ensure that all information is captured on output. There are many customization options and quality of life settings that can be access through the `config.json` file.
|
||||
|
||||
### CLI Support Limitations
|
||||
|
||||
The main CLI has some known limitations that are documented in code and in some prior support tickets:
|
||||
|
||||
- No programs are bundled by default
|
||||
- This is the result of the extremely varied builds that are allowed
|
||||
- For those who require broader archive/installer compatibility for protection scanning (Windows-only), please use the x86 builds as there are some specific scanning libraries that only work with that build
|
||||
- This is actively being worked on as part of [Binary Object Scanner](https://github.com/SabreTools/BinaryObjectScanner)
|
||||
- Please consider contributing if you have experience in dealing with multiple archive and installer types
|
||||
- Consider using a third-party scanning tool, such as Protection ID, if this is not sufficient for your needs
|
||||
- See [Compatibility Notes](https://github.com/SabreTools/BinaryObjectScanner?tab=readme-ov-file#compatibility-notes) for more details
|
||||
|
||||
## Media Preservation Frontend Checker (MPF.Check)
|
||||
|
||||
MPF.Check is a commandline-only program that allows users to generate submission information from their personal rips. This program supports the outputs from Redumper, Aaru, DiscImageCreator, Cleanrip, and UmdImageCreator. Running this program without any parameters will display the help text, including all supported parameters.
|
||||
|
||||
## System Requirements
|
||||
|
||||
Both MPF UI and MPF.Check have the same system requirements for running, with the exception that MPF UI is Windows-only.
|
||||
MPF.UI, MPF.CLI, and MPF.Check have the same system requirements for running, with the exception that MPF.UI is Windows-only.
|
||||
|
||||
- [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
|
||||
@@ -49,23 +66,29 @@ Ensure that your operating system and runtimes are as up-to-date as possible, si
|
||||
|
||||
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):**
|
||||
**MPF.UI (Windows only):**
|
||||
|
||||
```bash
|
||||
dotnet build MPF/MPF.csproj --framework net8.0-windows --runtime [win-x86|win-x64]
|
||||
```
|
||||
|
||||
**MPF.CLI (Windows, OSX, Linux):**
|
||||
|
||||
```bash
|
||||
dotnet build MPF.CLI/MPF.CLI.csproj --framework net8.0 --runtime [win-x86|win-x64|win-arm64|linux-x64|linux-arm64|osx-x64|osx-arm64]
|
||||
```
|
||||
|
||||
**MPF.Check (Windows, OSX, Linux):**
|
||||
|
||||
```bash
|
||||
dotnet build MPF.Check/MPF.Check.csproj --framework net8.0 --runtime [win-x86|win-x64|linux-x64|osx-x64]
|
||||
dotnet build MPF.Check/MPF.Check.csproj --framework net8.0 --runtime [win-x86|win-x64|win-arm64|linux-x64|linux-arm64|osx-x64|osx-arm64]
|
||||
```
|
||||
|
||||
Choose one of `win-x86`, `win-x64`, `linux-x64`, or `osx-x64` depending on the machine you are targeting.
|
||||
Choose one of `win-x86`, `win-x64`, `win-arm64`, `linux-x64`, `linux-arm64`, `osx-x64`, or `osx-arm64` depending on the machine you are targeting.
|
||||
|
||||
### Build Scripts
|
||||
|
||||
Windows users may run `publish-win.ps1` and Linux users may run `publish-nix.sh` to perform a full release build. Both scripts will build and package all variants of MPF UI and MPF.Check with commandline switches to control what is included.
|
||||
Windows users may run `publish-win.ps1` and Linux users may run `publish-nix.sh` to perform a full release build. Both scripts will build and package all variants of MPF.UI, MPF.CLI, and MPF.Check with commandline switches to control what is included.
|
||||
|
||||
- `publish-win.ps1` 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` requires `zip` and `git` to be installed and in `PATH`
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# version format
|
||||
version: 3.2.0-{build}
|
||||
version: 3.2.2-{build}
|
||||
|
||||
# pull request template
|
||||
pull_requests:
|
||||
|
||||
151
publish-nix.sh
151
publish-nix.sh
@@ -42,24 +42,31 @@ BUILD_FOLDER=$PWD
|
||||
# Set the current commit hash
|
||||
COMMIT=$(git log --pretty=%H -1)
|
||||
|
||||
# Output the selected options
|
||||
echo "Selected Options:"
|
||||
echo " Use all frameworks (-u) $USE_ALL"
|
||||
echo " Include programs (-p) $INCLUDE_PROGRAMS"
|
||||
echo " No build (-b) $NO_BUILD"
|
||||
echo " No archive (-a) $NO_ARCHIVE"
|
||||
echo " "
|
||||
|
||||
# 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")
|
||||
CHECK_RUNTIMES=("win-x86" "win-x64" "win-arm64" "linux-x64" "linux-arm64" "osx-x64" "osx-arm64")
|
||||
|
||||
# Use expanded lists, if requested
|
||||
# Use expanded framework 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
|
||||
|
||||
# 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_APPLE_FRAMEWORKS=("net6.0" "net7.0" "net8.0")
|
||||
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")
|
||||
VALID_CROSS_PLATFORM_RUNTIMES=("win-arm64" "linux-x64" "linux-arm64" "osx-x64" "osx-arm64")
|
||||
|
||||
# Only build if requested
|
||||
if [ $NO_BUILD = false ]; then
|
||||
@@ -67,12 +74,28 @@ if [ $NO_BUILD = false ]; then
|
||||
echo "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Create Nuget Packages
|
||||
dotnet pack MPF.ExecutionContexts/MPF.ExecutionContexts.csproj --output $BUILD_FOLDER
|
||||
dotnet pack MPF.Processors/MPF.Processors.csproj --output $BUILD_FOLDER
|
||||
|
||||
# Build UI
|
||||
for FRAMEWORK in "${UI_FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${UI_RUNTIMES[@]}"; do
|
||||
# Output the current build
|
||||
echo "===== Build UI - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# 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
|
||||
echo "Skipped due to invalid combination"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [ $RUNTIME = "osx-arm64" ]; then
|
||||
echo "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
@@ -94,12 +117,63 @@ if [ $NO_BUILD = false ]; then
|
||||
done
|
||||
done
|
||||
|
||||
# Build Check
|
||||
# Build CLI
|
||||
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
|
||||
# Output the current build
|
||||
echo "===== Build CLI - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# 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
|
||||
echo "Skipped due to invalid combination"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [ $RUNTIME = "osx-arm64" ]; then
|
||||
echo "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# 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.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
fi
|
||||
dotnet publish MPF.CLI/MPF.CLI.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.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
fi
|
||||
dotnet publish MPF.CLI/MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
# Build Check
|
||||
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
|
||||
# Output the current build
|
||||
echo "===== Build Check - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# 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
|
||||
echo "Skipped due to invalid combination"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [ $RUNTIME = "osx-arm64" ]; then
|
||||
echo "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
@@ -127,6 +201,25 @@ if [ $NO_ARCHIVE = false ]; then
|
||||
# Create UI archives
|
||||
for FRAMEWORK in "${UI_FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${UI_RUNTIMES[@]}"; do
|
||||
# Output the current build
|
||||
echo "===== Archive UI - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# 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
|
||||
echo "Skipped due to invalid combination"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [ $RUNTIME = "osx-arm64" ]; then
|
||||
echo "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
cd $BUILD_FOLDER/MPF.UI/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
@@ -145,12 +238,56 @@ if [ $NO_ARCHIVE = false ]; then
|
||||
done
|
||||
done
|
||||
|
||||
# Create Check archives
|
||||
# Create CLI archives
|
||||
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
|
||||
# Output the current build
|
||||
echo "===== Archive CLI - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# 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
|
||||
echo "Skipped due to invalid combination"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [ $RUNTIME = "osx-arm64" ]; then
|
||||
echo "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Only include Debug if building all
|
||||
if [ $USE_ALL = true ]; then
|
||||
cd $BUILD_FOLDER/MPF.CLI/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.CLI_${FRAMEWORK}_${RUNTIME}_debug.zip .
|
||||
fi
|
||||
cd $BUILD_FOLDER/MPF.CLI/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
|
||||
zip -r $BUILD_FOLDER/MPF.CLI_${FRAMEWORK}_${RUNTIME}_release.zip .
|
||||
done
|
||||
done
|
||||
|
||||
# Create Check archives
|
||||
for FRAMEWORK in "${CHECK_FRAMEWORKS[@]}"; do
|
||||
for RUNTIME in "${CHECK_RUNTIMES[@]}"; do
|
||||
# Output the current build
|
||||
echo "===== Archive Check - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# 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
|
||||
echo "Skipped due to invalid combination"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
|
||||
if [ $RUNTIME = "osx-arm64" ]; then
|
||||
echo "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
130
publish-win.ps1
130
publish-win.ps1
@@ -33,24 +33,31 @@ $BUILD_FOLDER = $PSScriptRoot
|
||||
# Set the current commit hash
|
||||
$COMMIT = git log --pretty=format:"%H" -1
|
||||
|
||||
# Output the selected options
|
||||
Write-Host "Selected Options:"
|
||||
Write-Host " Use all frameworks (-UseAll) $USE_ALL"
|
||||
Write-Host " Include programs (-IncludePrograms) $INCLUDE_PROGRAMS"
|
||||
Write-Host " No build (-NoBuild) $NO_BUILD"
|
||||
Write-Host " No archive (-NoArchive) $NO_ARCHIVE"
|
||||
Write-Host " "
|
||||
|
||||
# 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')
|
||||
$CHECK_RUNTIMES = @('win-x86', 'win-x64', 'win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64')
|
||||
|
||||
# Use expanded lists, if requested
|
||||
# Use expanded framework 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_APPLE_FRAMEWORKS = @('net6.0', 'net7.0', 'net8.0')
|
||||
$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')
|
||||
$VALID_CROSS_PLATFORM_RUNTIMES = @('win-arm64', 'linux-x64', 'linux-arm64', 'osx-x64', 'osx-arm64')
|
||||
|
||||
# Only build if requested
|
||||
if (!$NO_BUILD.IsPresent) {
|
||||
@@ -58,11 +65,25 @@ if (!$NO_BUILD.IsPresent) {
|
||||
Write-Host "Restoring Nuget packages"
|
||||
dotnet restore
|
||||
|
||||
# Create Nuget Packages
|
||||
dotnet pack MPF.ExecutionContexts/MPF.ExecutionContexts.csproj --output $BUILD_FOLDER
|
||||
dotnet pack MPF.Processors/MPF.Processors.csproj --output $BUILD_FOLDER
|
||||
|
||||
# Build UI
|
||||
foreach ($FRAMEWORK in $UI_FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $UI_RUNTIMES) {
|
||||
# Output the current build
|
||||
Write-Host "===== Build UI - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
Write-Host "Skipped due to invalid combination"
|
||||
continue
|
||||
}
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
|
||||
Write-Host "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -84,11 +105,57 @@ if (!$NO_BUILD.IsPresent) {
|
||||
}
|
||||
}
|
||||
|
||||
# Build CLI
|
||||
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $CHECK_RUNTIMES) {
|
||||
# Output the current build
|
||||
Write-Host "===== Build CLI - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
Write-Host "Skipped due to invalid combination"
|
||||
continue
|
||||
}
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
|
||||
Write-Host "Skipped due to no Apple Silicon support"
|
||||
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.CLI\MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true
|
||||
}
|
||||
dotnet publish MPF.CLI\MPF.CLI.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.CLI\MPF.CLI.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT
|
||||
}
|
||||
dotnet publish MPF.CLI\MPF.CLI.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) {
|
||||
# Output the current build
|
||||
Write-Host "===== Build Check - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
Write-Host "Skipped due to invalid combination"
|
||||
continue
|
||||
}
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
|
||||
Write-Host "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -116,6 +183,21 @@ if (!$NO_ARCHIVE.IsPresent) {
|
||||
# Create UI archives
|
||||
foreach ($FRAMEWORK in $UI_FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $UI_RUNTIMES) {
|
||||
# Output the current build
|
||||
Write-Host "===== Archive UI - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
Write-Host "Skipped due to invalid combination"
|
||||
continue
|
||||
}
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
|
||||
Write-Host "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
}
|
||||
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
Set-Location -Path $BUILD_FOLDER\MPF.UI\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
@@ -137,11 +219,49 @@ if (!$NO_ARCHIVE.IsPresent) {
|
||||
}
|
||||
}
|
||||
|
||||
# Create CLI archives
|
||||
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $CHECK_RUNTIMES) {
|
||||
# Output the current build
|
||||
Write-Host "===== Archive CLI - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
Write-Host "Skipped due to invalid combination"
|
||||
continue
|
||||
}
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
|
||||
Write-Host "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
}
|
||||
|
||||
# Only include Debug if building all
|
||||
if ($USE_ALL.IsPresent) {
|
||||
Set-Location -Path $BUILD_FOLDER\MPF.CLI\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\MPF.CLI_${FRAMEWORK}_${RUNTIME}_debug.zip *
|
||||
}
|
||||
Set-Location -Path $BUILD_FOLDER\MPF.CLI\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
|
||||
7z a -tzip $BUILD_FOLDER\MPF.CLI_${FRAMEWORK}_${RUNTIME}_release.zip *
|
||||
}
|
||||
}
|
||||
|
||||
# Create Check archives
|
||||
foreach ($FRAMEWORK in $CHECK_FRAMEWORKS) {
|
||||
foreach ($RUNTIME in $CHECK_RUNTIMES) {
|
||||
# Output the current build
|
||||
Write-Host "===== Archive Check - $FRAMEWORK, $RUNTIME ====="
|
||||
|
||||
# If we have an invalid combination of framework and runtime
|
||||
if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {
|
||||
Write-Host "Skipped due to invalid combination"
|
||||
continue
|
||||
}
|
||||
|
||||
# If we have Apple silicon but an unsupported framework
|
||||
if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') {
|
||||
Write-Host "Skipped due to no Apple Silicon support"
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user